@adobe/design-data-agent-mcp 1.10.1 → 1.12.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 +4 -4
- package/skills/design-data/SKILL.md +17 -6
- package/src/cascade-bootstrap.js +89 -3
- package/src/tools/read.js +136 -32
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.12.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.1
|
|
34
|
-
"@adobe/design-data-wasm": "0.
|
|
35
|
-
"@adobe/spectrum-design-data": "3.2.
|
|
33
|
+
"@adobe/design-data": "3.2.1",
|
|
34
|
+
"@adobe/design-data-wasm": "0.10.0",
|
|
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.12.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,
|
|
@@ -67,9 +67,18 @@ all subsequent lookups. No inputs required.
|
|
|
67
67
|
|
|
68
68
|
### Resolve a token to its literal value — `resolve_token`
|
|
69
69
|
|
|
70
|
-
Required: `property` (string) — e.g. `"
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
Required: `property` (string) — the bare `name.property` segment, e.g. `"background-color"`
|
|
71
|
+
or `"corner-radius"` (see `primer().properties` for the full list). **Not** a flattened
|
|
72
|
+
legacyKey like `"accent-background-color-default"` — that form never resolves.
|
|
73
|
+
Optional context: `colorScheme` (`"light"` or `"dark"`), `scale` (`"desktop"` or `"mobile"`),
|
|
74
|
+
`contrast` (`"regular"` or `"high"`). Optional narrowing: `component`, `variant`, `state`, and
|
|
75
|
+
`colorRole`. Set `excludeDeprecated` to omit tokens with `lifecycle.deprecatedIn`.
|
|
76
|
+
|
|
77
|
+
> **Gotcha:** when several tokens share a property, use `variant`, `state`, or `colorRole` to
|
|
78
|
+
> narrow the match, then check `ambiguous` and `deprecated` on the result. `component` is
|
|
79
|
+
> sparsely populated in the current dataset, so prefer the other fields when available.
|
|
80
|
+
> `resolve_token` still uses cascade ranking when multiple candidates remain; use `query_tokens`
|
|
81
|
+
> to inspect the full candidate set.
|
|
73
82
|
|
|
74
83
|
### Query tokens by filter expression — `query_tokens`
|
|
75
84
|
|
|
@@ -83,12 +92,14 @@ Filter syntax examples:
|
|
|
83
92
|
```
|
|
84
93
|
property=background-color
|
|
85
94
|
property=*background*
|
|
86
|
-
component=button
|
|
87
|
-
component=button,state=hover
|
|
88
95
|
property=background-color|property=border-color
|
|
89
96
|
$schema=https://spectrum.adobe.com/page/design-token/
|
|
90
97
|
```
|
|
91
98
|
|
|
99
|
+
> **Gotcha:** `component=<id>` currently always returns `[]` — tokens aren't
|
|
100
|
+
> component-indexed in this dataset. Use `describe_component` to see a component's known
|
|
101
|
+
> token bindings instead.
|
|
102
|
+
|
|
92
103
|
> **Exit codes:** `0` = matches found; empty array = no matches (not an error).
|
|
93
104
|
|
|
94
105
|
***
|
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
|
{
|
|
@@ -114,16 +121,16 @@ export function createReadTools() {
|
|
|
114
121
|
async handler() {
|
|
115
122
|
// Shape note: this response intentionally diverges from the CLI PrimerData
|
|
116
123
|
// struct (sdk/core/src/primer.rs). The CLI emits modeSets as an array of
|
|
117
|
-
// {name,
|
|
118
|
-
// shape uses keyed objects (matching the sibling design-data-mcp),
|
|
119
|
-
// and the SKILL.md skill prompt consume by key name. Skill
|
|
120
|
-
// tokenCount, modeSets.{colorScheme,scale,contrast}, components[],
|
|
124
|
+
// {name, modes, defaultMode} objects and taxonomyFields as a flat array. This
|
|
125
|
+
// in-process shape uses keyed objects (matching the sibling design-data-mcp),
|
|
126
|
+
// which agents and the SKILL.md skill prompt consume by key name. Skill
|
|
127
|
+
// contract: tokenCount, modeSets.{colorScheme,scale,contrast}, components[],
|
|
121
128
|
// taxonomyFields.{indexed,advisory}. provenance is included for metrics:
|
|
122
129
|
// for the embedded dataset it carries designDataVersion (@adobe/spectrum-design-data
|
|
123
130
|
// version baked in at wasm build time); for custom datasets the source differs.
|
|
124
131
|
const wasm = await getWasm();
|
|
125
132
|
const ds = await getDataset();
|
|
126
|
-
const { provenance } = ds.primer();
|
|
133
|
+
const { provenance, modeSets: rawModeSets } = ds.primer();
|
|
127
134
|
// Best-effort staleness check (spectrum-design-data-9fe.5) — silent on
|
|
128
135
|
// failure, never blocks the primer response.
|
|
129
136
|
const datasetStatus = await checkDatasetFreshness(
|
|
@@ -135,11 +142,13 @@ export function createReadTools() {
|
|
|
135
142
|
// and consumers should prefer it going forward.
|
|
136
143
|
source: config.cascadeActive ? "cascade" : "embedded",
|
|
137
144
|
tokenCount: ds.tokenCount(),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
// Mode-set dimensions (colorScheme, scale, contrast) live in
|
|
146
|
+
// graph.mode_sets, not the field-catalog registry, so they're read from
|
|
147
|
+
// ds.primer()'s modeSets array rather than wasm.getFieldValues()
|
|
148
|
+
// (spectrum-design-data-v9bb).
|
|
149
|
+
modeSets: Object.fromEntries(
|
|
150
|
+
rawModeSets.map((ms) => [ms.name, ms.modes]),
|
|
151
|
+
),
|
|
143
152
|
taxonomyFields: {
|
|
144
153
|
indexed: wasm.getIndexedFields(),
|
|
145
154
|
advisory: wasm.getAdvisoryFields() ?? [],
|
|
@@ -156,7 +165,9 @@ export function createReadTools() {
|
|
|
156
165
|
{
|
|
157
166
|
name: "resolve_token",
|
|
158
167
|
description:
|
|
159
|
-
"Resolve a design token property to its final value for a given
|
|
168
|
+
"Resolve a design token property to its final value for a given context. " +
|
|
169
|
+
"Use component, variant, state, or colorRole to disambiguate shared properties; " +
|
|
170
|
+
"inspect ambiguous and deprecated in the result.",
|
|
160
171
|
inputSchema: {
|
|
161
172
|
type: "object",
|
|
162
173
|
required: ["property"],
|
|
@@ -164,7 +175,9 @@ export function createReadTools() {
|
|
|
164
175
|
property: {
|
|
165
176
|
type: "string",
|
|
166
177
|
description:
|
|
167
|
-
"
|
|
178
|
+
"Bare token property name (the name.property segment, e.g. " +
|
|
179
|
+
'"background-color", "corner-radius" — see primer().properties for ' +
|
|
180
|
+
"the full list). Not a flattened legacyKey.",
|
|
168
181
|
},
|
|
169
182
|
colorScheme: {
|
|
170
183
|
type: "string",
|
|
@@ -180,10 +193,46 @@ export function createReadTools() {
|
|
|
180
193
|
enum: ["regular", "high"],
|
|
181
194
|
description: "Contrast: regular or high",
|
|
182
195
|
},
|
|
196
|
+
component: {
|
|
197
|
+
type: "string",
|
|
198
|
+
description:
|
|
199
|
+
"Optional component name-object field. It is sparsely populated; " +
|
|
200
|
+
"prefer variant, state, or colorRole when available.",
|
|
201
|
+
},
|
|
202
|
+
variant: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description:
|
|
205
|
+
"Optional token variant used to narrow shared properties.",
|
|
206
|
+
},
|
|
207
|
+
state: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description:
|
|
210
|
+
"Optional token state used to narrow shared properties (including array-valued state).",
|
|
211
|
+
},
|
|
212
|
+
colorRole: {
|
|
213
|
+
type: "string",
|
|
214
|
+
description:
|
|
215
|
+
"Optional color role such as accent, negative, or positive.",
|
|
216
|
+
},
|
|
217
|
+
excludeDeprecated: {
|
|
218
|
+
type: "boolean",
|
|
219
|
+
description:
|
|
220
|
+
"Exclude tokens with lifecycle.deprecatedIn from candidate resolution.",
|
|
221
|
+
},
|
|
183
222
|
},
|
|
184
223
|
additionalProperties: false,
|
|
185
224
|
},
|
|
186
|
-
async handler({
|
|
225
|
+
async handler({
|
|
226
|
+
property,
|
|
227
|
+
colorScheme,
|
|
228
|
+
scale,
|
|
229
|
+
contrast,
|
|
230
|
+
component,
|
|
231
|
+
variant,
|
|
232
|
+
state,
|
|
233
|
+
colorRole,
|
|
234
|
+
excludeDeprecated,
|
|
235
|
+
}) {
|
|
187
236
|
const ds = await loadDataset(
|
|
188
237
|
config.cascadeActive ? config.cascadeDataPath : config.dataPath,
|
|
189
238
|
);
|
|
@@ -191,10 +240,19 @@ export function createReadTools() {
|
|
|
191
240
|
if (colorScheme) context.colorScheme = colorScheme;
|
|
192
241
|
if (scale) context.scale = scale;
|
|
193
242
|
if (contrast) context.contrast = contrast;
|
|
194
|
-
const result = ds.resolve(property, context
|
|
243
|
+
const result = ds.resolve(property, context, {
|
|
244
|
+
component,
|
|
245
|
+
variant,
|
|
246
|
+
state,
|
|
247
|
+
colorRole,
|
|
248
|
+
excludeDeprecated: excludeDeprecated ?? false,
|
|
249
|
+
});
|
|
195
250
|
if (!result) {
|
|
251
|
+
const deprecatedHint = excludeDeprecated
|
|
252
|
+
? " Only deprecated matches may exist; retry without excludeDeprecated to inspect them."
|
|
253
|
+
: "";
|
|
196
254
|
throw new Error(
|
|
197
|
-
`No token found for property "${property}" in context ${JSON.stringify(context)}`,
|
|
255
|
+
`No token found for property "${property}" in context ${JSON.stringify(context)}.${deprecatedHint}`,
|
|
198
256
|
);
|
|
199
257
|
}
|
|
200
258
|
return result;
|
|
@@ -211,7 +269,11 @@ export function createReadTools() {
|
|
|
211
269
|
properties: {
|
|
212
270
|
filter: {
|
|
213
271
|
type: "string",
|
|
214
|
-
description:
|
|
272
|
+
description:
|
|
273
|
+
'Filter expression, e.g. "property=background-color". Valid keys: ' +
|
|
274
|
+
"property, component, variant, state, colorScheme, scale, contrast, " +
|
|
275
|
+
'uuid, $schema. Note: "component" is not currently populated on ' +
|
|
276
|
+
"tokens — component=<id> always returns []; use describe_component instead.",
|
|
215
277
|
},
|
|
216
278
|
},
|
|
217
279
|
additionalProperties: false,
|
|
@@ -238,7 +300,10 @@ export function createReadTools() {
|
|
|
238
300
|
},
|
|
239
301
|
async handler({ id }) {
|
|
240
302
|
validateComponentId(id);
|
|
241
|
-
const componentsDir =
|
|
303
|
+
const componentsDir = getCascadeAwareDir(
|
|
304
|
+
config.componentsDir,
|
|
305
|
+
"components",
|
|
306
|
+
);
|
|
242
307
|
if (!componentsDir) {
|
|
243
308
|
throw new Error(
|
|
244
309
|
`@adobe/spectrum-design-data is not installed — cannot load component "${id}". ` +
|
|
@@ -268,7 +333,10 @@ export function createReadTools() {
|
|
|
268
333
|
// of the component file into relationships/<id>.json — merge them back
|
|
269
334
|
// in so callers relying on this tool for a component's token bindings
|
|
270
335
|
// still see them (see spectrum-design-data-x29.4).
|
|
271
|
-
const relationshipsDir =
|
|
336
|
+
const relationshipsDir = getCascadeAwareDir(
|
|
337
|
+
config.relationshipsDir,
|
|
338
|
+
"relationships",
|
|
339
|
+
);
|
|
272
340
|
const relationshipFile = relationshipsDir
|
|
273
341
|
? join(relationshipsDir, `${id}.json`)
|
|
274
342
|
: null;
|
|
@@ -302,7 +370,10 @@ export function createReadTools() {
|
|
|
302
370
|
},
|
|
303
371
|
async handler({ id }) {
|
|
304
372
|
validateGuidelineId(id);
|
|
305
|
-
const guidelinesDir =
|
|
373
|
+
const guidelinesDir = getCascadeAwareDir(
|
|
374
|
+
config.guidelinesDir,
|
|
375
|
+
"guidelines",
|
|
376
|
+
);
|
|
306
377
|
if (!guidelinesDir) {
|
|
307
378
|
throw new Error(
|
|
308
379
|
`@adobe/spectrum-design-data is not installed — cannot load guideline "${id}". ` +
|
|
@@ -315,9 +386,8 @@ export function createReadTools() {
|
|
|
315
386
|
if (err.message.startsWith("Not found:")) {
|
|
316
387
|
let available;
|
|
317
388
|
try {
|
|
318
|
-
available =
|
|
319
|
-
.
|
|
320
|
-
.map((f) => f.replace(/\.json$/, ""))
|
|
389
|
+
available = listGuidelines(guidelinesDir)
|
|
390
|
+
.map((guideline) => guideline.slug)
|
|
321
391
|
.sort()
|
|
322
392
|
.join(", ");
|
|
323
393
|
} catch {
|
|
@@ -332,5 +402,39 @@ export function createReadTools() {
|
|
|
332
402
|
}
|
|
333
403
|
},
|
|
334
404
|
},
|
|
405
|
+
|
|
406
|
+
{
|
|
407
|
+
name: "list_guidelines",
|
|
408
|
+
description:
|
|
409
|
+
"List available Spectrum design guidelines and their metadata. Optionally " +
|
|
410
|
+
"filter the catalog by category before calling describe_guideline.",
|
|
411
|
+
inputSchema: {
|
|
412
|
+
type: "object",
|
|
413
|
+
properties: {
|
|
414
|
+
category: {
|
|
415
|
+
type: "string",
|
|
416
|
+
description:
|
|
417
|
+
"Optional guideline category, e.g. designing or implementing",
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
additionalProperties: false,
|
|
421
|
+
},
|
|
422
|
+
async handler({ category } = {}) {
|
|
423
|
+
const guidelinesDir = getCascadeAwareDir(
|
|
424
|
+
config.guidelinesDir,
|
|
425
|
+
"guidelines",
|
|
426
|
+
);
|
|
427
|
+
if (!guidelinesDir) {
|
|
428
|
+
throw new Error(
|
|
429
|
+
`@adobe/spectrum-design-data is not installed — cannot list guidelines. ` +
|
|
430
|
+
`Install it with: pnpm add @adobe/spectrum-design-data`,
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
const guidelines = listGuidelines(guidelinesDir);
|
|
434
|
+
return category
|
|
435
|
+
? guidelines.filter((guideline) => guideline.category === category)
|
|
436
|
+
: guidelines;
|
|
437
|
+
},
|
|
438
|
+
},
|
|
335
439
|
];
|
|
336
440
|
}
|