@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.
Files changed (3) hide show
  1. package/README.md +18 -8
  2. package/dist/index.js +265 -18
  3. 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
- Five tools, derived from the live [aicanvas.me](https://aicanvas.me) registry adding a component to the website auto-updates the MCP within minutes:
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 + complete `.tsx` source code for one component. |
24
- | `get_install_command` | Get the `npx shadcn add @aicanvas/<slug>` command for a component. |
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 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.
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 no package update required.
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 same as the AI Canvas project.
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 → all 64+ components' metadata
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.1.1';
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
- headers: { Accept: 'application/json', 'User-Agent': USER_AGENT },
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 ${meta.componentCount} components across ${meta.categories.length} categories.`,
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
- const ranked = meta.components
211
- .map((c) => ({ component: c, score: scoreMatch(query, c) }))
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.component);
293
+ .map((r) => r.item);
216
294
  const summary = ranked.length === 0
217
- ? `No matches for "${query}". Try \`list_categories\` to browse what exists.`
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
- if (!component) {
244
- return errorResult(`No component found with slug "${slug}". Use \`search_components\` or \`list_components\` to find valid slugs.`);
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 valid slugs.`);
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 React only)'}`,
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.1.1",
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
- "react",
11
- "components",
12
- "framer-motion"
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": {