@carto/meridian-ds 3.0.2 → 3.0.3-alpha.c232590.252
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/CHANGELOG.md +2 -0
- package/README.md +34 -0
- package/dist/bin/meridian-ds-mcp.js +269 -0
- package/dist/metadata.json +723 -0
- package/dist/types/components/Autocomplete/Autocomplete/Autocomplete.d.ts +1 -0
- package/dist/types/components/Autocomplete/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/types/components/Autocomplete/Autocomplete/Autocomplete.metadata.d.ts +26 -0
- package/dist/types/components/Autocomplete/Autocomplete/Autocomplete.metadata.d.ts.map +1 -0
- package/dist/types/components/Autocomplete/Autocomplete/Autocomplete.stories.d.ts.map +1 -1
- package/dist/types/components/Autocomplete/types.d.ts +2 -1
- package/dist/types/components/Autocomplete/types.d.ts.map +1 -1
- package/dist/types/components/Button/Button.d.ts +8 -0
- package/dist/types/components/Button/Button.d.ts.map +1 -1
- package/dist/types/components/Button/Button.metadata.d.ts +26 -0
- package/dist/types/components/Button/Button.metadata.d.ts.map +1 -0
- package/dist/types/components/Dialog/Dialog/Dialog.d.ts +1 -0
- package/dist/types/components/Dialog/Dialog/Dialog.d.ts.map +1 -1
- package/dist/types/components/Dialog/Dialog/Dialog.metadata.d.ts +26 -0
- package/dist/types/components/Dialog/Dialog/Dialog.metadata.d.ts.map +1 -0
- package/dist/types/components/Dialog/types.d.ts +3 -8
- package/dist/types/components/Dialog/types.d.ts.map +1 -1
- package/dist/types/components/IconButton/IconButton.d.ts +39 -4
- package/dist/types/components/IconButton/IconButton.d.ts.map +1 -1
- package/dist/types/components/IconButton/IconButton.metadata.d.ts +15 -0
- package/dist/types/components/IconButton/IconButton.metadata.d.ts.map +1 -0
- package/dist/types/components/IconButton/IconButton.stories.d.ts +13 -0
- package/dist/types/components/IconButton/IconButton.stories.d.ts.map +1 -1
- package/dist/types/components/SelectField/SelectField/SelectField.d.ts +1 -0
- package/dist/types/components/SelectField/SelectField/SelectField.d.ts.map +1 -1
- package/dist/types/components/SelectField/SelectField/SelectField.metadata.d.ts +26 -0
- package/dist/types/components/SelectField/SelectField/SelectField.metadata.d.ts.map +1 -0
- package/dist/types/components/SelectField/SelectField/SelectField.stories.d.ts.map +1 -1
- package/dist/types/components/SelectField/types.d.ts +6 -0
- package/dist/types/components/SelectField/types.d.ts.map +1 -1
- package/dist/types/components/SplitButton/SplitButton.d.ts +27 -0
- package/dist/types/components/SplitButton/SplitButton.d.ts.map +1 -1
- package/dist/types/components/SplitButton/SplitButton.metadata.d.ts +15 -0
- package/dist/types/components/SplitButton/SplitButton.metadata.d.ts.map +1 -0
- package/dist/types/components/Tag/Tag.d.ts +2 -1
- package/dist/types/components/Tag/Tag.d.ts.map +1 -1
- package/dist/types/components/Tag/Tag.metadata.d.ts +18 -0
- package/dist/types/components/Tag/Tag.metadata.d.ts.map +1 -0
- package/dist/types/components/Typography/Typography.d.ts +3 -0
- package/dist/types/components/Typography/Typography.d.ts.map +1 -1
- package/dist/types/components/Typography/Typography.metadata.d.ts +26 -0
- package/dist/types/components/Typography/Typography.metadata.d.ts.map +1 -0
- package/package.json +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- feat(ai-metadata): expose component knowledge to AI agents via the new `meridian-ds-mcp` MCP server — a build-time `dist/metadata.json` bundle (curation, conventions, example code) authored per component via `<Name>.metadata.ts` + JSDoc, validated in CI [#443](https://github.com/CartoDB/meridian-ds/pull/443)
|
|
6
|
+
|
|
5
7
|
## 3.0
|
|
6
8
|
|
|
7
9
|
### 3.0.2
|
package/README.md
CHANGED
|
@@ -42,3 +42,37 @@ Meridian ships CARTO's custom icon set as React components under `@carto/meridia
|
|
|
42
42
|
## Figma integration
|
|
43
43
|
|
|
44
44
|
Meridian publishes `Figma Code Connect` templates for all components, so **AI coding agents using the Figma MCP server can read designs and generate Meridian-conformant code**: the right component, the right import path, only real prop values, and the encoded semantics of Meridian primitives. The same templates surface the matching snippet to developers inspecting an instance in Figma's Dev Mode. See setup, MCP install, and template-authoring details in the [Figma Code Connect documentation](https://meridian.carto.com/?path=/docs/figma-code-connect--docs).
|
|
45
|
+
|
|
46
|
+
## AI readiness
|
|
47
|
+
|
|
48
|
+
Meridian ships structured metadata so **AI coding agents in apps that depend on it generate correct Meridian code** — the right component, real prop values, and Meridian conventions instead of bare MUI. Knowledge splits two ways: exact props and types come from the package's TypeScript declarations (`.d.ts`, which the consumer's IDE/`tsc` already reads), and everything types can't express — which component to use and when, gotchas, the high-value MUI props, and real examples — comes from a metadata bundle served over MCP.
|
|
49
|
+
|
|
50
|
+
### Meridian MCP
|
|
51
|
+
|
|
52
|
+
The package ships an MCP server as the `meridian-ds-mcp` bin. In a repo that has `@carto/meridian-ds` installed, register it with the Claude Code CLI:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
claude mcp add --scope project meridian-design-system -- npx meridian-ds-mcp
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`--scope project` writes `.mcp.json` at the repo root — Claude Code loads it whether the team commits it (to share the server) or gitignores it:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"meridian-design-system": {
|
|
64
|
+
"command": "npx",
|
|
65
|
+
"args": ["meridian-ds-mcp"]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
It's a stdio server (a local command, not a hosted URL), so it needs no network — the bin reads the metadata bundle that ships inside the installed `@carto/meridian-ds`, so the agent's knowledge always matches the installed version.
|
|
72
|
+
|
|
73
|
+
It exposes four tools:
|
|
74
|
+
|
|
75
|
+
- `meridian_list_components` — discover components by description + keywords.
|
|
76
|
+
- `meridian_get_component` — when to use it, alternatives, and the high-value inherited MUI props.
|
|
77
|
+
- `meridian_get_examples` — real, copy-able example code.
|
|
78
|
+
- `meridian_get_conventions` — cross-cutting usage rules.
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ai-metadata/mcp/server.ts
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { createRequire } from "module";
|
|
8
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
function resolveBundlePath() {
|
|
10
|
+
if (process.env.MERIDIAN_DS_METADATA) return process.env.MERIDIAN_DS_METADATA;
|
|
11
|
+
try {
|
|
12
|
+
return createRequire(import.meta.url).resolve("@carto/meridian-ds/metadata");
|
|
13
|
+
} catch {
|
|
14
|
+
let dir = __dirname;
|
|
15
|
+
for (let i = 0; i < 6; i++) {
|
|
16
|
+
const candidate = path.join(dir, "dist", "metadata.json");
|
|
17
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
18
|
+
dir = path.dirname(dir);
|
|
19
|
+
}
|
|
20
|
+
return path.resolve(__dirname, "..", "..", "dist", "metadata.json");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
var DOCS = [];
|
|
24
|
+
var CONVENTIONS = "";
|
|
25
|
+
var VERSION = "0.0.0";
|
|
26
|
+
var TOOLS = [
|
|
27
|
+
{
|
|
28
|
+
name: "meridian_list_components",
|
|
29
|
+
description: "List all Meridian components with their description, keywords, and example count. Start here: the catalog is small, so read it and pick the component whose description/keywords match your need.",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {},
|
|
33
|
+
additionalProperties: false
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "meridian_get_component",
|
|
38
|
+
description: "Curation for one component: when to use it, its decisionTree (when to reach for a different component), any limitations (capabilities it lacks vs. similar components \u2014 check before downgrading from one), and the high-value inherited MUI props (curation.mui, some with an aiHint). Own props, types, and JSDoc are NOT here \u2014 read the shipped .d.ts (the propTypes field gives the exact command).",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
name: { type: "string", description: 'Component name, e.g. "Button"' }
|
|
43
|
+
},
|
|
44
|
+
required: ["name"],
|
|
45
|
+
additionalProperties: false
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "meridian_get_examples",
|
|
50
|
+
description: "Real, copy-able example code (per Storybook story, with imports), shipped in the package. With no storyId: the first few stories in full plus an 'Other Stories' list (name + id) of the rest. Pass a storyId (from that list or meridian_get_component) to get just that story's full code.",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
name: { type: "string", description: 'Component name, e.g. "Button"' },
|
|
55
|
+
storyId: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "Optional. A story id (or name) to fetch just that one example \u2014 use for components whose full set is too large to return at once."
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
required: ["name"],
|
|
61
|
+
additionalProperties: false
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "meridian_get_conventions",
|
|
66
|
+
description: "Mandatory system-wide Meridian rules to apply whenever generating or editing UI (component selection, a11y, i18n, styling/theme tokens, props, examples). Call this once per session BEFORE writing code and follow it \u2014 these rules are not repeated in the per-component data.",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {},
|
|
70
|
+
additionalProperties: false
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
var find = (name) => DOCS.find((d) => d.name.toLowerCase() === String(name).toLowerCase());
|
|
75
|
+
var notFound = (name) => ({
|
|
76
|
+
error: `Unknown component "${name}". Available: ${DOCS.map((d) => d.name).join(", ")}`
|
|
77
|
+
});
|
|
78
|
+
function listComponents() {
|
|
79
|
+
return DOCS.map((d) => ({
|
|
80
|
+
name: d.name,
|
|
81
|
+
description: d.description,
|
|
82
|
+
keywords: d.curation.keywords ?? [],
|
|
83
|
+
examples: d.examples.length
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
function getComponent(name) {
|
|
87
|
+
const doc = find(name);
|
|
88
|
+
if (!doc) return notFound(name);
|
|
89
|
+
return {
|
|
90
|
+
name: doc.name,
|
|
91
|
+
description: doc.description,
|
|
92
|
+
reminder: "Apply the session conventions when using this \u2014 call meridian_get_conventions first if you have not this session.",
|
|
93
|
+
curation: doc.curation,
|
|
94
|
+
// Own props/types/JSDoc aren't in the bundle by design; point a headless
|
|
95
|
+
// agent at the shipped .d.ts (no IDE/hover assumed). Promoted inherited MUI
|
|
96
|
+
// props, when curated, are already in `curation.mui` above.
|
|
97
|
+
propTypes: `Own props, exact types, and JSDoc are not in this payload \u2014 they ship as TypeScript declarations. Read them from the installed package: \`find node_modules/@carto/meridian-ds/dist/types -name '${doc.name}.d.ts'\`. Import the component from '@carto/meridian-ds/components'.`,
|
|
98
|
+
examples: {
|
|
99
|
+
note: "Call meridian_get_examples(name) for the first stories in full plus an index of the rest; pass a storyId for any specific one.",
|
|
100
|
+
storyIds: doc.examples.map((e) => e.id)
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
var EXAMPLES_FULL = 5;
|
|
105
|
+
function titleize(name) {
|
|
106
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").trim();
|
|
107
|
+
}
|
|
108
|
+
function getComponentExamples(name, storyId) {
|
|
109
|
+
const doc = find(name);
|
|
110
|
+
if (!doc) return notFound(name);
|
|
111
|
+
if (storyId) {
|
|
112
|
+
const ex = doc.examples.find((e) => e.id === storyId || e.name === storyId);
|
|
113
|
+
if (!ex)
|
|
114
|
+
return {
|
|
115
|
+
error: `Unknown story "${storyId}" for ${doc.name}. Available: ${doc.examples.map((e) => e.id).join(", ")}`
|
|
116
|
+
};
|
|
117
|
+
return `# ${doc.name} - ${titleize(ex.name)}
|
|
118
|
+
|
|
119
|
+
${ex.code}`;
|
|
120
|
+
}
|
|
121
|
+
if (!doc.examples.length) return `# ${doc.name}
|
|
122
|
+
|
|
123
|
+
No examples available.`;
|
|
124
|
+
const componentId = doc.examples[0].id.split("--")[0];
|
|
125
|
+
const shown = doc.examples.slice(0, EXAMPLES_FULL);
|
|
126
|
+
const rest = doc.examples.slice(EXAMPLES_FULL);
|
|
127
|
+
let md = `# ${doc.name}
|
|
128
|
+
|
|
129
|
+
ID: ${componentId}
|
|
130
|
+
|
|
131
|
+
## Stories
|
|
132
|
+
`;
|
|
133
|
+
for (const e of shown)
|
|
134
|
+
md += `
|
|
135
|
+
### ${titleize(e.name)}
|
|
136
|
+
|
|
137
|
+
Story ID: ${e.id}
|
|
138
|
+
|
|
139
|
+
${e.code}
|
|
140
|
+
`;
|
|
141
|
+
if (rest.length) {
|
|
142
|
+
md += `
|
|
143
|
+
### Other Stories
|
|
144
|
+
|
|
145
|
+
Fetch any with meridian_get_examples(storyId):
|
|
146
|
+
|
|
147
|
+
`;
|
|
148
|
+
md += rest.map((e) => `- ${titleize(e.name)} (${e.id})`).join("\n") + "\n";
|
|
149
|
+
}
|
|
150
|
+
return md;
|
|
151
|
+
}
|
|
152
|
+
function callTool(name, args) {
|
|
153
|
+
switch (name) {
|
|
154
|
+
case "meridian_list_components":
|
|
155
|
+
return listComponents();
|
|
156
|
+
case "meridian_get_component":
|
|
157
|
+
return getComponent(String(args.name ?? ""));
|
|
158
|
+
case "meridian_get_examples":
|
|
159
|
+
return getComponentExamples(
|
|
160
|
+
String(args.name ?? ""),
|
|
161
|
+
args.storyId ? String(args.storyId) : void 0
|
|
162
|
+
);
|
|
163
|
+
case "meridian_get_conventions":
|
|
164
|
+
return { conventions: CONVENTIONS };
|
|
165
|
+
default:
|
|
166
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
var SERVER_INSTRUCTIONS = `Meridian Design System knowledge for writing React UI in an app that depends on @carto/meridian-ds.
|
|
170
|
+
|
|
171
|
+
REQUIRED FIRST STEP: before writing or editing ANY Meridian UI, call meridian_get_conventions once and follow it for the rest of the session. These are mandatory cross-cutting rules (component selection, a11y, i18n, styling/theme tokens, props, examples) that the per-component data does NOT repeat \u2014 skipping them produces off-spec code.
|
|
172
|
+
|
|
173
|
+
Flow:
|
|
174
|
+
1. meridian_get_conventions \u2014 read first; apply throughout the session.
|
|
175
|
+
2. meridian_list_components \u2014 read the (small) catalog and pick by description + keywords. There is no search tool; match by meaning (the model already knows "picker" \u2248 "dropdown" \u2248 "select").
|
|
176
|
+
3. meridian_get_component(name) \u2014 when to use it, its decisionTree (conditions that point to a different component), and the high-value inherited MUI props worth knowing (curation.mui; some carry an aiHint). It also returns the example story ids.
|
|
177
|
+
4. meridian_get_examples(name[, storyId]) \u2014 real, copy-able example code. With no storyId: the first few stories in full plus a list of the rest; pass a storyId for one story's full code.
|
|
178
|
+
|
|
179
|
+
Exact own props, types, and JSDoc are NOT served here \u2014 they ship as TypeScript declarations in the installed package. Read them with: find node_modules/@carto/meridian-ds/dist/types -name '<Component>.d.ts'. Import the component from '@carto/meridian-ds/components'. Prefer a Meridian component over a bare MUI one whenever one exists.`;
|
|
180
|
+
function send(msg) {
|
|
181
|
+
process.stdout.write(JSON.stringify(msg) + "\n");
|
|
182
|
+
}
|
|
183
|
+
function handle(req) {
|
|
184
|
+
const { id, method, params } = req;
|
|
185
|
+
const reply = (result) => send({ jsonrpc: "2.0", id, result });
|
|
186
|
+
switch (method) {
|
|
187
|
+
case "initialize":
|
|
188
|
+
return reply({
|
|
189
|
+
protocolVersion: "2024-11-05",
|
|
190
|
+
capabilities: { tools: {} },
|
|
191
|
+
serverInfo: { name: "meridian-design-system", version: VERSION },
|
|
192
|
+
instructions: SERVER_INSTRUCTIONS
|
|
193
|
+
});
|
|
194
|
+
case "notifications/initialized":
|
|
195
|
+
return;
|
|
196
|
+
// notification, no response
|
|
197
|
+
case "ping":
|
|
198
|
+
return reply({});
|
|
199
|
+
case "tools/list":
|
|
200
|
+
return reply({ tools: TOOLS });
|
|
201
|
+
case "tools/call":
|
|
202
|
+
try {
|
|
203
|
+
const result = callTool(params?.name, params?.arguments ?? {});
|
|
204
|
+
const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
205
|
+
return reply({ content: [{ type: "text", text }] });
|
|
206
|
+
} catch (err) {
|
|
207
|
+
return reply({
|
|
208
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
209
|
+
isError: true
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
default:
|
|
213
|
+
if (id !== void 0) {
|
|
214
|
+
send({
|
|
215
|
+
jsonrpc: "2.0",
|
|
216
|
+
id,
|
|
217
|
+
error: { code: -32601, message: `Method not found: ${method}` }
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function load() {
|
|
223
|
+
const bundlePath = resolveBundlePath();
|
|
224
|
+
if (!fs.existsSync(bundlePath)) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`metadata bundle not found at ${bundlePath}. In the Meridian repo run \`yarn metadata:build\`; in a consumer it ships with @carto/meridian-ds.`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const bundle = JSON.parse(
|
|
230
|
+
fs.readFileSync(bundlePath, "utf8")
|
|
231
|
+
);
|
|
232
|
+
DOCS = bundle.components;
|
|
233
|
+
CONVENTIONS = bundle.conventions;
|
|
234
|
+
VERSION = bundle.version;
|
|
235
|
+
process.stderr.write(
|
|
236
|
+
`[meridian-design-system] v${VERSION} ready: ${DOCS.map((d) => d.name).join(", ")}
|
|
237
|
+
`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
function main() {
|
|
241
|
+
load();
|
|
242
|
+
let buf = "";
|
|
243
|
+
process.stdin.setEncoding("utf8");
|
|
244
|
+
process.stdin.on("data", (chunk) => {
|
|
245
|
+
buf += chunk;
|
|
246
|
+
let nl;
|
|
247
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
248
|
+
const line = buf.slice(0, nl).trim();
|
|
249
|
+
buf = buf.slice(nl + 1);
|
|
250
|
+
if (!line) continue;
|
|
251
|
+
try {
|
|
252
|
+
handle(JSON.parse(line));
|
|
253
|
+
} catch (err) {
|
|
254
|
+
process.stderr.write(
|
|
255
|
+
`[meridian-design-system] bad message: ${err.message}
|
|
256
|
+
`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
process.stdin.on("end", () => process.exit(0));
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
main();
|
|
265
|
+
} catch (err) {
|
|
266
|
+
process.stderr.write(`[meridian-design-system] fatal: ${err}
|
|
267
|
+
`);
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|