@aicanvas/mcp 0.1.1 → 0.2.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 +18 -8
- package/dist/index.js +265 -18
- package/package.json +18 -4
package/README.md
CHANGED
|
@@ -13,18 +13,23 @@ AI: [edits the file directly]
|
|
|
13
13
|
|
|
14
14
|
## What it exposes
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Eight tools, derived from the live [aicanvas.me](https://aicanvas.me) registry. Adding a component to the website auto-updates the MCP within minutes. The registry holds 75 standalone components, plus the Andromeda design system with its own components and templates.
|
|
17
17
|
|
|
18
18
|
| Tool | Purpose |
|
|
19
19
|
|---|---|
|
|
20
20
|
| `list_categories` | Show every category with component counts. Orient before drilling in. |
|
|
21
|
-
| `list_components` | Browse components, optionally filtered by category. Paginated. |
|
|
22
|
-
| `search_components` | Fuzzy keyword search across slugs, names, descriptions, categories, tags. Ranked results. |
|
|
23
|
-
| `get_component` | Full metadata
|
|
24
|
-
| `get_install_command` | Get the `npx shadcn add @aicanvas/<slug>` command for
|
|
21
|
+
| `list_components` | Browse standalone components, optionally filtered by category. Paginated. |
|
|
22
|
+
| `search_components` | Fuzzy keyword search across standalone AND design-system components (slugs, names, descriptions, categories, tags). Ranked results. |
|
|
23
|
+
| `get_component` | Full metadata plus complete `.tsx` source code for one component. Resolves both standalone slugs and design-system component slugs (e.g. `andromeda-heat-grid`). |
|
|
24
|
+
| `get_install_command` | Get the `npx shadcn add @aicanvas/<slug>` command for any standalone or design-system component. |
|
|
25
|
+
| `list_systems` | List the design systems available (e.g. Andromeda), with component and template counts. |
|
|
26
|
+
| `get_system` | Every file for a whole design system, plus its shared tokens, its component install commands, and the dependency chain. |
|
|
27
|
+
| `get_template` | Every file for a single template (a ready-made composition), with the registry dependencies it pulls in. |
|
|
25
28
|
|
|
26
29
|
All tools are read-only. Nothing is mutated on AI Canvas's side.
|
|
27
30
|
|
|
31
|
+
Standalone components and design-system components are kept in separate buckets. `list_components` and `list_categories` cover the 75 standalones only, so the catalog stays clean. Design-system components are reached through `list_systems` / `get_system`, and are also resolvable by slug through `get_component`, `get_install_command`, and `search_components`.
|
|
32
|
+
|
|
28
33
|
## Setup
|
|
29
34
|
|
|
30
35
|
### Claude Desktop
|
|
@@ -100,7 +105,7 @@ Inside any of the AI editors above, ask:
|
|
|
100
105
|
|
|
101
106
|
> "What categories of components does AI Canvas have?"
|
|
102
107
|
|
|
103
|
-
You should see all 11 categories with counts. If you don't, check the host's MCP logs
|
|
108
|
+
You should see all 11 categories with counts. If you don't, check the host's MCP logs. Typically a "Failed to spawn process" or network error means npx couldn't fetch the package, or the host wasn't restarted after editing the config.
|
|
104
109
|
|
|
105
110
|
To inspect the server outside an editor:
|
|
106
111
|
|
|
@@ -115,10 +120,15 @@ This opens a browser UI where you can call each tool by hand.
|
|
|
115
120
|
| Env var | Default | What it does |
|
|
116
121
|
|---|---|---|
|
|
117
122
|
| `AICANVAS_REGISTRY_BASE` | `https://aicanvas.me/r` | Registry root URL. Override only for local development against a self-hosted mirror. |
|
|
123
|
+
| `AICANVAS_TOKEN` | _(none)_ | Optional per-account token. The website bakes it into the MCP config it gives you. It identifies your AI Canvas account so source pulls are authorized and any premium content you own unlocks. Sent as a `Bearer` token on every registry request. Without it you are anonymous: metadata browsing still works, but pulling a component's source returns a short "create a free account" notice instead of the code. |
|
|
124
|
+
|
|
125
|
+
### Accounts and premium content
|
|
126
|
+
|
|
127
|
+
Browsing metadata (`list_categories`, `list_components`, `search_components`, `list_systems`, `get_install_command`) is free and needs no account. Pulling actual source (`get_component`, `get_system`, `get_template`) runs against a free AI Canvas account: set `AICANVAS_TOKEN` from your account and the website gives you the ready-made config. Without a token, a free-component source pull returns a short "create a free account" notice instead of the code, and premium content (full design systems and templates) returns HTTP 402 with an upgrade link at [aicanvas.me/pricing](https://aicanvas.me/pricing). Update anytime with `npx @aicanvas/mcp@latest`.
|
|
118
128
|
|
|
119
129
|
## How the registry stays fresh
|
|
120
130
|
|
|
121
|
-
The MCP fetches `https://aicanvas.me/r/aicanvas-mcp.json` on first use and caches it for 5 minutes. New components on AI Canvas appear in the MCP automatically
|
|
131
|
+
The MCP fetches `https://aicanvas.me/r/aicanvas-mcp.json` on first use and caches it for 5 minutes. New components on AI Canvas appear in the MCP automatically, no package update required.
|
|
122
132
|
|
|
123
133
|
The full source code for each component is fetched on demand from `/r/<slug>.json` (the same shadcn registry items the CLI installs).
|
|
124
134
|
|
|
@@ -141,4 +151,4 @@ Edit `src/index.ts`, run `npm run build`, restart your AI editor.
|
|
|
141
151
|
|
|
142
152
|
## License
|
|
143
153
|
|
|
144
|
-
MIT
|
|
154
|
+
MIT, same as the AI Canvas project.
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* inspect, and install components without leaving the chat.
|
|
8
8
|
*
|
|
9
9
|
* Data flow:
|
|
10
|
-
* - aicanvas.me/r/aicanvas-mcp.json →
|
|
10
|
+
* - aicanvas.me/r/aicanvas-mcp.json → metadata for the 75 standalone
|
|
11
|
+
* components plus the Andromeda design system, its components, and templates
|
|
11
12
|
* - aicanvas.me/r/<slug>.json → full source code per component
|
|
12
13
|
*
|
|
13
14
|
* Both are static files, served from Vercel's CDN. Stateless server.
|
|
@@ -19,17 +20,27 @@ import { z } from 'zod';
|
|
|
19
20
|
const REGISTRY_BASE = process.env.AICANVAS_REGISTRY_BASE ?? 'https://aicanvas.me/r';
|
|
20
21
|
const META_URL = `${REGISTRY_BASE}/aicanvas-mcp.json`;
|
|
21
22
|
const META_TTL_MS = 5 * 60 * 1000; // 5 minutes — meta updates with deploys
|
|
22
|
-
const MCP_VERSION = '0.
|
|
23
|
+
const MCP_VERSION = '0.2.1';
|
|
23
24
|
const USER_AGENT = `aicanvas-mcp/${MCP_VERSION}`;
|
|
25
|
+
// Optional per-user token (the website bakes it into the copied MCP config).
|
|
26
|
+
// Identifies the account so free-component source pulls are authorized and any
|
|
27
|
+
// premium content you own unlocks. Absent = anonymous: metadata browsing still
|
|
28
|
+
// works, but source pulls of free components need a free account.
|
|
29
|
+
const USER_TOKEN = process.env.AICANVAS_TOKEN ?? '';
|
|
30
|
+
function registryHeaders() {
|
|
31
|
+
return {
|
|
32
|
+
Accept: 'application/json',
|
|
33
|
+
'User-Agent': USER_AGENT,
|
|
34
|
+
...(USER_TOKEN ? { Authorization: `Bearer ${USER_TOKEN}` } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
24
37
|
// ── Cached fetchers ──────────────────────────────────────────────────────────
|
|
25
38
|
let metaCache = null;
|
|
26
39
|
async function fetchMeta() {
|
|
27
40
|
if (metaCache && Date.now() - metaCache.fetchedAt < META_TTL_MS) {
|
|
28
41
|
return metaCache.data;
|
|
29
42
|
}
|
|
30
|
-
const res = await fetch(META_URL, {
|
|
31
|
-
headers: { Accept: 'application/json', 'User-Agent': USER_AGENT },
|
|
32
|
-
});
|
|
43
|
+
const res = await fetch(META_URL, { headers: registryHeaders() });
|
|
33
44
|
if (!res.ok) {
|
|
34
45
|
throw new Error(`Failed to fetch AI Canvas registry meta from ${META_URL}: ${res.status} ${res.statusText}`);
|
|
35
46
|
}
|
|
@@ -39,9 +50,34 @@ async function fetchMeta() {
|
|
|
39
50
|
}
|
|
40
51
|
async function fetchComponentSource(slug) {
|
|
41
52
|
const url = `${REGISTRY_BASE}/${encodeURIComponent(slug)}.json`;
|
|
42
|
-
const res = await fetch(url, {
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
const res = await fetch(url, { headers: registryHeaders() });
|
|
54
|
+
if (res.status === 402) {
|
|
55
|
+
const j = (await res.json().catch(() => ({})));
|
|
56
|
+
throw new Error(j.message ??
|
|
57
|
+
'This is premium AI Canvas content. Upgrade at https://aicanvas.me/pricing, ' +
|
|
58
|
+
'then use the AI Canvas MCP config from your account (your token is included) ' +
|
|
59
|
+
'and update with: npx @aicanvas/mcp@latest');
|
|
60
|
+
}
|
|
61
|
+
// Free component pulled without an account: the registry returns a 200
|
|
62
|
+
// placeholder (not a 402), tagged with this header. Surface a warm sign-up
|
|
63
|
+
// CTA instead of handing the placeholder back as if it were real source.
|
|
64
|
+
if (res.headers.get('x-aicanvas-content-type') === 'free-account-required') {
|
|
65
|
+
const j = (await res.json().catch(() => ({})));
|
|
66
|
+
const name = typeof j.title === 'string'
|
|
67
|
+
? j.title.replace(/\s*\(free account required\)\s*$/i, '')
|
|
68
|
+
: slug;
|
|
69
|
+
throw new Error(`Almost there! "${name}" is free with an AI Canvas account (free, unlimited installs). ` +
|
|
70
|
+
`Sign up at https://aicanvas.me/account/sign-up, then use the AI Canvas MCP config from ` +
|
|
71
|
+
`your account (your token is included) and ask again.`);
|
|
72
|
+
}
|
|
73
|
+
// NOTE: premium standalones ALSO return a 200 placeholder (header
|
|
74
|
+
// 'premium-standalone'), but that header value is NOT unique to the
|
|
75
|
+
// placeholder — an entitled subscriber's REAL source carries it too (the
|
|
76
|
+
// success path stamps the contentType). Keying the MCP on it would strip a
|
|
77
|
+
// paying subscriber's real component. So premium is deliberately NOT
|
|
78
|
+
// intercepted here. Closing that footgun safely needs premiumStub to emit a
|
|
79
|
+
// distinct header (like freeAccountStub's 'free-account-required'); tracked
|
|
80
|
+
// as a separate, billing-reviewed follow-up.
|
|
45
81
|
if (!res.ok) {
|
|
46
82
|
throw new Error(`Failed to fetch source for "${slug}" from ${url}: ${res.status} ${res.statusText}`);
|
|
47
83
|
}
|
|
@@ -88,6 +124,37 @@ function scoreMatch(query, c) {
|
|
|
88
124
|
function escapeRegExp(s) {
|
|
89
125
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
90
126
|
}
|
|
127
|
+
// Minimal searchable surface for design-system components — slug, name,
|
|
128
|
+
// description, and the system name. Mirrors scoreMatch's token logic so DS
|
|
129
|
+
// components rank alongside standalones in search results.
|
|
130
|
+
function scoreSystemComponent(query, c) {
|
|
131
|
+
const q = normalize(query);
|
|
132
|
+
if (!q)
|
|
133
|
+
return 0;
|
|
134
|
+
const tokens = q.split(/\s+/).filter((t) => t.length > 2);
|
|
135
|
+
if (tokens.length === 0)
|
|
136
|
+
return 0;
|
|
137
|
+
const fields = [
|
|
138
|
+
{ text: normalize(c.slug), weight: 5 },
|
|
139
|
+
{ text: normalize(c.name), weight: 4 },
|
|
140
|
+
{ text: normalize(c.description), weight: 1 },
|
|
141
|
+
{ text: normalize(c.system), weight: 2 },
|
|
142
|
+
];
|
|
143
|
+
let score = 0;
|
|
144
|
+
for (const tok of tokens) {
|
|
145
|
+
for (const f of fields) {
|
|
146
|
+
if (!f.text)
|
|
147
|
+
continue;
|
|
148
|
+
if (new RegExp(`\\b${escapeRegExp(tok)}\\b`).test(f.text)) {
|
|
149
|
+
score += f.weight * 2;
|
|
150
|
+
}
|
|
151
|
+
else if (f.text.includes(tok)) {
|
|
152
|
+
score += f.weight;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return score;
|
|
157
|
+
}
|
|
91
158
|
function asTextContent(value) {
|
|
92
159
|
return {
|
|
93
160
|
type: 'text',
|
|
@@ -112,7 +179,7 @@ server.registerTool('list_categories', {
|
|
|
112
179
|
try {
|
|
113
180
|
const meta = await fetchMeta();
|
|
114
181
|
const lines = [
|
|
115
|
-
`AI Canvas
|
|
182
|
+
`AI Canvas: ${meta.componentCount} components across ${meta.categories.length} categories.`,
|
|
116
183
|
'',
|
|
117
184
|
...meta.categories.map((c) => ` ${c.label.padEnd(24)} ${String(c.count).padStart(3)} components`),
|
|
118
185
|
];
|
|
@@ -207,14 +274,25 @@ server.registerTool('search_components', {
|
|
|
207
274
|
}, async ({ query, limit = 10 }) => {
|
|
208
275
|
try {
|
|
209
276
|
const meta = await fetchMeta();
|
|
210
|
-
|
|
211
|
-
|
|
277
|
+
// Rank standalones AND design-system components together so DS slugs
|
|
278
|
+
// (e.g. "andromeda-heat-grid") surface in search. Each result object stays
|
|
279
|
+
// usable: standalones keep their full shape; DS components carry their
|
|
280
|
+
// metadata (slug, name, description, system, install command, …).
|
|
281
|
+
const standaloneRanked = meta.components.map((c) => ({
|
|
282
|
+
item: c,
|
|
283
|
+
score: scoreMatch(query, c),
|
|
284
|
+
}));
|
|
285
|
+
const systemRanked = (meta.systemComponents ?? []).map((c) => ({
|
|
286
|
+
item: c,
|
|
287
|
+
score: scoreSystemComponent(query, c),
|
|
288
|
+
}));
|
|
289
|
+
const ranked = [...standaloneRanked, ...systemRanked]
|
|
212
290
|
.filter((r) => r.score > 0)
|
|
213
291
|
.sort((a, b) => b.score - a.score)
|
|
214
292
|
.slice(0, limit)
|
|
215
|
-
.map((r) => r.
|
|
293
|
+
.map((r) => r.item);
|
|
216
294
|
const summary = ranked.length === 0
|
|
217
|
-
? `No matches for "${query}". Try \`list_categories\` to browse
|
|
295
|
+
? `No matches for "${query}". Try \`list_categories\` to browse standalones, or \`list_systems\` for design systems.`
|
|
218
296
|
: `${ranked.length} match${ranked.length === 1 ? '' : 'es'} for "${query}"`;
|
|
219
297
|
return {
|
|
220
298
|
content: [asTextContent(summary + '\n\n' + JSON.stringify(ranked, null, 2))],
|
|
@@ -240,12 +318,41 @@ server.registerTool('get_component', {
|
|
|
240
318
|
try {
|
|
241
319
|
const meta = await fetchMeta();
|
|
242
320
|
const component = meta.components.find((c) => c.slug === slug);
|
|
243
|
-
|
|
244
|
-
|
|
321
|
+
// Fall back to design-system components (kept out of components[]).
|
|
322
|
+
const systemComponent = component
|
|
323
|
+
? undefined
|
|
324
|
+
: (meta.systemComponents ?? []).find((c) => c.slug === slug);
|
|
325
|
+
if (!component && !systemComponent) {
|
|
326
|
+
return errorResult(`No component found with slug "${slug}". Use \`search_components\` or \`list_components\` to find standalone slugs, ` +
|
|
327
|
+
`or \`list_systems\` / \`get_system\` for design-system components (e.g. "andromeda-heat-grid").`);
|
|
245
328
|
}
|
|
246
329
|
const source = await fetchComponentSource(slug);
|
|
247
330
|
const code = source.files[0]?.content ?? '';
|
|
248
331
|
const filePath = source.files[0]?.path ?? `components/aicanvas/${slug}.tsx`;
|
|
332
|
+
if (systemComponent) {
|
|
333
|
+
const result = { ...systemComponent, filePath, code };
|
|
334
|
+
return {
|
|
335
|
+
content: [
|
|
336
|
+
asTextContent([
|
|
337
|
+
`# ${systemComponent.name}`,
|
|
338
|
+
'',
|
|
339
|
+
systemComponent.description,
|
|
340
|
+
'',
|
|
341
|
+
`Design system: ${systemComponent.system}`,
|
|
342
|
+
`Dependencies: ${systemComponent.dependencies.join(', ') || '(none)'}`,
|
|
343
|
+
`Registry dependencies: ${(systemComponent.registryDependencies ?? []).join(', ') || '(none)'}`,
|
|
344
|
+
`Install: ${systemComponent.installCommand}`,
|
|
345
|
+
`Homepage: ${systemComponent.homepageUrl}`,
|
|
346
|
+
'',
|
|
347
|
+
'Note: a manual (non-CLI) write must ALSO install the shared tokens this component depends on (see registry dependencies above and `get_system`). The CLI install resolves them automatically.',
|
|
348
|
+
'',
|
|
349
|
+
`--- ${filePath} ---`,
|
|
350
|
+
code,
|
|
351
|
+
].join('\n')),
|
|
352
|
+
],
|
|
353
|
+
structuredContent: result,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
249
356
|
const result = {
|
|
250
357
|
...component,
|
|
251
358
|
filePath,
|
|
@@ -288,9 +395,11 @@ server.registerTool('get_install_command', {
|
|
|
288
395
|
}, async ({ slug }) => {
|
|
289
396
|
try {
|
|
290
397
|
const meta = await fetchMeta();
|
|
291
|
-
const component = meta.components.find((c) => c.slug === slug)
|
|
398
|
+
const component = meta.components.find((c) => c.slug === slug) ??
|
|
399
|
+
(meta.systemComponents ?? []).find((c) => c.slug === slug);
|
|
292
400
|
if (!component) {
|
|
293
|
-
return errorResult(`No component found with slug "${slug}". Use \`search_components\` to find
|
|
401
|
+
return errorResult(`No component found with slug "${slug}". Use \`search_components\` to find standalone slugs, ` +
|
|
402
|
+
`or \`list_systems\` / \`get_system\` for design-system components.`);
|
|
294
403
|
}
|
|
295
404
|
const out = {
|
|
296
405
|
slug: component.slug,
|
|
@@ -307,7 +416,7 @@ server.registerTool('get_install_command', {
|
|
|
307
416
|
'',
|
|
308
417
|
` ${component.installCommand}`,
|
|
309
418
|
'',
|
|
310
|
-
`Dependencies: ${component.dependencies.join(', ') || '(none
|
|
419
|
+
`Dependencies: ${component.dependencies.join(', ') || '(none, React only)'}`,
|
|
311
420
|
`Source: ${component.sourceUrl}`,
|
|
312
421
|
`Preview: ${component.homepageUrl}`,
|
|
313
422
|
].join('\n')),
|
|
@@ -319,6 +428,144 @@ server.registerTool('get_install_command', {
|
|
|
319
428
|
return errorResult(err instanceof Error ? err.message : String(err));
|
|
320
429
|
}
|
|
321
430
|
});
|
|
431
|
+
// ── Tool: list_systems ───────────────────────────────────────────────────────
|
|
432
|
+
server.registerTool('list_systems', {
|
|
433
|
+
title: 'List AI Canvas design systems',
|
|
434
|
+
description: 'Return every design system available on AI Canvas. A design system is a coordinated set of components plus shared tokens and utilities — installable in one CLI command. Use when the user asks about "themes", "design systems", or wants more than a single component.',
|
|
435
|
+
inputSchema: {},
|
|
436
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
437
|
+
}, async () => {
|
|
438
|
+
try {
|
|
439
|
+
const meta = await fetchMeta();
|
|
440
|
+
const systems = meta.systems ?? [];
|
|
441
|
+
if (systems.length === 0) {
|
|
442
|
+
return {
|
|
443
|
+
content: [asTextContent('No design systems available in this registry build.')],
|
|
444
|
+
structuredContent: { systems: [] },
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
const lines = [
|
|
448
|
+
`${systems.length} design system${systems.length === 1 ? '' : 's'} on AI Canvas:`,
|
|
449
|
+
'',
|
|
450
|
+
...systems.map((s) => ` ${s.name.padEnd(16)} ${String(s.componentCount).padStart(2)} components, ${s.templateSlugs.length} templates`),
|
|
451
|
+
'',
|
|
452
|
+
'Use `get_system` to fetch the full source of a system, or `get_template` for a single composition.',
|
|
453
|
+
];
|
|
454
|
+
return {
|
|
455
|
+
content: [asTextContent(lines.join('\n'))],
|
|
456
|
+
structuredContent: { systems },
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
catch (err) {
|
|
460
|
+
return errorResult(err instanceof Error ? err.message : String(err));
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
// ── Tool: get_system ─────────────────────────────────────────────────────────
|
|
464
|
+
server.registerTool('get_system', {
|
|
465
|
+
title: 'Get a complete AI Canvas design system (every file)',
|
|
466
|
+
description: 'Return all files for a design system in a single response — every component, plus shared tokens and utilities — ready to write into the user\'s project. Use when the user wants to adopt a whole system rather than pick individual components.',
|
|
467
|
+
inputSchema: {
|
|
468
|
+
slug: z
|
|
469
|
+
.string()
|
|
470
|
+
.min(1)
|
|
471
|
+
.describe('Design system slug, e.g. "andromeda". Use `list_systems` to discover.'),
|
|
472
|
+
},
|
|
473
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
474
|
+
}, async ({ slug }) => {
|
|
475
|
+
try {
|
|
476
|
+
const meta = await fetchMeta();
|
|
477
|
+
const system = (meta.systems ?? []).find((s) => s.slug === slug);
|
|
478
|
+
if (!system) {
|
|
479
|
+
return errorResult(`No design system found with slug "${slug}". Use \`list_systems\` to see what's available.`);
|
|
480
|
+
}
|
|
481
|
+
const item = await fetchComponentSource(slug);
|
|
482
|
+
// The system's own design-system components, with their per-component
|
|
483
|
+
// install commands — so an agent can install (or hand-write) any subset.
|
|
484
|
+
const components = (meta.systemComponents ?? []).filter((c) => c.system === system.slug ||
|
|
485
|
+
(system.componentSlugs ?? []).includes(c.slug));
|
|
486
|
+
const summary = [
|
|
487
|
+
`# ${system.name} design system`,
|
|
488
|
+
'',
|
|
489
|
+
system.description,
|
|
490
|
+
'',
|
|
491
|
+
`Files: ${item.files.length}`,
|
|
492
|
+
`Dependencies: ${system.dependencies.join(', ') || '(none)'}`,
|
|
493
|
+
`Registry dependencies: ${(system.registryDependencies ?? []).join(', ') || '(none)'}`,
|
|
494
|
+
`Install (whole system): ${system.installCommand}`,
|
|
495
|
+
...(system.tokensInstallCommand
|
|
496
|
+
? [`Install (shared tokens only): ${system.tokensInstallCommand}`]
|
|
497
|
+
: []),
|
|
498
|
+
...(system.tokensSourceUrl ? [`Tokens source: ${system.tokensSourceUrl}`] : []),
|
|
499
|
+
`Homepage: ${system.homepageUrl}`,
|
|
500
|
+
'',
|
|
501
|
+
'Note: the shared tokens ship as a SEPARATE registry dependency. For a manual (non-CLI) file write you must ALSO install the tokens (see "Install (shared tokens only)" above); the CLI resolves them automatically.',
|
|
502
|
+
'',
|
|
503
|
+
`--- components (${components.length}) ---`,
|
|
504
|
+
...components.map((c) => ` ${c.slug.padEnd(28)} ${c.installCommand}`),
|
|
505
|
+
'',
|
|
506
|
+
`--- file index ---`,
|
|
507
|
+
...item.files.map((f) => ` ${f.path}`),
|
|
508
|
+
].join('\n');
|
|
509
|
+
return {
|
|
510
|
+
content: [asTextContent(summary)],
|
|
511
|
+
structuredContent: {
|
|
512
|
+
...system,
|
|
513
|
+
components,
|
|
514
|
+
files: item.files,
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
return errorResult(err instanceof Error ? err.message : String(err));
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
// ── Tool: get_template ───────────────────────────────────────────────────────
|
|
523
|
+
server.registerTool('get_template', {
|
|
524
|
+
title: 'Get a complete AI Canvas design-system template (every file)',
|
|
525
|
+
description: 'Return all files for a single template — the example composition plus every component it uses plus shared tokens. Use for "I want exactly this dashboard" / "give me the entire mission-control screen". One CLI command installs all files.',
|
|
526
|
+
inputSchema: {
|
|
527
|
+
slug: z
|
|
528
|
+
.string()
|
|
529
|
+
.min(1)
|
|
530
|
+
.describe('Template slug, e.g. "andromeda-mission-control", "andromeda-exchange-terminal". Use `list_systems` then inspect templateSlugs to discover.'),
|
|
531
|
+
},
|
|
532
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
533
|
+
}, async ({ slug }) => {
|
|
534
|
+
try {
|
|
535
|
+
const meta = await fetchMeta();
|
|
536
|
+
const template = (meta.templates ?? []).find((t) => t.slug === slug);
|
|
537
|
+
if (!template) {
|
|
538
|
+
return errorResult(`No template found with slug "${slug}". Use \`list_systems\` to see available templates.`);
|
|
539
|
+
}
|
|
540
|
+
const item = await fetchComponentSource(slug);
|
|
541
|
+
const summary = [
|
|
542
|
+
`# ${template.name} (${template.system}${template.domain ? ` · ${template.domain}` : ''})`,
|
|
543
|
+
'',
|
|
544
|
+
template.description,
|
|
545
|
+
'',
|
|
546
|
+
`Files: ${item.files.length}`,
|
|
547
|
+
`Dependencies: ${template.dependencies.join(', ') || '(none)'}`,
|
|
548
|
+
`Registry dependencies: ${(template.registryDependencies ?? []).join(', ') || '(none)'}`,
|
|
549
|
+
`Install: ${template.installCommand}`,
|
|
550
|
+
`Preview: ${template.homepageUrl}`,
|
|
551
|
+
'',
|
|
552
|
+
'Note: a manual (non-CLI) file write must ALSO install the registry dependencies above (the base components + shared tokens) for the composition to build. The CLI install resolves them automatically.',
|
|
553
|
+
'',
|
|
554
|
+
`--- file index ---`,
|
|
555
|
+
...item.files.map((f) => ` ${f.path}`),
|
|
556
|
+
].join('\n');
|
|
557
|
+
return {
|
|
558
|
+
content: [asTextContent(summary)],
|
|
559
|
+
structuredContent: {
|
|
560
|
+
...template,
|
|
561
|
+
files: item.files,
|
|
562
|
+
},
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
catch (err) {
|
|
566
|
+
return errorResult(err instanceof Error ? err.message : String(err));
|
|
567
|
+
}
|
|
568
|
+
});
|
|
322
569
|
// ── Boot ─────────────────────────────────────────────────────────────────────
|
|
323
570
|
async function main() {
|
|
324
571
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aicanvas/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"mcpName": "io.github.uinerd16/aicanvas",
|
|
4
5
|
"description": "Model Context Protocol server for AI Canvas — search, inspect, and install components from the aicanvas.me registry directly from your AI editor.",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"mcp",
|
|
7
8
|
"model-context-protocol",
|
|
9
|
+
"mcp-server",
|
|
8
10
|
"aicanvas",
|
|
9
11
|
"shadcn",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
12
|
+
"shadcn-registry",
|
|
13
|
+
"shadcn-ui",
|
|
14
|
+
"react-components",
|
|
15
|
+
"ui-components",
|
|
16
|
+
"component-library",
|
|
17
|
+
"framer-motion",
|
|
18
|
+
"animated-components",
|
|
19
|
+
"tailwindcss",
|
|
20
|
+
"design-system",
|
|
21
|
+
"ai-agents",
|
|
22
|
+
"ai-coding",
|
|
23
|
+
"claude",
|
|
24
|
+
"cursor",
|
|
25
|
+
"code-generation",
|
|
26
|
+
"registry"
|
|
13
27
|
],
|
|
14
28
|
"homepage": "https://aicanvas.me",
|
|
15
29
|
"repository": {
|