@adobe/design-data-agent-mcp 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -186,7 +186,7 @@ file or class name and description of purpose be included on the
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
189
+ Copyright 2026 Adobe
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@adobe/design-data-agent-mcp`
2
2
 
3
- MCP server and Claude Code skill for the [Spectrum Design Data](../../packages/design-data-spec/) agent surface. Shells out to the `design-data` CLI — all logic stays in the Rust SDK.
3
+ MCP server and Claude Code skill for the [Spectrum Design Data](../../packages/design-data/) agent surface. Shells out to the `design-data` CLI — all logic stays in the Rust SDK.
4
4
 
5
5
  ## Install
6
6
 
@@ -47,15 +47,38 @@ node tools/design-data-agent-mcp/src/index.js
47
47
 
48
48
  ### Environment variables
49
49
 
50
- | Variable | Default | Description |
51
- | ------------------------ | ------------- | ----------------------------------------- |
52
- | `DESIGN_DATA_BIN` | `design-data` | Path to the `design-data` binary |
53
- | `DESIGN_DATA_PATH` | `.` | Dataset root path |
54
- | `DESIGN_DATA_COMPONENTS` | | Override components directory |
55
- | `DESIGN_DATA_FIELDS` | — | Override fields directory |
56
- | `DESIGN_DATA_DIMENSIONS` | — | Override dimensions directory |
57
- | `DESIGN_DATA_SCHEMAS` | — | Override schema path (for `validate`) |
58
- | `DESIGN_DATA_EXCEPTIONS` | — | Override exceptions path (for `validate`) |
50
+ | Variable | Default | Description |
51
+ | ------------------------ | ------------- | ------------------------------------------------- |
52
+ | `DESIGN_DATA_BIN` | `design-data` | Path to the `design-data` binary |
53
+ | `DESIGN_DATA_ROOT` | | Absolute root that relative paths are anchored to |
54
+ | `DESIGN_DATA_PATH` | `.` | Dataset root path |
55
+ | `DESIGN_DATA_COMPONENTS` | — | Override components directory |
56
+ | `DESIGN_DATA_FIELDS` | — | Override fields directory |
57
+ | `DESIGN_DATA_SCHEMAS` | — | Override schema path (for `validate`) |
58
+ | `DESIGN_DATA_EXCEPTIONS` | — | Override exceptions path (for `validate`) |
59
+
60
+ > **Path resolution.** The MCP client launches this server with the working
61
+ > directory inherited from wherever the editor was opened — which may be a
62
+ > subdirectory of your repo (e.g. `sdk/`), not the repo root. To stay independent
63
+ > of that working directory, each data path is resolved in this order:
64
+ >
65
+ > 1. **Explicit env override.** If `DESIGN_DATA_PATH` / `DESIGN_DATA_COMPONENTS` /
66
+ > `DESIGN_DATA_FIELDS` is set, it is used. Relative values are anchored to
67
+ > `DESIGN_DATA_ROOT` (absolute, recommended when launching via `npx`) or, if
68
+ > that is unset, to the server package's own location in the monorepo. Absolute
69
+ > values are used as-is.
70
+ > 2. **Resolved `@adobe/spectrum-design-data` package** (zero config). When no env
71
+ > override is set, the server resolves the installed `@adobe/spectrum-design-data`
72
+ > package via Node module resolution and reads its `tokens/`, `components/`, and
73
+ > `fields/` directories. In a pnpm workspace this follows the symlink to
74
+ > `packages/design-data`; when published it uses the installed dependency. This
75
+ > is independent of the working directory.
76
+ > 3. **Fallback.** `dataPath` falls back to the (anchored) current directory; the
77
+ > component/field overrides fall back to the `design-data` binary's own
78
+ > discovery (including its embedded snapshot).
79
+ >
80
+ > In a monorepo checkout you typically need no `DESIGN_DATA_*` env vars at all —
81
+ > resolution via the workspace package handles it.
59
82
 
60
83
  ### Example (Cursor `.cursor/mcp.json`)
61
84
 
@@ -66,10 +89,10 @@ node tools/design-data-agent-mcp/src/index.js
66
89
  "command": "npx",
67
90
  "args": ["-y", "@adobe/design-data-agent-mcp"],
68
91
  "env": {
69
- "DESIGN_DATA_PATH": "./packages/tokens/src",
70
- "DESIGN_DATA_COMPONENTS": "./packages/design-data-spec/components",
71
- "DESIGN_DATA_FIELDS": "./packages/design-data-spec/fields",
72
- "DESIGN_DATA_DIMENSIONS": "./packages/design-data-spec/dimensions"
92
+ "DESIGN_DATA_ROOT": "/abs/path/to/your/repo",
93
+ "DESIGN_DATA_PATH": "packages/design-data/tokens",
94
+ "DESIGN_DATA_COMPONENTS": "packages/design-data/components",
95
+ "DESIGN_DATA_FIELDS": "packages/design-data/fields"
73
96
  }
74
97
  }
75
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/design-data-agent-mcp",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "MCP server and Claude Code skill for the design-data agent surface — shells out to the design-data CLI",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -29,7 +29,10 @@
29
29
  "provenance": true
30
30
  },
31
31
  "dependencies": {
32
- "@modelcontextprotocol/sdk": "^1.27.1"
32
+ "@modelcontextprotocol/sdk": "^1.27.1",
33
+ "@adobe/design-data": "2.0.0",
34
+ "@adobe/design-data-wasm": "0.1.0",
35
+ "@adobe/spectrum-design-data": "0.3.0"
33
36
  },
34
37
  "devDependencies": {
35
38
  "ava": "^6.0.1"
@@ -2,80 +2,76 @@
2
2
  name: design-data-agent
3
3
  description: >
4
4
  Validate, query, resolve, diff, and author spec-conformant design tokens and components using the
5
- design-data CLI against a local dataset. Use when the user asks about design tokens, a design
5
+ design-data MCP tools against a local dataset. Use when the user asks about design tokens, a design
6
6
  system, token lookup, spec-conformance, drift detection, or token authoring on custom data.
7
7
  when_to_use: >
8
8
  Trigger on: design system, design tokens, spec-conformant, drift, validate tokens, token
9
9
  authoring, custom dataset, DESIGN_DATA_PATH, design-data validate, design-data diff,
10
10
  design-data write, product-context.json.
11
- allowed-tools: Bash(npx @adobe/design-data *)
11
+ allowed-tools: mcp__design-data-agent__primer, mcp__design-data-agent__query_tokens, mcp__design-data-agent__resolve_token, mcp__design-data-agent__describe_component, mcp__design-data-agent__validate_usage, mcp__design-data-agent__diff_datasets, mcp__design-data-agent__write, mcp__design-data-agent__start_authoring_session, mcp__design-data-agent__authoring_session_step_intent, mcp__design-data-agent__authoring_session_step_classification, mcp__design-data-agent__authoring_session_step_values, mcp__design-data-agent__authoring_session_commit, mcp__design-data-agent__authoring_session_cancel, mcp__design-data-agent__authoring_session_get, mcp__design-data-agent__authoring_session_list
12
12
  ---
13
13
 
14
14
  # design-data agent skill
15
15
 
16
- `design-data` is the reference CLI for the Spectrum Design Data specification. It validates, queries, resolves, and authors spec-conformant tokens and components from any dataset on the local filesystem.
16
+ `@adobe/design-data-agent-mcp` provides in-process wasm tools for validating, querying,
17
+ resolving, diffing, and authoring spec-conformant tokens and components from any dataset
18
+ on the local filesystem.
17
19
 
18
- Set two path variables once and reference them throughout. The token dataset and the spec catalog (components, fields, dimensions) live in separate directories:
20
+ Set two path variables once and reference them throughout:
19
21
 
20
22
  ```bash
