@fragments-sdk/mcp 0.5.3 → 0.5.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @fragments-sdk/mcp
2
2
 
3
- Standalone MCP server for the Fragments design system. Gives AI coding assistants (Claude Code, Cursor, etc.) semantic search over components, blocks, and tokens — no Playwright or build tools required.
3
+ Standalone MCP server for the Fragments design system. Gives AI coding assistants (Claude Code, Cursor, etc.) intelligent component discovery over components, blocks, and tokens — no Playwright or build tools required.
4
4
 
5
5
  ## Setup
6
6
 
@@ -78,24 +78,19 @@ Once the project root is known, the server walks up from it, scans `package.json
78
78
  | `fragments_implement` | One-shot: components + blocks + tokens for a use case |
79
79
  | `fragments_render` | Render a component screenshot (requires dev server) |
80
80
  | `fragments_fix` | Generate token-compliance patches (requires dev server) |
81
+ | `fragments_graph` | Query the component relationship graph (dependencies, impact, composition, health) |
82
+ | `fragments_a11y` | Run an accessibility audit on a component (requires dev server) |
81
83
 
82
84
  ## How search works
83
85
 
84
- Queries run through **hybrid search** Convex-hosted vector search (Voyage-Code-3 embeddings) fused with local keyword scoring via Reciprocal Rank Fusion. If Convex is unreachable, falls back silently to keyword-only.
86
+ Queries use **keyword search** with synonym expansion, weighted multi-field scoring (name, description, tags, usage, category, variants), and category-aware ranking all running locally against your `fragments.json`. No external API calls are made.
87
+
88
+ **Premium:** Pass `--api-key` to enable semantic vector search (Voyage-Code-3 embeddings) fused with keyword results via Reciprocal Rank Fusion for deeper, meaning-based discovery.
85
89
 
86
90
  ## Options
87
91
 
88
92
  ```
89
93
  -p, --project-root <path> Project root (default: cwd)
90
- -u, --viewer-url <url> Dev server URL for render/fix tools
94
+ -u, --viewer-url <url> Dev server URL for render/fix/a11y tools
95
+ -k, --api-key <key> Premium API key for semantic vector search
91
96
  ```
92
-
93
- ## Convex backend
94
-
95
- The `convex/` directory contains the hosted search backend:
96
-
97
- - **schema.ts** — `entries` table with vector index (components, blocks, tokens)
98
- - **search.ts** — HTTP action that embeds queries with Voyage-Code-3 and runs vector search
99
- - **ingest.ts** — Mutation to upsert entries at publish time
100
-
101
- Index entries: `pnpm --filter @fragments-sdk/mcp index` (requires `VOYAGE_API_KEY` and `CONVEX_URL`).
package/dist/bin.js CHANGED
@@ -1,29 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startMcpServer
4
- } from "./chunk-QAXQVJOS.js";
5
- import "./chunk-WXUZ55XQ.js";
4
+ } from "./chunk-WEHZRM4L.js";
5
+ import "./chunk-IEJ7ZYTZ.js";
6
6
 
7
7
  // src/bin.ts
8
8
  var args = process.argv.slice(2);
9
9
  var projectRoot = process.cwd();
10
10
  var viewerUrl;
11
+ var apiKey;
11
12
  for (let i = 0; i < args.length; i++) {
12
13
  const arg = args[i];
13
14
  if (arg === "--project-root" || arg === "-p") {
14
15
  projectRoot = args[++i] ?? projectRoot;
15
16
  } else if (arg === "--viewer-url" || arg === "-u") {
16
17
  viewerUrl = args[++i];
18
+ } else if (arg === "--api-key" || arg === "-k") {
19
+ apiKey = args[++i];
17
20
  } else if (arg === "--help" || arg === "-h") {
18
21
  console.log(`
19
22
  Usage: fragments-mcp [options]
20
23
 
21
24
  Standalone MCP server for Fragments design system.
22
- Provides component discovery with semantic search, no CLI/Playwright needed.
25
+ Provides component discovery with keyword search, no CLI/Playwright needed.
23
26
 
24
27
  Options:
25
28
  -p, --project-root <path> Project root directory (default: cwd)
26
- -u, --viewer-url <url> Viewer URL for render/fix tools (optional)
29
+ -u, --viewer-url <url> Viewer URL for render/fix/a11y tools (optional)
30
+ -k, --api-key <key> Premium API key for semantic vector search (optional)
27
31
  -h, --help Show this help message
