@adobe/design-data-agent-mcp 1.3.0 → 1.4.1

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/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.0",
3
+ "version": "1.4.1",
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",
@@ -14,10 +14,6 @@
14
14
  "README.md",
15
15
  "LICENSE"
16
16
  ],
17
- "scripts": {
18
- "start": "node src/index.js",
19
- "test": "ava"
20
- },
21
17
  "repository": {
22
18
  "type": "git",
23
19
  "url": "git+https://github.com/adobe/spectrum-design-data.git",
@@ -33,7 +29,8 @@
33
29
  "provenance": true
34
30
  },
35
31
  "dependencies": {
36
- "@modelcontextprotocol/sdk": "^1.27.1"
32
+ "@modelcontextprotocol/sdk": "^1.27.1",
33
+ "@adobe/spectrum-design-data": "0.3.0"
37
34
  },
38
35
  "devDependencies": {
39
36
  "ava": "^6.0.1"
@@ -50,5 +47,9 @@
50
47
  "cli"
51
48
  ],
52
49
  "author": "Adobe",
53
- "license": "Apache-2.0"
54
- }
50
+ "license": "Apache-2.0",
51
+ "scripts": {
52
+ "start": "node src/index.js",
53
+ "test": "ava"
54
+ }
55
+ }
@@ -18,8 +18,8 @@ allowed-tools: Bash(npx @adobe/design-data *)
18
18
  Set two path variables once and reference them throughout. The token dataset and the spec catalog (components, fields, dimensions) live in separate directories:
19
19
 
20
20
  ```bash
21
- export DESIGN_DATA_PATH=./packages/tokens/src
22
- export DESIGN_DATA_SPEC_PATH=./packages/design-data-spec
21
+ export DESIGN_DATA_PATH=./packages/design-data/tokens
22
+ export DESIGN_DATA_SPEC_PATH=./packages/design-data
23
23
  ```
24
24
 
25
25
  For Spectrum tokens with zero setup (embedded snapshot), use the `design-data` skill instead — this skill targets custom or repo-local datasets.
@@ -41,13 +41,12 @@ Call `primer` at the start of every session that touches design data. It returns
41
41
  ```bash
42
42
  npx @adobe/design-data primer "$DESIGN_DATA_PATH" --format json \
43
43
  --components-dir "$DESIGN_DATA_SPEC_PATH/components" \
44
- --fields-dir "$DESIGN_DATA_SPEC_PATH/fields" \
45
- --dimensions-dir "$DESIGN_DATA_SPEC_PATH/dimensions"
44
+ --fields-dir "$DESIGN_DATA_SPEC_PATH/fields"
46
45
  ```
47
46
 
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`.
47
+ Always pass `--components-dir` and `--fields-dir` explicitly. These directories live under `packages/design-data/`, alongside 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` and `taxonomyFields`.
49
48
 
50
- The payload includes `specVersion`, `manifest`, `dimensions`, `components`, `taxonomyFields`, and `tokenCount`.
49
+ The payload includes `specVersion`, `manifest`, `components`, `taxonomyFields`, and `tokenCount`.
51
50
 
52
51
  ***
53
52
 
@@ -175,9 +174,8 @@ For always-available tool access (higher context cost), add `@adobe/design-data-
175
174
  "args": ["-y", "@adobe/design-data-agent-mcp"],
176
175
  "env": {
177
176
  "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"
177
+ "DESIGN_DATA_COMPONENTS": "./packages/design-data/components",
178
+ "DESIGN_DATA_FIELDS": "./packages/design-data/fields"
181
179
  }
182
180
  }
183
181
  }
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
  };
package/src/index.js CHANGED
@@ -9,7 +9,7 @@
9
9
  // OF ANY KIND, either express or implied. See the License for the specific language
10
10
  // governing permissions and limitations under the License.
11
11
 
12
- import { readFileSync } from "fs";
12
+ import { readFileSync, realpathSync } from "fs";
13
13
  import { fileURLToPath } from "url";
14
14
  import { resolve, dirname } from "path";
15
15
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -89,7 +89,21 @@ async function startServer() {
89
89
  console.error("design-data-agent-mcp started");
90
90
  }
91
91
 
92
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
92
+ // Resolve real paths so the entry-point check still matches when this module is
93
+ // launched through a symlink (e.g. node_modules/.bin shim created by npx/pnpm).
94
+ function isMainModule() {
95
+ const entry = process.argv[1];
96
+ if (!entry) return false;
97
+ const here = fileURLToPath(import.meta.url);
98
+ if (entry === here) return true;
99
+ try {
100
+ return realpathSync(entry) === realpathSync(here);
101
+ } catch {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ if (isMainModule()) {
93
107
  startServer().catch((err) => {
94
108
  console.error(err);
95
109
  process.exit(1);
package/src/tools/read.js CHANGED
@@ -27,8 +27,6 @@ export function createReadTools() {
27
27
  if (config.componentsDir)
28
28
  args.push("--components-dir", config.componentsDir);
29
29
  if (config.fieldsDir) args.push("--fields-dir", config.fieldsDir);
30
- if (config.dimensionsDir)
31
- args.push("--dimensions-dir", config.dimensionsDir);
32
30
  const { exitCode, stdout, stderr } = await runCli(args);
33
31
  if (exitCode !== 0)
34
32
  throw new Error(stderr || `primer exited ${exitCode}`);
@@ -67,9 +65,6 @@ export function createReadTools() {
67
65
  },
68
66
  async handler({ property, colorScheme, scale, contrast }) {
69
67
  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
68
  if (colorScheme) args.push("--color-scheme", colorScheme);
74
69
  if (scale) args.push("--scale", scale);
75
70
  if (contrast) args.push("--contrast", contrast);
@@ -35,9 +35,6 @@ export function createValidateTools() {
35
35
  if (config.schemaPath) args.push("--schema-path", config.schemaPath);
36
36
  if (config.exceptionsPath)
37
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
38
  if (strict === true) args.push("--strict");
42
39
  const { exitCode, stdout, stderr } = await runCli(args);
43
40
  if (exitCode !== 0 && !stdout)