21
- export DESIGN_DATA_PATH=./packages/tokens/src
22
- export DESIGN_DATA_SPEC_PATH=./packages/design-data-spec
23
+ export DESIGN_DATA_PATH=./packages/design-data/tokens
24
+ export DESIGN_DATA_SPEC_PATH=./packages/design-data
23
25
  ```
24
26
 
25
27
  For Spectrum tokens with zero setup (embedded snapshot), use the `design-data` skill instead — this skill targets custom or repo-local datasets.
26
28
 
27
29
  ## Bootstrap
28
30
 
29
- On first use, ensure the CLI is available:
31
+ Add `@adobe/design-data-agent-mcp` to your `.cursor/mcp.json`:
30
32
 
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "design-data-agent": {
37
+ "command": "npx",
38
+ "args": ["-y", "@adobe/design-data-agent-mcp"],
39
+ "env": {
40
+ "DESIGN_DATA_PATH": "./packages/tokens/src",
41
+ "DESIGN_DATA_COMPONENTS": "./packages/design-data/components",
42
+ "DESIGN_DATA_FIELDS": "./packages/design-data/fields"
43
+ }
44
+ }
45
+ }
46
+ }
31
47
  ```
32
- npx @adobe/design-data --version
33
- ```
48
+
49
+ Adjust paths to match your dataset layout.
34
50
 
35
51
  ***
36
52
 
37
53
  ## Session start — call `primer` first
38
54
 
39
- Call `primer` at the start of every session that touches design data. It returns the active dimensions, component list, taxonomy fields, and token count — structural context that scopes all subsequent lookups.
40
-
41
- ```bash
42
- npx @adobe/design-data primer "$DESIGN_DATA_PATH" --format json \
43
- --components-dir "$DESIGN_DATA_SPEC_PATH/components" \
44
- --fields-dir "$DESIGN_DATA_SPEC_PATH/fields" \
45
- --dimensions-dir "$DESIGN_DATA_SPEC_PATH/dimensions"
46
- ```
47
-
48
- Always pass `--components-dir`, `--fields-dir`, and `--dimensions-dir` explicitly. These directories live under `packages/design-data-spec/`, not under the token dataset. The CLI defaults probe those paths relative to CWD, so omitting the flags when running from an arbitrary directory (or with an absolute `DESIGN_DATA_PATH`) produces empty `components`, `taxonomyFields`, and `dimensions`.
49
-
50
- The payload includes `specVersion`, `manifest`, `dimensions`, `components`, `taxonomyFields`, and `tokenCount`.
55
+ Call `primer` at the start of every session that touches design data. It returns the active
56
+ dimensions, component list, taxonomy fields, and token count — structural context that scopes
57
+ all subsequent lookups. No inputs required.
51
58
 
52
59
  ***
53
60
 
54
61
  ## Token lookup
55
62
 
56
- ### Resolve a token to its literal value
63
+ ### Resolve a token to its literal value — `resolve_token`
57
64
 
58
- ```bash
59
- npx @adobe/design-data resolve <property> "$DESIGN_DATA_PATH" --format json \
60
- [--color-scheme light|dark] \
61
- [--scale desktop|mobile] \
62
- [--contrast regular|high]
63
- ```
65
+ Required: `property` (string) — e.g. `"accent-background-color-default"`
66
+ Optional: `colorScheme` (`"light"` or `"dark"`), `scale` (`"desktop"` or `"mobile"`),
67
+ `contrast` (`"regular"` or `"high"`)
64
68
 
65
- Example:
69
+ ### Query tokens by filter expression — `query_tokens`
66
70
 
67
- ```bash
68
- npx @adobe/design-data resolve accent-background-color-default "$DESIGN_DATA_PATH" \
69
- --format json --color-scheme dark --scale desktop
70
- ```
71
-
72
- ### Query tokens by filter expression
71
+ Required: `filter` (string)
73
72
 
74
- ```bash
75
- npx @adobe/design-data query "$DESIGN_DATA_PATH" --filter "<expr>" --format json
76
- ```
77
-
78
- Valid filter keys: `property`, `component`, `variant`, `state`, `colorScheme`, `scale`, `contrast`, `uuid`, `$schema`. Any other key is a parse error. The dimension keys (`colorScheme`, `scale`, `contrast`) accept the same enum values as the `resolve` flags (`light`/`dark`, `desktop`/`mobile`, `regular`/`high`).
73
+ Valid filter keys: `property`, `component`, `variant`, `state`, `colorScheme`, `scale`,
74
+ `contrast`, `uuid`, `$schema`.
79
75
 
80
76
  Filter syntax examples:
81
77
 
@@ -88,64 +84,71 @@ property=background-color|property=border-color
88
84
  $schema=https://spectrum.adobe.com/page/design-token/
89
85
  ```