28
32
 
29
33
  Setup (Claude Code / Cursor):
@@ -41,7 +45,8 @@ Setup (Claude Code / Cursor):
41
45
  }
42
46
  startMcpServer({
43
47
  projectRoot,
44
- viewerUrl
48
+ viewerUrl,
49
+ apiKey
45
50
  }).catch((error) => {
46
51
  console.error("Failed to start MCP server:", error);
47
52
  process.exit(1);
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { startMcpServer } from './server.js';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nlet projectRoot = process.cwd();\nlet viewerUrl: string | undefined;\n\nfor (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === '--project-root' || arg === '-p') {\n projectRoot = args[++i] ?? projectRoot;\n } else if (arg === '--viewer-url' || arg === '-u') {\n viewerUrl = args[++i];\n } else if (arg === '--help' || arg === '-h') {\n console.log(`\nUsage: fragments-mcp [options]\n\nStandalone MCP server for Fragments design system.\nProvides component discovery with semantic search, no CLI/Playwright needed.\n\nOptions:\n -p, --project-root <path> Project root directory (default: cwd)\n -u, --viewer-url <url> Viewer URL for render/fix tools (optional)\n -h, --help Show this help message\n\nSetup (Claude Code / Cursor):\n {\n \"mcpServers\": {\n \"fragments\": {\n \"command\": \"npx\",\n \"args\": [\"@fragments-sdk/mcp\"]\n }\n }\n }\n`);\n process.exit(0);\n }\n}\n\n// Start server\nstartMcpServer({\n projectRoot,\n viewerUrl,\n}).catch((error) => {\n console.error('Failed to start MCP server:', error);\n process.exit(1);\n});\n"],"mappings":";;;;;;;AAIA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAI,cAAc,QAAQ,IAAI;AAC9B,IAAI;AAEJ,SAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAM,MAAM,KAAK,CAAC;AAElB,MAAI,QAAQ,oBAAoB,QAAQ,MAAM;AAC5C,kBAAc,KAAK,EAAE,CAAC,KAAK;AAAA,EAC7B,WAAW,QAAQ,kBAAkB,QAAQ,MAAM;AACjD,gBAAY,KAAK,EAAE,CAAC;AAAA,EACtB,WAAW,QAAQ,YAAY,QAAQ,MAAM;AAC3C,YAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAoBf;AACG,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAGA,eAAe;AAAA,EACb;AAAA,EACA;AACF,CAAC,EAAE,MAAM,CAAC,UAAU;AAClB,UAAQ,MAAM,+BAA+B,KAAK;AAClD,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { startMcpServer } from './server.js';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nlet projectRoot = process.cwd();\nlet viewerUrl: string | undefined;\nlet apiKey: string | undefined;\n\nfor (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === '--project-root' || arg === '-p') {\n projectRoot = args[++i] ?? projectRoot;\n } else if (arg === '--viewer-url' || arg === '-u') {\n viewerUrl = args[++i];\n } else if (arg === '--api-key' || arg === '-k') {\n apiKey = args[++i];\n } else if (arg === '--help' || arg === '-h') {\n console.log(`\nUsage: fragments-mcp [options]\n\nStandalone MCP server for Fragments design system.\nProvides component discovery with keyword search, no CLI/Playwright needed.\n\nOptions:\n -p, --project-root <path> Project root directory (default: cwd)\n -u, --viewer-url <url> Viewer URL for render/fix/a11y tools (optional)\n -k, --api-key <key> Premium API key for semantic vector search (optional)\n -h, --help Show this help message\n\nSetup (Claude Code / Cursor):\n {\n \"mcpServers\": {\n \"fragments\": {\n \"command\": \"npx\",\n \"args\": [\"@fragments-sdk/mcp\"]\n }\n }\n }\n`);\n process.exit(0);\n }\n}\n\n// Start server\nstartMcpServer({\n projectRoot,\n viewerUrl,\n apiKey,\n}).catch((error) => {\n console.error('Failed to start MCP server:', error);\n process.exit(1);\n});\n"],"mappings":";;;;;;;AAIA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAI,cAAc,QAAQ,IAAI;AAC9B,IAAI;AACJ,IAAI;AAEJ,SAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAM,MAAM,KAAK,CAAC;AAElB,MAAI,QAAQ,oBAAoB,QAAQ,MAAM;AAC5C,kBAAc,KAAK,EAAE,CAAC,KAAK;AAAA,EAC7B,WAAW,QAAQ,kBAAkB,QAAQ,MAAM;AACjD,gBAAY,KAAK,EAAE,CAAC;AAAA,EACtB,WAAW,QAAQ,eAAe,QAAQ,MAAM;AAC9C,aAAS,KAAK,EAAE,CAAC;AAAA,EACnB,WAAW,QAAQ,YAAY,QAAQ,MAAM;AAC3C,YAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAqBf;AACG,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAGA,eAAe;AAAA,EACb;AAAA,EACA;AAAA,EACA;AACF,CAAC,EAAE,MAAM,CAAC,UAAU;AAClB,UAAQ,MAAM,+BAA+B,KAAK;AAClD,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
@@ -201,7 +201,7 @@ var ComponentGraphEngine = class {
201
201
  const node = this.nodes.get(component);
202
202
  const subComponents = node?.subComponents ?? [];
203
203
  const parentEdges = (this.outgoing.get(component) ?? []).filter((e) => e.type === "parent-of");
204
- const requiredChildren = parentEdges.map((e) => e.target);
204
+ const children = parentEdges.map((e) => e.target);
205
205
  const childEdges = (this.incoming.get(component) ?? []).filter((e) => e.type === "parent-of");
206
206
  const parent = childEdges.length > 0 ? childEdges[0].source : void 0;
207
207
  const siblings = [];
@@ -225,22 +225,25 @@ var ComponentGraphEngine = class {
225
225
  component,
226
226
  compositionPattern: node?.compositionPattern,
227
227
  subComponents,
228
- requiredChildren,
228
+ children,
229
229
  parent,
230
230
  siblings,
231
231
  blocks: this.blockIndex.get(component) ?? []
232
232
  };
233
233
  }
234
- /** Get alternative components */
234
+ /** Get alternative components (deduplicated for bidirectional edges) */
235
235
  alternatives(component) {
236
+ const seen = /* @__PURE__ */ new Set();
236
237
  const alts = [];
237
238
  for (const edge of this.outgoing.get(component) ?? []) {
238
- if (edge.type === "alternative-to") {
239
+ if (edge.type === "alternative-to" && !seen.has(edge.target)) {
240
+ seen.add(edge.target);
239
241
  alts.push({ component: edge.target, note: edge.note });
240
242
  }
241
243
  }
242
244
  for (const edge of this.incoming.get(component) ?? []) {
243
- if (edge.type === "alternative-to") {
245
+ if (edge.type === "alternative-to" && !seen.has(edge.source)) {
246
+ seen.add(edge.source);
244
247
  alts.push({ component: edge.source, note: edge.note });
245
248
  }
246
249
  }
@@ -383,4 +386,4 @@ export {
383
386
  serializeGraph,
384
387
  deserializeGraph
385
388
  };
386
- //# sourceMappingURL=chunk-WXUZ55XQ.js.map
389
+ //# sourceMappingURL=chunk-IEJ7ZYTZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../context/dist/graph/index.js"],"sourcesContent":["// src/graph/types.ts\nvar GRAPH_EDGE_TYPES = [\n \"imports\",\n \"hook-depends\",\n \"renders\",\n \"composes\",\n \"parent-of\",\n \"alternative-to\",\n \"sibling-of\"\n];\nvar EDGE_TYPE_WEIGHTS = {\n \"imports\": 1,\n \"hook-depends\": 0.75,\n \"renders\": 0.5,\n \"composes\": 0.5,\n \"parent-of\": 1,\n \"alternative-to\": 1,\n \"sibling-of\": 0.75\n};\n\n// src/graph/engine.ts\nvar ComponentGraphEngine = class {\n nodes;\n outgoing;\n incoming;\n edges;\n blockIndex;\n health;\n constructor(graph, blocks) {\n this.nodes = /* @__PURE__ */ new Map();\n this.outgoing = /* @__PURE__ */ new Map();\n this.incoming = /* @__PURE__ */ new Map();\n this.edges = graph.edges;\n this.health = graph.health;\n this.blockIndex = /* @__PURE__ */ new Map();\n for (const node of graph.nodes) {\n this.nodes.set(node.name, node);\n this.outgoing.set(node.name, []);\n this.incoming.set(node.name, []);\n }\n for (const edge of graph.edges) {\n const out = this.outgoing.get(edge.source);\n if (out) out.push(edge);\n else this.outgoing.set(edge.source, [edge]);\n const inc = this.incoming.get(edge.target);\n if (inc) inc.push(edge);\n else this.incoming.set(edge.target, [edge]);\n }\n if (blocks) {\n for (const [blockName, block] of Object.entries(blocks)) {\n for (const comp of block.components) {\n const existing = this.blockIndex.get(comp);\n if (existing) existing.push(blockName);\n else this.blockIndex.set(comp, [blockName]);\n }\n }\n }\n }\n // -------------------------------------------------------------------------\n // Core queries\n // -------------------------------------------------------------------------\n /** Get outgoing edges from a component, optionally filtered by edge type */\n dependencies(component, edgeTypes) {\n const edges = this.outgoing.get(component) ?? [];\n if (!edgeTypes || edgeTypes.length === 0) return edges;\n return edges.filter((e) => edgeTypes.includes(e.type));\n }\n /** Get incoming edges to a component, optionally filtered by edge type */\n dependents(component, edgeTypes) {\n const edges = this.incoming.get(component) ?? [];\n if (!edgeTypes || edgeTypes.length === 0) return edges;\n return edges.filter((e) => edgeTypes.includes(e.type));\n }\n /** BFS transitive closure — what's affected if this component changes */\n impact(component, maxDepth = 3) {\n const affected = [];\n const visited = /* @__PURE__ */ new Set([component]);\n const queue = [\n { name: component, depth: 0, path: [component] }\n ];\n while (queue.length > 0) {\n const current = queue.shift();\n if (current.depth >= maxDepth) continue;\n const deps = this.incoming.get(current.name) ?? [];\n for (const edge of deps) {\n if (visited.has(edge.source)) continue;\n visited.add(edge.source);\n const newPath = [...current.path, edge.source];\n affected.push({\n component: edge.source,\n depth: current.depth + 1,\n path: newPath,\n edgeType: edge.type\n });\n queue.push({ name: edge.source, depth: current.depth + 1, path: newPath });\n }\n }\n const affectedComponents = /* @__PURE__ */ new Set([component, ...affected.map((a) => a.component)]);\n const affectedBlocks = /* @__PURE__ */ new Set();\n for (const comp of affectedComponents) {\n const blocks = this.blockIndex.get(comp);\n if (blocks) {\n for (const b of blocks) affectedBlocks.add(b);\n }\n }\n return {\n component,\n affected,\n affectedBlocks: [...affectedBlocks],\n totalAffected: affected.length\n };\n }\n /** BFS shortest path between two components (undirected) */\n path(from, to) {\n if (from === to) {\n return { found: true, path: [from], edges: [] };\n }\n const visited = /* @__PURE__ */ new Set([from]);\n const queue = [\n { name: from, path: [from], edges: [] }\n ];\n while (queue.length > 0) {\n const current = queue.shift();\n const allEdges = [\n ...this.outgoing.get(current.name) ?? [],\n ...this.incoming.get(current.name) ?? []\n ];\n for (const edge of allEdges) {\n const neighbor = edge.source === current.name ? edge.target : edge.source;\n if (visited.has(neighbor)) continue;\n visited.add(neighbor);\n const newPath = [...current.path, neighbor];\n const newEdges = [...current.edges, edge];\n if (neighbor === to) {\n return { found: true, path: newPath, edges: newEdges };\n }\n queue.push({ name: neighbor, path: newPath, edges: newEdges });\n }\n }\n return { found: false, path: [], edges: [] };\n }\n /** Connected components via BFS on undirected projection */\n islands() {\n const visited = /* @__PURE__ */ new Set();\n const components = [];\n for (const nodeName of this.nodes.keys()) {\n if (visited.has(nodeName)) continue;\n const island = [];\n const queue = [nodeName];\n visited.add(nodeName);\n while (queue.length > 0) {\n const current = queue.shift();\n island.push(current);\n const allEdges = [\n ...this.outgoing.get(current) ?? [],\n ...this.incoming.get(current) ?? []\n ];\n for (const edge of allEdges) {\n const neighbor = edge.source === current ? edge.target : edge.source;\n if (!visited.has(neighbor) && this.nodes.has(neighbor)) {\n visited.add(neighbor);\n queue.push(neighbor);\n }\n }\n }\n components.push(island.sort());\n }\n return components.sort((a, b) => b.length - a.length);\n }\n /** All components reachable within N hops (undirected) */\n neighbors(component, maxHops = 1) {\n const neighbors = [];\n const visited = /* @__PURE__ */ new Set([component]);\n const queue = [\n { name: component, hops: 0 }\n ];\n while (queue.length > 0) {\n const current = queue.shift();\n if (current.hops >= maxHops) continue;\n const allEdges = [\n ...this.outgoing.get(current.name) ?? [],\n ...this.incoming.get(current.name) ?? []\n ];\n for (const edge of allEdges) {\n const neighbor = edge.source === current.name ? edge.target : edge.source;\n if (visited.has(neighbor)) continue;\n visited.add(neighbor);\n neighbors.push({\n component: neighbor,\n hops: current.hops + 1,\n edgeType: edge.type\n });\n queue.push({ name: neighbor, hops: current.hops + 1 });\n }\n }\n return { component, neighbors };\n }\n // -------------------------------------------------------------------------\n // Design-system queries\n // -------------------------------------------------------------------------\n /** Get the composition tree for a compound component */\n composition(component) {\n const node = this.nodes.get(component);\n const subComponents = node?.subComponents ?? [];\n const parentEdges = (this.outgoing.get(component) ?? []).filter((e) => e.type === \"parent-of\");\n const children = parentEdges.map((e) => e.target);\n const childEdges = (this.incoming.get(component) ?? []).filter((e) => e.type === \"parent-of\");\n const parent = childEdges.length > 0 ? childEdges[0].source : void 0;\n const siblings = [];\n if (parent) {\n const parentOut = (this.outgoing.get(parent) ?? []).filter((e) => e.type === \"parent-of\");\n for (const edge of parentOut) {\n if (edge.target !== component) {\n siblings.push(edge.target);\n }\n }\n }\n const siblingEdges = [\n ...(this.outgoing.get(component) ?? []).filter((e) => e.type === \"sibling-of\"),\n ...(this.incoming.get(component) ?? []).filter((e) => e.type === \"sibling-of\")\n ];\n for (const edge of siblingEdges) {\n const sib = edge.source === component ? edge.target : edge.source;\n if (!siblings.includes(sib)) siblings.push(sib);\n }\n return {\n component,\n compositionPattern: node?.compositionPattern,\n subComponents,\n children,\n parent,\n siblings,\n blocks: this.blockIndex.get(component) ?? []\n };\n }\n /** Get alternative components (deduplicated for bidirectional edges) */\n alternatives(component) {\n const seen = /* @__PURE__ */ new Set();\n const alts = [];\n for (const edge of this.outgoing.get(component) ?? []) {\n if (edge.type === \"alternative-to\" && !seen.has(edge.target)) {\n seen.add(edge.target);\n alts.push({ component: edge.target, note: edge.note });\n }\n }\n for (const edge of this.incoming.get(component) ?? []) {\n if (edge.type === \"alternative-to\" && !seen.has(edge.source)) {\n seen.add(edge.source);\n alts.push({ component: edge.source, note: edge.note });\n }\n }\n return alts;\n }\n /** Get blocks that use a component */\n blocksUsing(component) {\n return this.blockIndex.get(component) ?? [];\n }\n /** Extract an induced subgraph for a set of components */\n subgraph(components) {\n const componentSet = new Set(components);\n const nodes = components.map((name) => this.nodes.get(name)).filter((n) => n !== void 0);\n const edges = this.edges.filter(\n (e) => componentSet.has(e.source) && componentSet.has(e.target)\n );\n return {\n nodes,\n edges,\n health: computeHealthFromData(nodes, edges, this.blockIndex)\n };\n }\n /** Return precomputed health metrics */\n getHealth() {\n return this.health;\n }\n /** Get a single node by name */\n getNode(name) {\n return this.nodes.get(name);\n }\n /** Check if a component exists in the graph */\n hasNode(name) {\n return this.nodes.has(name);\n }\n};\nfunction computeHealthFromData(nodes, edges, blockIndex) {\n const nodeNames = new Set(nodes.map((n) => n.name));\n const degreeMap = /* @__PURE__ */ new Map();\n for (const name of nodeNames) {\n degreeMap.set(name, 0);\n }\n for (const edge of edges) {\n degreeMap.set(edge.source, (degreeMap.get(edge.source) ?? 0) + 1);\n degreeMap.set(edge.target, (degreeMap.get(edge.target) ?? 0) + 1);\n }\n const orphans = [];\n for (const [name, degree] of degreeMap) {\n if (degree === 0) orphans.push(name);\n }\n const hubs = [...degreeMap.entries()].map(([name, degree]) => ({ name, degree })).sort((a, b) => b.degree - a.degree).slice(0, 10);\n let inBlock = 0;\n if (blockIndex) {\n for (const name of nodeNames) {\n if ((blockIndex.get(name) ?? []).length > 0) inBlock++;\n }\n }\n const compositionCoverage = nodeNames.size > 0 ? Math.round(inBlock / nodeNames.size * 100) : 0;\n const adjacency = /* @__PURE__ */ new Map();\n for (const name of nodeNames) {\n adjacency.set(name, /* @__PURE__ */ new Set());\n }\n for (const edge of edges) {\n adjacency.get(edge.source)?.add(edge.target);\n adjacency.get(edge.target)?.add(edge.source);\n }\n const visited = /* @__PURE__ */ new Set();\n const connectedComponents = [];\n for (const name of nodeNames) {\n if (visited.has(name)) continue;\n const island = [];\n const queue = [name];\n visited.add(name);\n while (queue.length > 0) {\n const current = queue.shift();\n island.push(current);\n for (const neighbor of adjacency.get(current) ?? []) {\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n queue.push(neighbor);\n }\n }\n }\n connectedComponents.push(island.sort());\n }\n connectedComponents.sort((a, b) => b.length - a.length);\n const totalDegree = [...degreeMap.values()].reduce((sum, d) => sum + d, 0);\n const averageDegree = nodeNames.size > 0 ? Math.round(totalDegree / nodeNames.size * 100) / 100 : 0;\n return {\n orphans: orphans.sort(),\n hubs,\n compositionCoverage,\n connectedComponents,\n averageDegree,\n nodeCount: nodeNames.size,\n edgeCount: edges.length\n };\n}\n\n// src/graph/serialization.ts\nfunction serializeGraph(graph) {\n return {\n nodes: graph.nodes,\n edges: graph.edges.map(serializeEdge),\n health: graph.health\n };\n}\nfunction deserializeGraph(serialized) {\n return {\n nodes: serialized.nodes,\n edges: serialized.edges.map(deserializeEdge),\n health: serialized.health\n };\n}\nfunction serializeEdge(edge) {\n const result = {\n s: edge.source,\n t: edge.target,\n ty: edge.type,\n w: edge.weight,\n p: edge.provenance\n };\n if (edge.note) result.no = edge.note;\n return result;\n}\nfunction deserializeEdge(edge) {\n const result = {\n source: edge.s,\n target: edge.t,\n type: edge.ty,\n weight: edge.w,\n provenance: edge.p\n };\n if (edge.no) result.note = edge.no;\n return result;\n}\nexport {\n ComponentGraphEngine,\n EDGE_TYPE_WEIGHTS,\n GRAPH_EDGE_TYPES,\n computeHealthFromData,\n deserializeGraph,\n serializeGraph\n};\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ComponentGraphEngine,
3
3
  deserializeGraph
4
- } from "./chunk-WXUZ55XQ.js";
4
+ } from "./chunk-IEJ7ZYTZ.js";
5
5
 
6
6
  // src/server.ts
7
7
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -745,8 +745,10 @@ var SYNONYM_MAP = {
745
745
  "input": ["form", "field", "text", "entry"],
746
746
  "button": ["action", "click", "submit", "trigger"],
747
747
  "action": ["button", "click", "trigger"],
748
+ "submit": ["button", "form", "action", "send"],
748
749
  "alert": ["notification", "message", "warning", "error", "feedback"],
749
750
  "notification": ["alert", "message", "toast"],
751
+ "feedback": ["form", "comment", "review", "rating"],
750
752
  "card": ["container", "panel", "box", "content"],
751
753
  "toggle": ["switch", "checkbox", "boolean", "on/off"],
752
754
  "switch": ["toggle", "checkbox", "boolean"],
@@ -755,15 +757,20 @@ var SYNONYM_MAP = {
755
757
  "login": ["auth", "signin", "authentication", "form"],
756
758
  "auth": ["login", "signin", "authentication"],
757
759
  "chat": ["message", "conversation", "ai"],
758
- "table": ["data", "grid", "list", "rows"]
760
+ "table": ["data", "grid", "list", "rows"],
761
+ "textarea": ["text", "input", "multiline", "area", "comment"],
762
+ "area": ["textarea", "multiline", "text"]
759
763
  };
760
- async function searchConvex(query, limit = 10, kind) {
764
+ async function searchConvex(query, apiKey, limit = 10, kind) {
761
765
  try {
762
766
  const controller = new AbortController();
763
767
  const timeout = setTimeout(() => controller.abort(), CONVEX_TIMEOUT_MS);
764
768
  const response = await fetch(CONVEX_SEARCH_URL, {
765
769
  method: "POST",
766
- headers: { "Content-Type": "application/json" },
770
+ headers: {
771
+ "Content-Type": "application/json",
772
+ "Authorization": `Bearer ${apiKey}`
773
+ },
767
774
  body: JSON.stringify({ query, limit, ...kind && { kind } }),
768
775
  signal: controller.signal
769
776
  });
@@ -896,7 +903,7 @@ function reciprocalRankFusion(resultSets, k = 60) {
896
903
  });
897
904
  return fused;
898
905
  }
899
- async function hybridSearch(query, data, limit = 10, kind) {
906
+ async function hybridSearch(query, data, limit = 10, kind, apiKey) {
900
907
  const keywordResults = [];
901
908
  if (!kind || kind === "component") {
902
909
  keywordResults.push(...keywordScoreComponents(query, data.fragments));
@@ -911,14 +918,17 @@ async function hybridSearch(query, data, limit = 10, kind) {
911
918
  keywordResults.forEach((r, i) => {
912
919
  r.rank = i;
913
920
  });
914
- const vectorResults = await searchConvex(query, limit, kind);
921
+ if (!apiKey) {
922
+ return keywordResults.slice(0, limit);
923
+ }
924
+ const vectorResults = await searchConvex(query, apiKey, limit, kind);
915
925
  if (vectorResults.length === 0) {
916
926
  return keywordResults.slice(0, limit);
917
927
  }
918
928
  const graphBoostResults = [];
919
929
  if (data.graph) {
920
930
  try {
921
- const { ComponentGraphEngine: ComponentGraphEngine2, deserializeGraph: deserializeGraph2 } = await import("./graph-6PMJ6XCF.js");
931
+ const { ComponentGraphEngine: ComponentGraphEngine2, deserializeGraph: deserializeGraph2 } = await import("./graph-UWOAWP4T.js");
922
932
  const graph = deserializeGraph2(data.graph);
923
933
  const engine = new ComponentGraphEngine2(graph);
924
934
  const topComponents = [...keywordResults, ...vectorResults].filter((r) => r.kind === "component").slice(0, 5);
@@ -1090,10 +1100,14 @@ function handleGraphTool(args, serializedGraph, blocks) {
1090
1100
  switch (args.mode) {
1091
1101
  case "health": {
1092
1102
  const health = engine.getHealth();
1103
+ const blockCount = blocks ? Object.keys(blocks).length : 0;
1093
1104
  return {
1094
1105
  text: JSON.stringify({
1095
1106
  mode: "health",
1096
1107
  ...health,
1108
+ ...health.compositionCoverage === 0 && blockCount === 0 && {
1109
+ compositionNote: "No composition blocks defined yet \u2014 compositionCoverage will increase as blocks are added"
1110
+ },
1097
1111
  summary: `${health.nodeCount} components, ${health.edgeCount} edges, ${health.connectedComponents.length} island(s), ${health.orphans.length} orphan(s), ${health.compositionCoverage}% in blocks`
1098
1112
  }, null, 2)
1099
1113
  };
@@ -1237,6 +1251,7 @@ function handleGraphTool(args, serializedGraph, blocks) {
1237
1251
 
1238
1252
  // src/server.ts
1239
1253
  var TOOL_NAMES = buildToolNames(BRAND.nameLower);
1254
+ var NO_VIEWER_MSG = "This tool requires a running dev server. Pass --viewer-url to the MCP server, or use the fragments-dev MCP config which connects to your local dev server.";
1240
1255
  var TOOLS = buildMcpTools(BRAND.nameLower);
1241
1256
  function createMcpServer(config) {
1242
1257
  const server = new Server(
@@ -1387,7 +1402,7 @@ If you're a library author, run \`${BRAND.cliCommand} build\` first.`
1387
1402
  blocks: allBlocks,
1388
1403
  tokenData: data.tokens
1389
1404
  };
1390
- const searchResults = await hybridSearch(fullQuery, localData, 10, "component");
1405
+ const searchResults = await hybridSearch(fullQuery, localData, 10, "component", config.apiKey);
1391
1406
  const scored = searchResults.map((result) => {
1392
1407
  const fragment = allFragments.find(
1393
1408
  (s) => s.meta.name.toLowerCase() === result.name.toLowerCase()
@@ -1511,7 +1526,10 @@ If you're a library author, run \`${BRAND.cliCommand} build\` first.`
1511
1526
  description: s.meta.description,
1512
1527
  status: s.meta.status ?? "stable",
1513
1528
  variantCount: s.variants.length,
1514
- tags: s.meta.tags ?? []
1529
+ tags: s.meta.tags ?? [],
1530
+ ...includeCode && s.variants[0]?.code && {
1531
+ example: s.variants[0].code
1532
+ }
1515
1533
  }));
1516
1534
  return {
1517
1535
  content: [{
@@ -1644,7 +1662,7 @@ If you're a library author, run \`${BRAND.cliCommand} build\` first.`
1644
1662
  text: JSON.stringify({
1645
1663
  total: 0,
1646
1664
  blocks: [],
1647
- hint: `No blocks found. Run \`${BRAND.cliCommand} build\` after adding .block.ts files.`
1665
+ hint: `No composition blocks found. Blocks are reusable patterns showing how components wire together (e.g., "Login Form", "Settings Page"). Create .block.ts files and run \`${BRAND.cliCommand} build\`.`
1648
1666
  }, null, 2)
1649
1667
  }]
1650
1668
  };
@@ -1682,7 +1700,10 @@ If you're a library author, run \`${BRAND.cliCommand} build\` first.`
1682
1700
  type: "text",
1683
1701
  text: JSON.stringify({
1684
1702
  total: filtered.length,
1685
- blocks: filtered
1703
+ blocks: filtered,
1704
+ ...filtered.length === 0 && allBlocks.length > 0 && {
1705
+ hint: "No blocks matching your query. Try broader search terms."
1706
+ }
1686
1707
  }, null, 2)
1687
1708
  }]
1688
1709
  };
@@ -1764,7 +1785,7 @@ If you're a library author, run \`${BRAND.cliCommand} build\` first.`
1764
1785
  blocks: allBlocks,
1765
1786
  tokenData
1766
1787
  };
1767
- const searchResults = await hybridSearch(useCase, localData, 15);
1788
+ const searchResults = await hybridSearch(useCase, localData, 15, void 0, config.apiKey);
1768
1789
  const componentResults = searchResults.filter((r) => r.kind === "component").slice(0, 3);
1769
1790
  const blockResults = searchResults.filter((r) => r.kind === "block").slice(0, 2);
1770
1791
  const tokenResults = searchResults.filter((r) => r.kind === "token").slice(0, 5);
@@ -1852,7 +1873,7 @@ If you're a library author, run \`${BRAND.cliCommand} build\` first.`
1852
1873
  return {
1853
1874
  content: [{
1854
1875
  type: "text",
1855
- text: "Render requires a running Fragments dev server. Start it with `fragments dev` and pass --viewer-url, or use the CLI MCP server which includes Playwright."
1876
+ text: NO_VIEWER_MSG
1856
1877
  }],
1857
1878
  isError: true
1858
1879
  };
@@ -1968,7 +1989,7 @@ Suggestion: ${result.suggestion}` : ""}`
1968
1989
  return {
1969
1990
  content: [{
1970
1991
  type: "text",
1971
- text: "Fix requires a running Fragments dev server. Start it with `fragments dev` and pass --viewer-url."
1992
+ text: NO_VIEWER_MSG
1972
1993
  }],
1973
1994
  isError: true
1974
1995
  };
@@ -2028,7 +2049,7 @@ Suggestion: ${result.suggestion}` : ""}`
2028
2049
  return {
2029
2050
  content: [{
2030
2051
  type: "text",
2031
- text: "A11y audit requires a running Fragments dev server. Start it with `fragments dev` and pass --viewer-url."
2052
+ text: NO_VIEWER_MSG
2032
2053
  }],
2033
2054
  isError: true
2034
2055
  };
@@ -2129,4 +2150,4 @@ export {
2129
2150
  createMcpServer,
2130
2151
  startMcpServer
2131
2152
  };
2132
- //# sourceMappingURL=chunk-QAXQVJOS.js.map
2153
+ //# sourceMappingURL=chunk-WEHZRM4L.js.map