@adobe/design-data-agent-mcp 1.10.0 → 1.11.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/README.md +7 -5
- package/package.json +3 -3
- package/skills/design-data/SKILL.md +1 -1
- package/src/cascade-bootstrap.js +89 -3
- package/src/tools/read.js +65 -16
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/) agent surface. Read tools (`primer`, `resolve_token`, `query_tokens`, `describe_component`, `describe_guideline`) run fully in-process via `@adobe/design-data-wasm` — no CLI binary required for those. Only `authoring_session_step_intent` still invokes the native binary (for NLP suggest ranking, not yet on the wasm surface).
|
|
3
|
+
MCP server and Claude Code skill for the [Spectrum Design Data](../../packages/design-data/) agent surface. Read tools (`primer`, `resolve_token`, `query_tokens`, `describe_component`, `describe_guideline`, `list_guidelines`) run fully in-process via `@adobe/design-data-wasm` — no CLI binary required for those. Only `authoring_session_step_intent` still invokes the native binary (for NLP suggest ranking, not yet on the wasm surface).
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -77,10 +77,11 @@ node tools/design-data-agent-mcp/src/index.js
|
|
|
77
77
|
> json`, run from the config's directory) to resolve its `.design-data.toml`
|
|
78
78
|
> source (path/npm/github/git) and any top-level `manifest` cascade, then
|
|
79
79
|
> materializes the result to a temp dir and points `primer` / `resolve_token` /
|
|
80
|
-
> `query_tokens` / `validate_usage`
|
|
81
|
-
>
|
|
82
|
-
>
|
|
83
|
-
>
|
|
80
|
+
> `query_tokens` / `validate_usage` / `describe_component` /
|
|
81
|
+
> `describe_guideline` / `list_guidelines` at it instead of the embedded
|
|
82
|
+
> Spectrum snapshot. Component, relationship, and guideline catalogs are copied
|
|
83
|
+
> from the fallback dataset and overlaid with platform manifest extensions. If
|
|
84
|
+
> resolution fails
|
|
84
85
|
> (e.g. no network for a github source), the server logs a warning and falls
|
|
85
86
|
> back to the embedded/local dataset rather than crashing.
|
|
86
87
|
|
|
@@ -153,6 +154,7 @@ node tools/design-data-agent-mcp/src/index.js
|
|
|
153
154
|
| `query_tokens` | Filter tokens by expression |
|
|
154
155
|
| `describe_component` | Fetch component schema and token bindings |
|
|
155
156
|
| `describe_guideline` | Fetch guideline documentBlocks content and metadata |
|
|
157
|
+
| `list_guidelines` | List guideline slugs and metadata, optionally by category |
|
|
156
158
|
| `validate_usage` | Validate token usage and return a diagnostic report |
|
|
157
159
|
| `diff_datasets` | Compare two datasets and return a semantic diff |
|
|
158
160
|
| `write` | Write agent-generated product context to the dataset |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/design-data-agent-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "MCP server and Claude Code skill for the design-data agent surface — read tools run in-process via wasm",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
33
|
-
"@adobe/design-data": "3.
|
|
33
|
+
"@adobe/design-data": "3.2.0",
|
|
34
34
|
"@adobe/design-data-wasm": "0.9.0",
|
|
35
|
-
"@adobe/spectrum-design-data": "3.2.
|
|
35
|
+
"@adobe/spectrum-design-data": "3.2.5"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"ava": "^6.0.1"
|
|
@@ -6,7 +6,7 @@ description: >
|
|
|
6
6
|
system, token lookup, spec-conformance, drift detection, or token authoring on custom data.
|
|
7
7
|
metadata:
|
|
8
8
|
author: adobe
|
|
9
|
-
version: "1.
|
|
9
|
+
version: "1.11.0"
|
|
10
10
|
when_to_use: >
|
|
11
11
|
Trigger on: design system, design tokens, spec-conformant, drift, validate tokens, token
|
|
12
12
|
authoring, custom dataset, DESIGN_DATA_PATH, design-data validate, design-data diff,
|
package/src/cascade-bootstrap.js
CHANGED
|
@@ -17,11 +17,22 @@
|
|
|
17
17
|
* The CLI already does config discovery, github fetch/cache, and manifest-cascade
|
|
18
18
|
* application on every data-touching subcommand — this just shells out to it once
|
|
19
19
|
* (`design-data query --filter "" --format json`, cwd = the config's directory)
|
|
20
|
-
* and materializes the result as a
|
|
21
|
-
*
|
|
20
|
+
* and materializes the result as a cascade dataset in a temp dir, which
|
|
21
|
+
* loadDataset()/validateDataset() already know how to read. Catalog directories
|
|
22
|
+
* are copied from the configured fallback dataset and then overlaid with any
|
|
23
|
+
* manifest extensions so component/guideline reads see the same cascade.
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
cpSync,
|
|
28
|
+
existsSync,
|
|
29
|
+
mkdirSync,
|
|
30
|
+
mkdtempSync,
|
|
31
|
+
readdirSync,
|
|
32
|
+
readFileSync,
|
|
33
|
+
statSync,
|
|
34
|
+
writeFileSync,
|
|
35
|
+
} from "node:fs";
|
|
25
36
|
import { tmpdir } from "node:os";
|
|
26
37
|
import { dirname, join } from "node:path";
|
|
27
38
|
import { runCli } from "./cli.js";
|
|
@@ -32,6 +43,80 @@ function resolveConfigDir(configPath) {
|
|
|
32
43
|
return statSync(configPath).isDirectory() ? configPath : dirname(configPath);
|
|
33
44
|
}
|
|
34
45
|
|
|
46
|
+
function resolveManifestPath(configDir) {
|
|
47
|
+
const configPath = join(configDir, ".design-data.toml");
|
|
48
|
+
if (!existsSync(configPath)) return null;
|
|
49
|
+
|
|
50
|
+
const configText = readFileSync(configPath, "utf-8");
|
|
51
|
+
const match = configText.match(/^\s*manifest\s*=\s*["']([^"']+)["']\s*$/m);
|
|
52
|
+
const manifestPath = join(configDir, match?.[1] ?? "manifest.json");
|
|
53
|
+
return existsSync(manifestPath) ? manifestPath : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function materializeCatalogs(config, cascadeDir, configDir) {
|
|
57
|
+
const manifestPath = resolveManifestPath(configDir);
|
|
58
|
+
let extensionsDir = null;
|
|
59
|
+
if (manifestPath) {
|
|
60
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
61
|
+
extensionsDir = join(
|
|
62
|
+
dirname(manifestPath),
|
|
63
|
+
manifest.extensionsDir ?? "extensions",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const [key, subdir] of [
|
|
68
|
+
["componentsDir", "components"],
|
|
69
|
+
["relationshipsDir", "relationships"],
|
|
70
|
+
["guidelinesDir", "guidelines"],
|
|
71
|
+
]) {
|
|
72
|
+
const targetDir = join(cascadeDir, subdir);
|
|
73
|
+
mkdirSync(targetDir, { recursive: true });
|
|
74
|
+
const fallbackDir = config[key];
|
|
75
|
+
if (fallbackDir && existsSync(fallbackDir)) {
|
|
76
|
+
cpSync(fallbackDir, targetDir, { recursive: true, force: true });
|
|
77
|
+
}
|
|
78
|
+
const extensionDir = extensionsDir && join(extensionsDir, subdir);
|
|
79
|
+
if (extensionDir && existsSync(extensionDir)) {
|
|
80
|
+
cpSync(extensionDir, targetDir, { recursive: true, force: true });
|
|
81
|
+
if (subdir === "guidelines") {
|
|
82
|
+
mergeGuidelineManifest(targetDir, extensionDir);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function mergeGuidelineManifest(targetDir, extensionDir) {
|
|
89
|
+
const manifestPath = join(targetDir, "manifest.json");
|
|
90
|
+
const manifest = existsSync(manifestPath)
|
|
91
|
+
? JSON.parse(readFileSync(manifestPath, "utf-8"))
|
|
92
|
+
: { guidelines: [] };
|
|
93
|
+
const entries = new Map(
|
|
94
|
+
(manifest.guidelines ?? []).map((entry) => [entry.slug, entry]),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
for (const file of readdirSync(extensionDir)) {
|
|
98
|
+
if (!file.endsWith(".json") || file === "manifest.json") continue;
|
|
99
|
+
const document = JSON.parse(
|
|
100
|
+
readFileSync(join(extensionDir, file), "utf-8"),
|
|
101
|
+
);
|
|
102
|
+
const slug = document.name ?? file.replace(/\.json$/, "");
|
|
103
|
+
entries.set(slug, {
|
|
104
|
+
slug,
|
|
105
|
+
...(document.title && { title: document.title }),
|
|
106
|
+
...(document.category && { category: document.category }),
|
|
107
|
+
...(document.status && { status: document.status }),
|
|
108
|
+
...(document.sourceUrl && { sourceUrl: document.sourceUrl }),
|
|
109
|
+
file: `guidelines/${file}`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
writeFileSync(
|
|
114
|
+
manifestPath,
|
|
115
|
+
JSON.stringify({ ...manifest, guidelines: [...entries.values()] }),
|
|
116
|
+
"utf-8",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
35
120
|
/**
|
|
36
121
|
* Resolve `config.designDataConfig`'s cascade and populate
|
|
37
122
|
* `config.cascadeDataPath` / `config.cascadeActive` with the result.
|
|
@@ -74,6 +159,7 @@ export async function bootstrapCascade(config, { run = runCli } = {}) {
|
|
|
74
159
|
JSON.stringify(tokens),
|
|
75
160
|
"utf-8",
|
|
76
161
|
);
|
|
162
|
+
materializeCatalogs(config, dir, configDir);
|
|
77
163
|
|
|
78
164
|
config.cascadeDataPath = dir;
|
|
79
165
|
config.cascadeActive = true;
|
package/src/tools/read.js
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* required. primer and describe_component were migrated in issue m1r.
|
|
16
16
|
* describe_guideline (spectrum-design-data-9fe.7) is the read-back counterpart to
|
|
17
17
|
* data_create/data_edit's "guidelines" category, closing the same read/write
|
|
18
|
-
* asymmetry describe_component already closes for components. Its guideline
|
|
19
|
-
*
|
|
18
|
+
* asymmetry describe_component already closes for components. Its guideline
|
|
19
|
+
* loading and listing logic is shared with the sibling
|
|
20
20
|
* @adobe/design-data-mcp package's `design-data-guideline` tool via the
|
|
21
21
|
* `@adobe/design-data/guideline` module, rather than duplicated here.
|
|
22
22
|
*
|
|
@@ -26,19 +26,17 @@
|
|
|
26
26
|
* Cascade scope (see cascade-bootstrap.js / spectrum-design-data-h890.14): once a
|
|
27
27
|
* `.design-data.toml` cascade is resolved, primer/resolve_token/query_tokens/
|
|
28
28
|
* validate_usage all reflect it (they read config.cascadeDataPath instead of
|
|
29
|
-
* config.dataPath when config.cascadeActive).
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* schemas, so this is left out of scope rather than guessed at; revisit if a platform
|
|
35
|
-
* manifest starts declaring components or guidelines.
|
|
29
|
+
* config.dataPath when config.cascadeActive). Cascade bootstrap copies the
|
|
30
|
+
* fallback component/relationship/guideline catalogs into that snapshot and
|
|
31
|
+
* overlays manifest extensions, so describe_component/describe_guideline/list_guidelines
|
|
32
|
+
* read the same active cascade while preserving the embedded fallback for inactive
|
|
33
|
+
* cascades.
|
|
36
34
|
*/
|
|
37
35
|
|
|
38
36
|
import { readFileSync, existsSync, readdirSync } from "fs";
|
|
39
37
|
import { join } from "path";
|
|
40
38
|
import { loadDataset } from "@adobe/design-data/load";
|
|
41
|
-
import { loadGuideline } from "@adobe/design-data/guideline";
|
|
39
|
+
import { listGuidelines, loadGuideline } from "@adobe/design-data/guideline";
|
|
42
40
|
import { config } from "../config.js";
|
|
43
41
|
import { checkDatasetFreshness } from "../dataset-freshness.js";
|
|
44
42
|
|
|
@@ -100,6 +98,15 @@ function validateGuidelineId(id) {
|
|
|
100
98
|
validateKebabId(id, "guideline");
|
|
101
99
|
}
|
|
102
100
|
|
|
101
|
+
function getCascadeAwareDir(baseDir, subdir) {
|
|
102
|
+
if (!baseDir || !config.cascadeActive || !config.cascadeDataPath) {
|
|
103
|
+
return baseDir;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const cascadeDir = join(config.cascadeDataPath, subdir);
|
|
107
|
+
return existsSync(cascadeDir) ? cascadeDir : baseDir;
|
|
108
|
+
}
|
|
109
|
+
|
|
103
110
|
export function createReadTools() {
|
|
104
111
|
return [
|
|
105
112
|
{
|
|
@@ -238,7 +245,10 @@ export function createReadTools() {
|
|
|
238
245
|
},
|
|
239
246
|
async handler({ id }) {
|
|
240
247
|
validateComponentId(id);
|
|
241
|
-
const componentsDir =
|
|
248
|
+
const componentsDir = getCascadeAwareDir(
|
|
249
|
+
config.componentsDir,
|
|
250
|
+
"components",
|
|
251
|
+
);
|
|
242
252
|
if (!componentsDir) {
|
|
243
253
|
throw new Error(
|
|
244
254
|
`@adobe/spectrum-design-data is not installed — cannot load component "${id}". ` +
|
|
@@ -268,7 +278,10 @@ export function createReadTools() {
|
|
|
268
278
|
// of the component file into relationships/<id>.json — merge them back
|
|
269
279
|
// in so callers relying on this tool for a component's token bindings
|
|
270
280
|
// still see them (see spectrum-design-data-x29.4).
|
|
271
|
-
const relationshipsDir =
|
|
281
|
+
const relationshipsDir = getCascadeAwareDir(
|
|
282
|
+
config.relationshipsDir,
|
|
283
|
+
"relationships",
|
|
284
|
+
);
|
|
272
285
|
const relationshipFile = relationshipsDir
|
|
273
286
|
? join(relationshipsDir, `${id}.json`)
|
|
274
287
|
: null;
|
|
@@ -302,7 +315,10 @@ export function createReadTools() {
|
|
|
302
315
|
},
|
|
303
316
|
async handler({ id }) {
|
|
304
317
|
validateGuidelineId(id);
|
|
305
|
-
const guidelinesDir =
|
|
318
|
+
const guidelinesDir = getCascadeAwareDir(
|
|
319
|
+
config.guidelinesDir,
|
|
320
|
+
"guidelines",
|
|
321
|
+
);
|
|
306
322
|
if (!guidelinesDir) {
|
|
307
323
|
throw new Error(
|
|
308
324
|
`@adobe/spectrum-design-data is not installed — cannot load guideline "${id}". ` +
|
|
@@ -315,9 +331,8 @@ export function createReadTools() {
|
|
|
315
331
|
if (err.message.startsWith("Not found:")) {
|
|
316
332
|
let available;
|
|
317
333
|
try {
|
|
318
|
-
available =
|
|
319
|
-
.
|
|
320
|
-
.map((f) => f.replace(/\.json$/, ""))
|
|
334
|
+
available = listGuidelines(guidelinesDir)
|
|
335
|
+
.map((guideline) => guideline.slug)
|
|
321
336
|
.sort()
|
|
322
337
|
.join(", ");
|
|
323
338
|
} catch {
|
|
@@ -332,5 +347,39 @@ export function createReadTools() {
|
|
|
332
347
|
}
|
|
333
348
|
},
|
|
334
349
|
},
|
|
350
|
+
|
|
351
|
+
{
|
|
352
|
+
name: "list_guidelines",
|
|
353
|
+
description:
|
|
354
|
+
"List available Spectrum design guidelines and their metadata. Optionally " +
|
|
355
|
+
"filter the catalog by category before calling describe_guideline.",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: "object",
|
|
358
|
+
properties: {
|
|
359
|
+
category: {
|
|
360
|
+
type: "string",
|
|
361
|
+
description:
|
|
362
|
+
"Optional guideline category, e.g. designing or implementing",
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
additionalProperties: false,
|
|
366
|
+
},
|
|
367
|
+
async handler({ category } = {}) {
|
|
368
|
+
const guidelinesDir = getCascadeAwareDir(
|
|
369
|
+
config.guidelinesDir,
|
|
370
|
+
"guidelines",
|
|
371
|
+
);
|
|
372
|
+
if (!guidelinesDir) {
|
|
373
|
+
throw new Error(
|
|
374
|
+
`@adobe/spectrum-design-data is not installed — cannot list guidelines. ` +
|
|
375
|
+
`Install it with: pnpm add @adobe/spectrum-design-data`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
const guidelines = listGuidelines(guidelinesDir);
|
|
379
|
+
return category
|
|
380
|
+
? guidelines.filter((guideline) => guideline.category === category)
|
|
381
|
+
: guidelines;
|
|
382
|
+
},
|
|
383
|
+
},
|
|
335
384
|
];
|
|
336
385
|
}
|