90
86
 
91
- > **Exit codes:** `0` = matches found; `1` = no matches (returns `[]` — not an error); `>1` = error.
87
+ > **Exit codes:** `0` = matches found; empty array = no matches (not an error).
92
88
 
93
89
  ***
94
90
 
95
- ## Component info
91
+ ## Component info — `describe_component`
96
92
 
97
- ```bash
98
- npx @adobe/design-data component <id> --components-dir "$DESIGN_DATA_SPEC_PATH/components"
99
- ```
93
+ Required: `id` (string) — kebab-case component ID, e.g. `button`, `action-button`
100
94
 
101
- Returns the component contract: `name`, `displayName`, `options`, `anatomy`, `states`, and `tokenBindings`. Output is always JSON; there is no `--format` flag on this subcommand.
95
+ Returns the component contract: `name`, `displayName`, `options`, `anatomy`, `states`,
96
+ and `tokenBindings`.
102
97
 
103
- Pass `--components-dir` explicitly for the same reason as `primer` — the default probes CWD-relative paths that won't resolve in agent contexts.
98
+ ***
104
99
 
105
- Example:
100
+ ## Validation — `validate_usage`
106
101
 
107
- ```bash
108
- npx @adobe/design-data component button --components-dir "$DESIGN_DATA_SPEC_PATH/components"
109
- ```
102
+ Optional inputs:
103
+
104
+ * `path` — dataset path (defaults to `DESIGN_DATA_PATH`)
105
+ * `strict` (boolean) — treat warnings as errors
106
+ * `schema_path` — override schemas directory (defaults to `@adobe/spectrum-tokens` schemas)
107
+
108
+ Runs Layer-1 JSON-Schema structural validation and Layer-2 relational rules.
109
+ Returns `{ valid, errors, warnings }`.
110
+
111
+ > **Note:** `--exceptions-path` (SPEC-007 naming allowlist) is not supported in the
112
+ > in-process path. Use the `design-data` CLI directly if you need exceptions support.
110
113
 
111
114
  ***
112
115
 
113
- ## Validation
116
+ ## Dataset diff — `diff_datasets`
114
117
 
115
- ```bash
116
- npx @adobe/design-data validate "$DESIGN_DATA_PATH" --format json \
117
- [--schema-path <path>] \
118
- [--exceptions-path <path>] \
119
- [--strict]
120
- ```
118
+ Required: `oldPath`, `newPath`
119
+ Optional: `filter` (substring to narrow results by token name)
121
120
 
122
- Returns a `ValidationReport` object: `{ layer: 1|2, errors: [...], warnings: [...] }` where each diagnostic has `rule_id` (e.g. `"SPEC-002"`), `severity` (`"error"` | `"warning"`), and `message`. Layer 1 is schema validation; Layer 2 is cascade-rule validation. `--strict` treats warnings as errors.
121
+ Returns `{ renamed, deprecated, reverted, added, deleted, updated }`.
123
122
 
124
123
  ***
125
124
 
126
- ## Dataset diff
125
+ ## Product-layer authoring — `write`
127
126
 
128
- ```bash
129
- npx @adobe/design-data diff <old-path> <new-path> --format json [--filter <expr>]
130
- ```
127
+ Write or update the product context document in the dataset.
131
128
 
132
- > **Exit codes:** `0` = no changes; `1` = differences found (returns JSON diff — not an error); `>1` = error.
129
+ Optional inputs: `output` (defaults to `$DESIGN_DATA_PATH/product-context.json`),
130
+ `rationale` (string)
133
131
 
134
132
  ***
135
133
 
136
- ## Product-layer authoring
134
+ ## Token authoring session
137
135
 
138
- Write or update the product context document in the dataset:
136
+ Use the following tools in sequence to create a new token through the wizard:
139
137
 
140
- ```bash
141
- npx @adobe/design-data write \
142
- --output "$DESIGN_DATA_PATH/product-context.json" \
143
- --rationale "Why these overrides exist"
144
- ```
138
+ 1. **`start_authoring_session`** — start a session (returns `session_id`)
139
+ 2. **`authoring_session_step_intent`** — provide natural-language intent; get token suggestions
140
+ 3. **`authoring_session_step_classification`** — set layer, property, name fields
141
+ 4. **`authoring_session_step_values`** set mode-specific value rows
142
+ 5. **`authoring_session_commit`** — validate and write the token to disk
143
+ 6. **`authoring_session_cancel`** — cancel without writing
145
144
 
146
- Always pass `--output` with an explicit path inside the dataset. The default resolves relative to CWD, which is rarely correct in agent contexts.
145
+ Helper tools: `authoring_session_get` (inspect state), `authoring_session_list` (all active sessions).
147
146
 
148
- Generates a product-context scaffold at the output path (`specVersion`, `layer: "product"`, `rationale`, attribution metadata). Creates the file if absent; overwrites if it already exists. The confirmation string returned to stdout on success is a plain text message — not JSON.
147
+ `authoring_session_commit` accepts an optional `schema_path` to override the schemas directory
148
+ for Layer-1 JSON-Schema validation before writing.
149
+
150
+ > **Note:** `authoring_session_step_intent` (NLP suggestion ranking) still delegates to the
151
+ > `design-data` CLI because the NLP `suggest` API is not yet on the wasm surface.
149
152
 
150
153
  ***
151
154
 
@@ -153,9 +156,8 @@ Generates a product-context scaffold at the output path (`specVersion`, `layer:
153
156
 
154
157
  * **Scale values:** `desktop` and `mobile` — not `medium`/`large`.
155
158
  * **Contrast values:** `regular` and `high` — not `standard`/`high`.
156
- * **`query` exit 1** means no matches and still emits `[]`. Only `>1` is an error.
157
- * **`diff` exit 1** means changes were found. The JSON diff is in stdout — still a success result.
158
- * **`--format json`** — always pass this in agent and script contexts; the CLI pretty-prints when stdout is a TTY.
159
+ * **`query_tokens` returns `[]`** when no tokens match not an error.
160
+ * **`diff_datasets` filter** matches by token name substring (case-insensitive).
159
161
 
160
162
  ## When working in Cursor
161
163
 
@@ -164,24 +166,3 @@ Cursor Settings → Rules → **Add Rule** → **Remote Rule (GitHub)** → past
164
166
  ```
165
167
  https://github.com/adobe/spectrum-design-data/tree/main/tools/design-data-agent-mcp/skills/design-data
166
168
  ```
167
-
168
- For always-available tool access (higher context cost), add `@adobe/design-data-agent-mcp` to `.cursor/mcp.json`:
169
-
170
- ```json
171
- {
172
- "mcpServers": {
173
- "design-data-agent": {
174
- "command": "npx",
175
- "args": ["-y", "@adobe/design-data-agent-mcp"],
176
- "env": {
177
- "DESIGN_DATA_PATH": "./packages/tokens/src",
178
- "DESIGN_DATA_COMPONENTS": "./packages/design-data-spec/components",
179
- "DESIGN_DATA_FIELDS": "./packages/design-data-spec/fields",
180
- "DESIGN_DATA_DIMENSIONS": "./packages/design-data-spec/dimensions"
181
- }
182
- }
183
- }
184
- }
185
- ```
186
-
187
- Adjust paths to match your dataset layout.
package/src/cli.js CHANGED
@@ -14,6 +14,10 @@ import { config } from "./config.js";
14
14
  export function runCli(args, { timeout = 10_000 } = {}) {
15
15
  return new Promise((resolve, reject) => {
16
16
  const proc = spawn(config.bin, args, {
17
+ // Anchor the CLI's working directory to the resolved data root so its own
18
+ // tier/probe logic (data_source::resolve, is_in_repo) resolves correctly
19
+ // even when Claude Code launched the server from a monorepo subdirectory.
20
+ cwd: config.dataRoot,
17
21
  // isolates CLI stdout from the MCP JSON-RPC stream on the parent's stdout
18
22
  stdio: ["ignore", "pipe", "pipe"],
19
23
  });
package/src/config.js CHANGED
@@ -8,12 +8,70 @@
8
8
  // OF ANY KIND, either express or implied. See the License for the specific language
9
9
  // governing permissions and limitations under the License.
10
10
 
11
+ import { createRequire } from "module";
12
+ import { fileURLToPath } from "url";
13
+ import { dirname, isAbsolute, resolve } from "path";
14
+
15
+ // The repo root relative to this file: src → package → tools → repo root.
16
+ // Used as the self-anchoring fallback when DESIGN_DATA_ROOT is not provided
17
+ // and the server is run from inside the monorepo checkout.
18
+ const SERVER_ROOT = fileURLToPath(new URL("../../..", import.meta.url));
19
+
20
+ // Claude Code launches the MCP server with the working directory inherited from
21
+ // wherever the editor was opened (e.g. `sdk/`), not the repo root. Relative
22
+ // DESIGN_DATA_* paths must therefore be anchored to a known root rather than the
23
+ // process CWD. Prefer an explicit absolute DESIGN_DATA_ROOT (works even when the
24
+ // server is launched via `npx` from the npm cache); fall back to SERVER_ROOT for
25
+ // in-repo runs.
26
+ const dataRoot = process.env.DESIGN_DATA_ROOT
27
+ ? resolve(process.env.DESIGN_DATA_ROOT)
28
+ : SERVER_ROOT;
29
+
30
+ function anchorPath(p) {
31
+ return p && !isAbsolute(p) ? resolve(dataRoot, p) : p;
32
+ }
33
+
34
+ // Locate a directory inside the @adobe/spectrum-design-data package via Node's
35
+ // module resolution. In a pnpm workspace this resolves through the symlink to
36
+ // `packages/design-data`; when published it resolves the installed dependency.
37
+ // Either way it is independent of the process working directory, so it provides
38
+ // a zero-config default that does not depend on where the server was launched.
39
+ // Returns null when the package is not installed (e.g. a standalone CLI install
40
+ // that relies on the design-data binary's embedded snapshot instead).
41
+ function resolveDataPackageDir(subdir) {
42
+ try {
43
+ const pkgJson = createRequire(import.meta.url).resolve(
44
+ "@adobe/spectrum-design-data/package.json",
45
+ );
46
+ return resolve(dirname(pkgJson), subdir);
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ // Path precedence: explicit env override (anchored to dataRoot) wins, then the
53
+ // resolved design-data package directory, then a final fallback.
54
+ //
55
+ // The fallback differs by field on purpose: `dataPath` is a required positional
56
+ // CLI argument, so it falls back to the anchored current directory. `componentsDir`
57
+ // and `fieldsDir` are optional `--components-dir`/`--fields-dir` flags, so they fall
58
+ // back to `null` (flag omitted) and let the design-data binary's own discovery —
59
+ // including its embedded snapshot — locate them.
11
60
  export const config = {
12
61
  bin: process.env.DESIGN_DATA_BIN ?? "design-data",
13
- dataPath: process.env.DESIGN_DATA_PATH ?? ".",
14
- schemaPath: process.env.DESIGN_DATA_SCHEMAS ?? null,
15
- exceptionsPath: process.env.DESIGN_DATA_EXCEPTIONS ?? null,
16
- componentsDir: process.env.DESIGN_DATA_COMPONENTS ?? null,
17
- fieldsDir: process.env.DESIGN_DATA_FIELDS ?? null,
18
- dimensionsDir: process.env.DESIGN_DATA_DIMENSIONS ?? null,
62
+ dataRoot,
63
+ dataPath:
64
+ anchorPath(process.env.DESIGN_DATA_PATH) ??
65
+ resolveDataPackageDir("tokens") ??
66
+ anchorPath("."),
67
+ schemaPath: anchorPath(process.env.DESIGN_DATA_SCHEMAS ?? null),
68
+ exceptionsPath: anchorPath(process.env.DESIGN_DATA_EXCEPTIONS ?? null),
69
+ componentsDir:
70
+ anchorPath(process.env.DESIGN_DATA_COMPONENTS) ??
71
+ resolveDataPackageDir("components") ??
72
+ null,
73
+ fieldsDir:
74
+ anchorPath(process.env.DESIGN_DATA_FIELDS) ??
75
+ resolveDataPackageDir("fields") ??
76
+ null,
19
77
  };
@@ -8,21 +8,27 @@
8
8
  // OF ANY KIND, either express or implied. See the License for the specific language
9
9
  // governing permissions and limitations under the License.
10
10
 
11
- //! MCP authoring-session tools (RFC #973 Q4).
12
- //!
13
- //! Each tool maps to one `design-data authoring-session` CLI subcommand.
14
- //! State is held on disk (one JSON file per session); the CLI is stateless.
11
+ /**
12
+ * Authoring-session tools for design-data-agent-mcp.
13
+ *
14
+ * Most operations use @adobe/design-data (on-disk session store) and run
15
+ * fully in-process. The exception is authoring_session_step_intent, which still
16
+ * delegates to the CLI because the NLP `suggest` ranking is not yet on the wasm
17
+ * surface. When that API is added, step_intent can be migrated here too.
18
+ */
15
19
 
20
+ import {
21
+ startSession,
22
+ getSession,
23
+ listSessions,
24
+ stepClassification,
25
+ stepValues,
26
+ commitSession,
27
+ cancelSession,
28
+ } from "@adobe/design-data/session";
16
29
  import { runCli } from "../cli.js";
17
30
  import { config } from "../config.js";
18
31
 
19
- async function callCli(args) {
20
- const { exitCode, stdout, stderr } = await runCli(args, { timeout: 30_000 });
21
- if (exitCode !== 0)
22
- throw new Error(stderr || `authoring-session exited ${exitCode}`);
23
- return JSON.parse(stdout);
24
- }
25
-
26
32
  export function createAuthoringTools() {
27
33
  return [
28
34
  {
@@ -48,7 +54,7 @@ export function createAuthoringTools() {
48
54
  "dataset_path is required (or set DESIGN_DATA_PATH in the environment)",
49
55
  );
50
56
  }
51
- return callCli(["authoring-session", "start", path]);
57
+ return startSession(path);
52
58
  },
53
59
  },
54
60
 
@@ -71,15 +77,25 @@ export function createAuthoringTools() {
71
77
  additionalProperties: false,
72
78
  },
73
79
  async handler({ session_id, intent }) {
74
- return callCli([
75
- "authoring-session",
76
- "step",
77
- "intent",
78
- "--session-id",
79
- session_id,
80
- "--intent",
81
- intent,
82
- ]);
80
+ // step_intent requires NLP suggest ranking — still uses the CLI.
81
+ // Will be migrated when suggest is added to the wasm surface.
82
+ const { exitCode, stdout, stderr } = await runCli(
83
+ [
84
+ "authoring-session",
85
+ "step",
86
+ "intent",
87
+ "--session-id",
88
+ session_id,
89
+ "--intent",
90
+ intent,
91
+ ],
92
+ { timeout: 30_000 },
93
+ );
94
+ if (exitCode !== 0)
95
+ throw new Error(
96
+ stderr || `authoring-session step intent exited ${exitCode}`,
97
+ );
98
+ return JSON.parse(stdout);
83
99
  },
84
100
  },
85
101
 
@@ -118,21 +134,11 @@ export function createAuthoringTools() {
118
134
  additionalProperties: false,
119
135
  },
120
136
  async handler({ session_id, layer, property, name_fields = [] }) {
121
- const args = [
122
- "authoring-session",
123
- "step",
124
- "classification",
125
- "--session-id",
126
- session_id,
127
- "--layer",
137
+ return stepClassification(session_id, {
128
138
  layer,
129
- "--property",
130
139
  property,
131
- ];
132
- for (const { key, value } of name_fields) {
133
- args.push("--name-field", `${key}=${value}`);
134
- }
135
- return callCli(args);
140
+ nameFields: name_fields,
141
+ });
136
142
  },
137
143
  },
138
144
 
@@ -174,15 +180,7 @@ export function createAuthoringTools() {
174
180
  additionalProperties: false,
175
181
  },
176
182
  async handler({ session_id, rows }) {
177
- return callCli([
178
- "authoring-session",
179
- "step",
180
- "values",
181
- "--session-id",
182
- session_id,
183
- "--rows",
184
- JSON.stringify(rows),
185
- ]);
183
+ return stepValues(session_id, rows);
186
184
  },
187
185
  },
188
186
 
@@ -217,7 +215,9 @@ export function createAuthoringTools() {
217
215
  schema_path: {
218
216
  type: "string",
219
217
  description:
220
- "Path to schemas directory. Defaults to packages/tokens/schemas relative to target.",
218
+ "Path to schemas directory containing token-types/ and token-file.json. " +
219
+ "Used for Layer-1 JSON-Schema validation before writing. " +
220
+ "Defaults to @adobe/spectrum-tokens schemas when omitted.",
221
221
  },
222
222
  is_override: {
223
223
  type: "boolean",
@@ -236,22 +236,15 @@ export function createAuthoringTools() {
236
236
  schema_path,
237
237
  is_override = false,
238
238
  }) {
239
- const args = [
240
- "authoring-session",
241
- "commit",
242
- "--session-id",
243
- session_id,
244
- "--schema-url",
245
- schema_url,
246
- "--target",
239
+ return commitSession({
240
+ sessionId: session_id,
241
+ schemaUrl: schema_url,
247
242
  target,
248
- "--rationale",
249
243
  rationale,
250
- ];
251
- if (product_context) args.push("--product-context", product_context);
252
- if (schema_path) args.push("--schema-path", schema_path);
253
- if (is_override) args.push("--is-override");
254
- return callCli(args);
244
+ productContext: product_context,
245
+ schemaPath: schema_path ?? null,
246
+ isOverride: is_override,
247
+ });
255
248
  },
256
249
  },
257
250
 
@@ -261,18 +254,11 @@ export function createAuthoringTools() {
261
254
  inputSchema: {
262
255
  type: "object",
263
256
  required: ["session_id"],
264
- properties: {
265
- session_id: { type: "string" },
266
- },
257
+ properties: { session_id: { type: "string" } },
267
258
  additionalProperties: false,
268
259
  },
269
260
  async handler({ session_id }) {
270
- return callCli([
271
- "authoring-session",
272
- "cancel",
273
- "--session-id",
274
- session_id,
275
- ]);
261
+ return cancelSession(session_id);
276
262
  },
277
263
  },
278
264
 
@@ -282,18 +268,11 @@ export function createAuthoringTools() {
282
268
  inputSchema: {
283
269
  type: "object",
284
270
  required: ["session_id"],
285
- properties: {
286
- session_id: { type: "string" },
287
- },
271
+ properties: { session_id: { type: "string" } },
288
272
  additionalProperties: false,
289
273
  },
290
274
  async handler({ session_id }) {
291
- return callCli([
292
- "authoring-session",
293
- "get",
294
- "--session-id",
295
- session_id,
296
- ]);
275
+ return getSession(session_id);
297
276
  },
298
277
  },
299
278
 
@@ -306,7 +285,7 @@ export function createAuthoringTools() {
306
285
  additionalProperties: false,
307
286
  },
308
287
  async handler() {
309
- return callCli(["authoring-session", "list"]);
288
+ return listSessions();
310
289
  },
311
290
  },
312
291
  ];
package/src/tools/diff.js CHANGED
@@ -8,7 +8,32 @@
8
8
  // OF ANY KIND, either express or implied. See the License for the specific language
9
9
  // governing permissions and limitations under the License.
10
10
 
11
- import { runCli } from "../cli.js";
11
+ import { loadDataset } from "@adobe/design-data/load";
12
+
13
+ /**
14
+ * Filter a diff result by a substring match against token names.
15
+ *
16
+ * The diff() return shape uses camelCase (per wasm serde rename_all = "camelCase"):
17
+ * - renamed entries: { oldName, newName, ... }
18
+ * - all other entries: { name, ... }
19
+ *
20
+ * @param {object} diff - DiffResult from Dataset.diff().
21
+ * @param {string} filter - Substring to match (case-insensitive).
22
+ * @returns {object} Filtered diff with the same top-level keys.
23
+ */
24
+ export function filterDiffByName(diff, filter) {
25
+ const f = filter.toLowerCase();
26
+ const matchName = (t) =>
27
+ [t.name, t.oldName, t.newName].some((n) => n?.toLowerCase().includes(f));
28
+ return {
29
+ renamed: diff.renamed.filter(matchName),
30
+ deprecated: diff.deprecated.filter(matchName),
31
+ reverted: diff.reverted.filter(matchName),
32
+ added: diff.added.filter(matchName),
33
+ deleted: diff.deleted.filter(matchName),
34
+ updated: diff.updated.filter(matchName),
35
+ };
36
+ }
12
37
 
13
38
  export function createDiffTools() {
14
39
  return [
@@ -36,12 +61,13 @@ export function createDiffTools() {
36
61
  additionalProperties: false,
37
62
  },
38
63
  async handler({ oldPath, newPath, filter }) {
39
- const args = ["diff", oldPath, newPath, "--format", "json"];
40
- if (filter) args.push("--filter", filter);
41
- const { exitCode, stdout, stderr } = await runCli(args);
42
- // exit code 1 means differences found — that is a valid result, not an error
43
- if (exitCode > 1) throw new Error(stderr || `diff exited ${exitCode}`);
44
- return JSON.parse(stdout);
64
+ const [oldDs, newDs] = await Promise.all([
65
+ loadDataset(oldPath),
66
+ loadDataset(newPath),
67
+ ]);
68
+ const diff = oldDs.diff(newDs);
69
+ if (!filter) return diff;
70
+ return filterDiffByName(diff, filter);
45
71
  },
46
72
  },
47
73
  ];
package/src/tools/read.js CHANGED
@@ -8,6 +8,19 @@
8
8
  // OF ANY KIND, either express or implied. See the License for the specific language
9
9
  // governing permissions and limitations under the License.
10
10
 
11
+ /**
12
+ * Read tools for design-data-agent-mcp.
13
+ *
14
+ * query_tokens and resolve_token use @adobe/design-data (loadDataset) + the
15
+ * wasm Dataset to run in-process without spawning the CLI binary.
16
+ *
17
+ * primer and describe_component still invoke the CLI: primer aggregates complex
18
+ * catalog metadata (components, fields) not yet on the wasm surface, and
19
+ * describe_component requires the components catalog path resolution that the CLI
20
+ * handles. These will be ported when those APIs are added to the wasm surface.
21
+ */
22
+
23
+ import { loadDataset } from "@adobe/design-data/load";
11
24
  import { runCli } from "../cli.js";
12
25
  import { config } from "../config.js";
13
26
 
@@ -27,14 +40,13 @@ export function createReadTools() {
27
40
  if (config.componentsDir)
28
41
  args.push("--components-dir", config.componentsDir);
29
42
  if (config.fieldsDir) args.push("--fields-dir", config.fieldsDir);
30
- if (config.dimensionsDir)
31
- args.push("--dimensions-dir", config.dimensionsDir);
32
43
  const { exitCode, stdout, stderr } = await runCli(args);
33
44
  if (exitCode !== 0)
34
45
  throw new Error(stderr || `primer exited ${exitCode}`);
35
46
  return JSON.parse(stdout);
36
47
  },
37
48
  },
49
+
38
50
  {
39
51
  name: "resolve_token",
40
52
  description:
@@ -66,19 +78,21 @@ export function createReadTools() {
66
78
  additionalProperties: false,
67
79
  },
68
80
  async handler({ property, colorScheme, scale, contrast }) {
69
- const args = ["resolve", property, config.dataPath, "--format", "json"];
70
- // resolve uses --dimensions-path (old flag name, not --dimensions-dir)
71
- if (config.dimensionsDir)
72
- args.push("--dimensions-path", config.dimensionsDir);
73
- if (colorScheme) args.push("--color-scheme", colorScheme);
74
- if (scale) args.push("--scale", scale);
75
- if (contrast) args.push("--contrast", contrast);
76
- const { exitCode, stdout, stderr } = await runCli(args);
77
- if (exitCode !== 0)
78
- throw new Error(stderr || `resolve exited ${exitCode}`);
79
- return JSON.parse(stdout);
81
+ const ds = await loadDataset(config.dataPath);
82
+ const context = {};
83
+ if (colorScheme) context.colorScheme = colorScheme;
84
+ if (scale) context.scale = scale;
85
+ if (contrast) context.contrast = contrast;
86
+ const result = ds.resolve(property, context);
87
+ if (!result) {
88
+ throw new Error(
89
+ `No token found for property "${property}" in context ${JSON.stringify(context)}`,
90
+ );
91
+ }
92
+ return result;
80
93
  },
81
94
  },
95
+
82
96
  {
83
97
  name: "query_tokens",
84
98
  description:
@@ -95,20 +109,11 @@ export function createReadTools() {
95
109
  additionalProperties: false,
96
110
  },
97
111
  async handler({ filter }) {
98
- const args = [
99
- "query",
100
- config.dataPath,
101
- "--filter",
102
- filter,
103
- "--format",
104
- "json",
105
- ];
106
- const { exitCode, stdout, stderr } = await runCli(args);
107
- // exit code 1 means no matches — still valid JSON []
108
- if (exitCode > 1) throw new Error(stderr || `query exited ${exitCode}`);
109
- return JSON.parse(stdout);
112
+ const ds = await loadDataset(config.dataPath);
113
+ return ds.query(filter);
110
114
  },
111
115
  },
116
+
112
117
  {
113
118
  name: "describe_component",
114
119
  description:
@@ -8,7 +8,7 @@
8
8
  // OF ANY KIND, either express or implied. See the License for the specific language
9
9
  // governing permissions and limitations under the License.
10
10
 
11
- import { runCli } from "../cli.js";
11
+ import { validateDataset } from "@adobe/design-data/validate";
12
12
  import { config } from "../config.js";
13
13
 
14
14
  export function createValidateTools() {
@@ -16,7 +16,8 @@ export function createValidateTools() {
16
16
  {
17
17
  name: "validate_usage",
18
18
  description:
19
- "Validate design token usage in a dataset. Returns a JSON report of violations and warnings.",
19
+ "Validate design token usage in a dataset. Runs Layer-1 JSON-Schema structural " +
20
+ "validation and Layer-2 relational rules. Returns a JSON report of violations and warnings.",
20
21
  inputSchema: {
21
22
  type: "object",
22
23
  properties: {
@@ -26,23 +27,24 @@ export function createValidateTools() {
26
27
  "Path to dataset to validate (defaults to DESIGN_DATA_PATH)",
27
28
  },
28
29
  strict: { type: "boolean", description: "Treat warnings as errors" },
30
+ schema_path: {
31
+ type: "string",
32
+ description:
33
+ "Path to schemas directory containing token-types/ and token-file.json. " +
34
+ "Defaults to @adobe/spectrum-tokens schemas. Set DESIGN_DATA_SCHEMAS env var or " +
35
+ "pass explicitly for custom schema sets.",
36
+ },
29
37
  },
30
38
  additionalProperties: false,
31
39
  },
32
- async handler({ path, strict } = {}) {
40
+ async handler({ path, strict, schema_path } = {}) {
33
41
  const target = path ?? config.dataPath;
34
- const args = ["validate", target, "--format", "json"];
35
- if (config.schemaPath) args.push("--schema-path", config.schemaPath);
36
- if (config.exceptionsPath)
37
- args.push("--exceptions-path", config.exceptionsPath);
38
- // validate uses --dimensions-path (old flag name, not --dimensions-dir)
39
- if (config.dimensionsDir)
40
- args.push("--dimensions-path", config.dimensionsDir);
41
- if (strict === true) args.push("--strict");
42
- const { exitCode, stdout, stderr } = await runCli(args);
43
- if (exitCode !== 0 && !stdout)
44
- throw new Error(stderr || `validate exited ${exitCode}`);
45
- return JSON.parse(stdout);
42
+ const schemaPath = schema_path ?? config.schemaPath ?? null;
43
+ // NOTE: exceptionsPath (DESIGN_DATA_EXCEPTIONS / --exceptions-path) applies to the
44
+ // SPEC-007 naming rule in the relational layer. The in-process wasm validate() does
45
+ // not consume it. Passing exceptionsPath here would throw an explicit error from
46
+ // validateDataset omit it and document the limitation.
47
+ return validateDataset(target, { schemaPath, strict: strict ?? false });
46
48
  },
47
49
  },
48
50
  ];
@@ -9,7 +9,7 @@
9
9
  // governing permissions and limitations under the License.
10
10
 
11
11
  import { join } from "path";
12
- import { runCli } from "../cli.js";
12
+ import { writeProductContext } from "@adobe/design-data/write";
13
13
  import { config } from "../config.js";
14
14
 
15
15
  export function createWriteTools() {
@@ -36,12 +36,7 @@ export function createWriteTools() {
36
36
  async handler({ output, rationale } = {}) {
37
37
  const resolvedOutput =
38
38
  output ?? join(config.dataPath, "product-context.json");
39
- const args = ["write", "--output", resolvedOutput];
40
- if (rationale) args.push("--rationale", rationale);
41
- const { exitCode, stdout, stderr } = await runCli(args);
42
- if (exitCode !== 0)
43
- throw new Error(stderr || `write exited ${exitCode}`);
44
- return stdout;
39
+ return writeProductContext({ output: resolvedOutput, rationale });
45
40
  },
46
41
  },
47
42
  ];