@fragments-sdk/mcp 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,11 +2,19 @@
2
2
 
3
3
  > **Source-available.** This package is distributed publicly on npm under FSL-1.1-MIT; source stays private. Contribute by filing an issue at https://github.com/fragments-sdk/fragments/issues.
4
4
 
5
- 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.
5
+ Standalone MCP server for Fragments. It helps AI coding assistants validate generated UI against your design system: check candidate specs, repair safe governance failures, suggest design tokens before hardcoded CSS is written, and triage real Fragments Cloud findings.
6
+
7
+ The public MCP surface is intentionally small: `govern`, `validate_and_fix`, `tokens.suggest`, `findings_list`, `findings_summary`, `findings_for_file`, and `swap_to_canonical`.
6
8
 
7
9
  ## Setup
8
10
 
9
- Add a **project-level** MCP config to your workspace root. This ensures the server runs from your project directory and auto-discovers `fragments.json` via `node_modules`.
11
+ The recommended setup writes both MCP config and IDE rules that teach agents the validator loop:
12
+
13
+ ```bash
14
+ npx @fragments-sdk/mcp@latest init
15
+ ```
16
+
17
+ You can also add a **project-level** MCP config to your workspace root manually. This ensures the server runs from your project directory and auto-discovers `fragments.json` via `node_modules`.
10
18
 
11
19
  ### Cursor
12
20
 
@@ -69,32 +77,38 @@ The server auto-discovers `fragments.json` through two layers:
69
77
 
70
78
  Once the project root is known, the server walks up from it, scans `package.json` dependencies for packages with a `"fragments"` field, and resolves through `node_modules` (supporting pnpm, Yarn, and npm).
71
79
 
80
+ ## Validator Loop
81
+
82
+ Ask your AI assistant to follow this loop whenever it generates or edits JSX, class names, style props, or CSS:
83
+
84
+ 1. Draft the UI spec or code change.
85
+ 2. Call `govern({ spec })`.
86
+ 3. If the result asks for revision, call `validate_and_fix({ spec, applyFixes: true })`.
87
+ 4. Apply deterministic repairs and run `govern` again.
88
+ 5. Before writing hardcoded CSS values, call `tokens.suggest({ property, value })` and use the returned `cssValue` when present.
89
+
72
90
  ## Tools
73
91
 
74
92
  | Tool | Description |
75
93
  |------|-------------|
76
- | `discover` | List, search, or get AI-powered component suggestions |
77
- | `inspect` | Full component details props, examples, guidelines |
78
- | `blocks` | Composition patterns (e.g. "Login Form", "Settings Page") |
79
- | `tokens` | CSS custom properties by category |
80
- | `implement` | One-shot: components + blocks + tokens for a use case |
81
- | `render` | Render a component screenshot (requires dev server) |
82
- | `fix` | Generate token-compliance patches (requires dev server) |
83
- | `graph` | Query the component relationship graph (dependencies, impact, composition, health) |
84
- | `a11y` | Run an accessibility audit on a component (requires dev server) |
85
-
86
- ## How search works
94
+ | `govern` | Validate an AI-generated UI spec and return violations/fix guidance |
95
+ | `validate_and_fix` | Deterministically repair safe governance failures and return a fixed spec |
96
+ | `tokens.suggest` | Suggest the right design token for a CSS property and optional raw value |
97
+ | `findings_list` | List Cloud governance findings for agent planning |
98
+ | `findings_summary` | Summarize Cloud findings by severity, category, rule, and file |
99
+ | `findings_for_file` | Retrieve open Cloud findings for the file an agent is editing |
100
+ | `swap_to_canonical` | Suggest swaps from raw HTML (`<button>`, `<input>`, `<dialog>`, …) to the project's canonical components when a mapping is at confidence `auto` or `overridden` |
87
101
 
88
- 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.
102
+ ## How Token Suggestions Work
89
103
 
90
- **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.
104
+ Validator tools run locally against your `fragments.json` or against a Cloud catalog when configured. Token suggestions use property-family-aware matching so color properties receive color tokens, spacing properties receive spacing tokens, and unsupported properties return an honest `noSuggestion` result.
91
105
 
92
106
  ## Options
93
107
 
94
108
  ```
95
109
  -p, --project-root <path> Project root (default: cwd)
96
- -u, --viewer-url <url> Dev server URL for render/fix/a11y tools
97
- -k, --api-key <key> Premium API key for semantic vector search
110
+ --cloud-api-key <key> Cloud API key for org-scoped catalog and findings
111
+ --generate-rules Generate IDE rules files and exit
98
112
  ```
99
113
 
100
114
  <img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=a02beb71-df11-498d-8e1f-39de2e64ce5b" />
package/dist/bin.js CHANGED
@@ -4,9 +4,8 @@ import {
4
4
  resolveDataAdapter,
5
5
  resolveSearchApiKey,
6
6
  startMcpServer
7
- } from "./chunk-YSNIGHNU.js";
7
+ } from "./chunk-YJTMK4JY.js";
8
8
  import "./chunk-4SVS3AA3.js";
9
- import "./chunk-YSRGQDEB.js";
10
9
  import "./chunk-7D4SUZUM.js";
11
10
 
12
11
  // src/bin.ts
@@ -46,8 +45,6 @@ Examples:
46
45
  process.exit(0);
47
46
  }
48
47
  var projectRoot = process.cwd();
49
- var viewerUrl;
50
- var apiKey;
51
48
  var cloudApiKey;
52
49
  var source = "auto";
53
50
  var generateRules = false;
@@ -57,12 +54,6 @@ for (let i = 0; i < args.length; i++) {
57
54
  const arg = args[i];
58
55
  if (arg === "--project-root" || arg === "-p") {
59
56
  projectRoot = args[++i] ?? projectRoot;
60
- } else if (arg === "--viewer-url" || arg === "-u") {
61
- viewerUrl = args[++i];
62
- } else if (arg === "--api-key" || arg === "-k") {
63
- apiKey = args[++i];
64
- } else if (arg === "--search-api-key") {
65
- apiKey = args[++i];
66
57
  } else if (arg === "--cloud-api-key") {
67
58
  cloudApiKey = args[++i];
68
59
  } else if (arg === "--source") {
@@ -86,16 +77,13 @@ for (let i = 0; i < args.length; i++) {
86
77
  Usage: fragments-mcp [command] [options]
87
78
 
88
79
  Standalone MCP server for design system intelligence.
89
- Provides component discovery, search, and governance \u2014 no CLI/Playwright needed.
80
+ Provides UI validation, deterministic repair, token suggestions, and Cloud findings.
90
81
 
91
82
  Commands:
92
83
  init Write MCP config for your AI editor(s)
93
84
 
94
85
  Options:
95
86
  -p, --project-root <path> Project root directory (default: cwd)
96
- -u, --viewer-url <url> Viewer URL for render/fix/a11y tools (optional)
97
- -k, --api-key <key> Premium API key for semantic vector search (optional)
98
- --search-api-key <key> Explicit premium vector search key (same as --api-key)
99
87
  --cloud-api-key <key> Cloud API key for org-scoped catalog access
100
88
  --source <mode> Source override: auto, fragments-json, cloud, bundle, extract
101
89
  --generate-rules Generate IDE rules files and exit
@@ -112,8 +100,7 @@ Quick start:
112
100
  var fileConfig = loadConfigFile(projectRoot) ?? void 0;
113
101
  var serverConfig = {
114
102
  projectRoot,
115
- viewerUrl,
116
- searchApiKey: resolveSearchApiKey({ projectRoot, apiKey }, fileConfig),
103
+ searchApiKey: resolveSearchApiKey({ projectRoot }, fileConfig),
117
104
  cloudApiKey,
118
105
  source,
119
106
  fileConfig
@@ -126,7 +113,7 @@ if (mode === "extract") {
126
113
  }
127
114
  if (generateRules) {
128
115
  (async () => {
129
- const { generateRulesFiles } = await import("./rules-TN4KHFFG.js");
116
+ const { generateRulesFiles } = await import("./rules-JUZ3RABB.js");
130
117
  const defaultFormats = ["cursor", "claude", "copilot"];
131
118
  const formats = rulesFormats ? rulesFormats.split(",").map((f) => f.trim()) : defaultFormats;
132
119
  const validFormats = /* @__PURE__ */ new Set(["cursor", "claude", "copilot"]);
@@ -162,9 +149,7 @@ if (generateRules) {
162
149
  } else {
163
150
  startMcpServer({
164
151
  projectRoot,
165
- viewerUrl,
166
- apiKey,
167
- searchApiKey: resolveSearchApiKey({ projectRoot, apiKey }, fileConfig),
152
+ searchApiKey: resolveSearchApiKey({ projectRoot }, fileConfig),
168
153
  cloudApiKey,
169
154
  source,
170
155
  fileConfig,
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';\nimport { loadConfigFile } from './config.js';\nimport { resolveDataAdapter, resolveSearchApiKey } from './source-selection.js';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\n\n// ---------------------------------------------------------------------------\n// Subcommand: init\n// ---------------------------------------------------------------------------\nif (args[0] === 'init') {\n const { runInit } = await import('./init.js');\n const initArgs = args.slice(1);\n\n let projectRoot = process.cwd();\n let clients: string[] | undefined;\n\n for (let i = 0; i < initArgs.length; i++) {\n const arg = initArgs[i];\n if (arg === '--project-root' || arg === '-p') {\n projectRoot = initArgs[++i] ?? projectRoot;\n } else if (arg === '--client' || arg === '-c') {\n clients = (initArgs[++i] ?? '').split(',').map((s) => s.trim());\n } else if (arg === '--help' || arg === '-h') {\n console.log(`\nUsage: fragments-mcp init [options]\n\nWrite MCP server config for your AI editor(s).\nAuto-detects Claude Code, Cursor, VS Code, and Windsurf.\n\nOptions:\n -p, --project-root <path> Project root directory (default: cwd)\n -c, --client <list> Comma-separated clients: claude,cursor,vscode,windsurf\n (default: auto-detect)\n -h, --help Show this help message\n\nExamples:\n npx @fragments-sdk/mcp init\n npx @fragments-sdk/mcp init --client cursor,claude\n`);\n process.exit(0);\n }\n }\n\n runInit({ projectRoot, clients: clients as import('./init.js').Client[] });\n process.exit(0);\n}\n\n// ---------------------------------------------------------------------------\n// Main: MCP server mode\n// ---------------------------------------------------------------------------\nlet projectRoot = process.cwd();\nlet viewerUrl: string | undefined;\nlet apiKey: string | undefined;\nlet cloudApiKey: string | undefined;\nlet source: 'auto' | 'fragments-json' | 'cloud' | 'bundle' | 'extract' =\n 'auto';\nlet generateRules = false;\nlet rulesFormats: string | undefined;\nlet force = false;\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 === '--search-api-key') {\n apiKey = args[++i];\n } else if (arg === '--cloud-api-key') {\n cloudApiKey = args[++i];\n } else if (arg === '--source') {\n const next = args[++i];\n if (\n next === 'auto' ||\n next === 'fragments-json' ||\n next === 'cloud' ||\n next === 'bundle' ||\n next === 'extract'\n ) {\n source = next;\n } else {\n console.error(\n `Invalid source: \"${next}\". Valid values: auto, fragments-json, cloud, bundle, extract`,\n );\n process.exit(1);\n }\n } else if (arg === '--generate-rules') {\n generateRules = true;\n } else if (arg === '--formats') {\n rulesFormats = args[++i];\n } else if (arg === '--force') {\n force = true;\n } else if (arg === '--help' || arg === '-h') {\n console.log(`\nUsage: fragments-mcp [command] [options]\n\nStandalone MCP server for design system intelligence.\nProvides component discovery, search, and governance — no CLI/Playwright needed.\n\nCommands:\n init Write MCP config for your AI editor(s)\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 --search-api-key <key> Explicit premium vector search key (same as --api-key)\n --cloud-api-key <key> Cloud API key for org-scoped catalog access\n --source <mode> Source override: auto, fragments-json, cloud, bundle, extract\n --generate-rules Generate IDE rules files and exit\n --formats <list> Comma-separated formats: cursor,claude,copilot (default: all)\n --force Overwrite existing rules files\n -h, --help Show this help message\n\nQuick start:\n npx @fragments-sdk/mcp init\n`);\n process.exit(0);\n }\n}\n\nconst fileConfig = loadConfigFile(projectRoot) ?? undefined;\nconst serverConfig = {\n projectRoot,\n viewerUrl,\n searchApiKey: resolveSearchApiKey({ projectRoot, apiKey }, fileConfig),\n cloudApiKey,\n source,\n fileConfig,\n};\nconst { adapter, mode } = resolveDataAdapter(serverConfig, fileConfig);\n\nif (mode === 'extract') {\n console.error(\n '[fragments-mcp] No fragments.json found — auto-extracting from source.',\n );\n}\n\n// --generate-rules: generate IDE rules and exit\nif (generateRules) {\n (async () => {\n const { generateRulesFiles } = await import('./rules.js');\n type RulesFormat = 'cursor' | 'claude' | 'copilot';\n\n const defaultFormats: RulesFormat[] = ['cursor', 'claude', 'copilot'];\n const formats: RulesFormat[] = rulesFormats\n ? (rulesFormats.split(',').map(f => f.trim()) as RulesFormat[])\n : defaultFormats;\n\n // Validate formats\n const validFormats = new Set(['cursor', 'claude', 'copilot']);\n for (const fmt of formats) {\n if (!validFormats.has(fmt)) {\n console.error(`Invalid format: \"${fmt}\". Valid formats: cursor, claude, copilot`);\n process.exit(1);\n }\n }\n\n // Load data using the same adapter\n const data = await adapter.load(projectRoot);\n\n // Resolve brand name from config\n const { BRAND } = await import('./constants.js');\n const brandName = fileConfig?.brand?.name ?? BRAND.name;\n\n // Generate rules\n const files = generateRulesFiles({\n data,\n brandName,\n outputDir: projectRoot,\n formats,\n force,\n });\n\n if (files.length === 0) {\n console.log('No rules files generated.');\n } else {\n console.log(`Generated ${files.length} rules file(s):`);\n for (const file of files) {\n console.log(` ${file}`);\n }\n }\n\n process.exit(0);\n })().catch((error) => {\n console.error('Failed to generate rules:', error instanceof Error ? error.message : error);\n process.exit(1);\n });\n} else {\n // Default: start MCP server\n startMcpServer({\n projectRoot,\n viewerUrl,\n apiKey,\n searchApiKey: resolveSearchApiKey({ projectRoot, apiKey }, fileConfig),\n cloudApiKey,\n source,\n fileConfig,\n adapter,\n }).catch((error) => {\n console.error('Failed to start MCP server:', error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;AAMA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AAKjC,IAAI,KAAK,CAAC,MAAM,QAAQ;AACtB,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,WAAW;AAC5C,QAAM,WAAW,KAAK,MAAM,CAAC;AAE7B,MAAIA,eAAc,QAAQ,IAAI;AAC9B,MAAI;AAEJ,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,MAAM,SAAS,CAAC;AACtB,QAAI,QAAQ,oBAAoB,QAAQ,MAAM;AAC5C,MAAAA,eAAc,SAAS,EAAE,CAAC,KAAKA;AAAA,IACjC,WAAW,QAAQ,cAAc,QAAQ,MAAM;AAC7C,iBAAW,SAAS,EAAE,CAAC,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,IAChE,WAAW,QAAQ,YAAY,QAAQ,MAAM;AAC3C,cAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAejB;AACK,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,UAAQ,EAAE,aAAAA,cAAa,QAAiD,CAAC;AACzE,UAAQ,KAAK,CAAC;AAChB;AAKA,IAAI,cAAc,QAAQ,IAAI;AAC9B,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI,SACF;AACF,IAAI,gBAAgB;AACpB,IAAI;AACJ,IAAI,QAAQ;AAEZ,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,oBAAoB;AACrC,aAAS,KAAK,EAAE,CAAC;AAAA,EACnB,WAAW,QAAQ,mBAAmB;AACpC,kBAAc,KAAK,EAAE,CAAC;AAAA,EACxB,WAAW,QAAQ,YAAY;AAC7B,UAAM,OAAO,KAAK,EAAE,CAAC;AACrB,QACE,SAAS,UACT,SAAS,oBACT,SAAS,WACT,SAAS,YACT,SAAS,WACT;AACA,eAAS;AAAA,IACX,OAAO;AACL,cAAQ;AAAA,QACN,oBAAoB,IAAI;AAAA,MAC1B;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,WAAW,QAAQ,oBAAoB;AACrC,oBAAgB;AAAA,EAClB,WAAW,QAAQ,aAAa;AAC9B,mBAAe,KAAK,EAAE,CAAC;AAAA,EACzB,WAAW,QAAQ,WAAW;AAC5B,YAAQ;AAAA,EACV,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;AAAA;AAAA,CAuBf;AACG,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,IAAM,aAAa,eAAe,WAAW,KAAK;AAClD,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA,cAAc,oBAAoB,EAAE,aAAa,OAAO,GAAG,UAAU;AAAA,EACrE;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,EAAE,SAAS,KAAK,IAAI,mBAAmB,cAAc,UAAU;AAErE,IAAI,SAAS,WAAW;AACtB,UAAQ;AAAA,IACN;AAAA,EACF;AACF;AAGA,IAAI,eAAe;AACjB,GAAC,YAAY;AACX,UAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,qBAAY;AAGxD,UAAM,iBAAgC,CAAC,UAAU,UAAU,SAAS;AACpE,UAAM,UAAyB,eAC1B,aAAa,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,IAC1C;AAGJ,UAAM,eAAe,oBAAI,IAAI,CAAC,UAAU,UAAU,SAAS,CAAC;AAC5D,eAAW,OAAO,SAAS;AACzB,UAAI,CAAC,aAAa,IAAI,GAAG,GAAG;AAC1B,gBAAQ,MAAM,oBAAoB,GAAG,2CAA2C;AAChF,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAGA,UAAM,OAAO,MAAM,QAAQ,KAAK,WAAW;AAG3C,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,yBAAgB;AAC/C,UAAM,YAAY,YAAY,OAAO,QAAQ,MAAM;AAGnD,UAAM,QAAQ,mBAAmB;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,IAAI,2BAA2B;AAAA,IACzC,OAAO;AACL,cAAQ,IAAI,aAAa,MAAM,MAAM,iBAAiB;AACtD,iBAAW,QAAQ,OAAO;AACxB,gBAAQ,IAAI,KAAK,IAAI,EAAE;AAAA,MACzB;AAAA,IACF;AAEA,YAAQ,KAAK,CAAC;AAAA,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU;AACpB,YAAQ,MAAM,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AACzF,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH,OAAO;AAEL,iBAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,oBAAoB,EAAE,aAAa,OAAO,GAAG,UAAU;AAAA,IACrE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EAAE,MAAM,CAAC,UAAU;AAClB,YAAQ,MAAM,+BAA+B,KAAK;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;","names":["projectRoot"]}
1
+ {"version":3,"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { startMcpServer } from './server.js';\nimport { loadConfigFile } from './config.js';\nimport { resolveDataAdapter, resolveSearchApiKey } from './source-selection.js';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\n\n// ---------------------------------------------------------------------------\n// Subcommand: init\n// ---------------------------------------------------------------------------\nif (args[0] === 'init') {\n const { runInit } = await import('./init.js');\n const initArgs = args.slice(1);\n\n let projectRoot = process.cwd();\n let clients: string[] | undefined;\n\n for (let i = 0; i < initArgs.length; i++) {\n const arg = initArgs[i];\n if (arg === '--project-root' || arg === '-p') {\n projectRoot = initArgs[++i] ?? projectRoot;\n } else if (arg === '--client' || arg === '-c') {\n clients = (initArgs[++i] ?? '').split(',').map((s) => s.trim());\n } else if (arg === '--help' || arg === '-h') {\n console.log(`\nUsage: fragments-mcp init [options]\n\nWrite MCP server config for your AI editor(s).\nAuto-detects Claude Code, Cursor, VS Code, and Windsurf.\n\nOptions:\n -p, --project-root <path> Project root directory (default: cwd)\n -c, --client <list> Comma-separated clients: claude,cursor,vscode,windsurf\n (default: auto-detect)\n -h, --help Show this help message\n\nExamples:\n npx @fragments-sdk/mcp init\n npx @fragments-sdk/mcp init --client cursor,claude\n`);\n process.exit(0);\n }\n }\n\n runInit({ projectRoot, clients: clients as import('./init.js').Client[] });\n process.exit(0);\n}\n\n// ---------------------------------------------------------------------------\n// Main: MCP server mode\n// ---------------------------------------------------------------------------\nlet projectRoot = process.cwd();\nlet cloudApiKey: string | undefined;\nlet source: 'auto' | 'fragments-json' | 'cloud' | 'bundle' | 'extract' =\n 'auto';\nlet generateRules = false;\nlet rulesFormats: string | undefined;\nlet force = false;\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 === '--cloud-api-key') {\n cloudApiKey = args[++i];\n } else if (arg === '--source') {\n const next = args[++i];\n if (\n next === 'auto' ||\n next === 'fragments-json' ||\n next === 'cloud' ||\n next === 'bundle' ||\n next === 'extract'\n ) {\n source = next;\n } else {\n console.error(\n `Invalid source: \"${next}\". Valid values: auto, fragments-json, cloud, bundle, extract`,\n );\n process.exit(1);\n }\n } else if (arg === '--generate-rules') {\n generateRules = true;\n } else if (arg === '--formats') {\n rulesFormats = args[++i];\n } else if (arg === '--force') {\n force = true;\n } else if (arg === '--help' || arg === '-h') {\n console.log(`\nUsage: fragments-mcp [command] [options]\n\nStandalone MCP server for design system intelligence.\nProvides UI validation, deterministic repair, token suggestions, and Cloud findings.\n\nCommands:\n init Write MCP config for your AI editor(s)\n\nOptions:\n -p, --project-root <path> Project root directory (default: cwd)\n --cloud-api-key <key> Cloud API key for org-scoped catalog access\n --source <mode> Source override: auto, fragments-json, cloud, bundle, extract\n --generate-rules Generate IDE rules files and exit\n --formats <list> Comma-separated formats: cursor,claude,copilot (default: all)\n --force Overwrite existing rules files\n -h, --help Show this help message\n\nQuick start:\n npx @fragments-sdk/mcp init\n`);\n process.exit(0);\n }\n}\n\nconst fileConfig = loadConfigFile(projectRoot) ?? undefined;\nconst serverConfig = {\n projectRoot,\n searchApiKey: resolveSearchApiKey({ projectRoot }, fileConfig),\n cloudApiKey,\n source,\n fileConfig,\n};\nconst { adapter, mode } = resolveDataAdapter(serverConfig, fileConfig);\n\nif (mode === 'extract') {\n console.error(\n '[fragments-mcp] No fragments.json found — auto-extracting from source.',\n );\n}\n\n// --generate-rules: generate IDE rules and exit\nif (generateRules) {\n (async () => {\n const { generateRulesFiles } = await import('./rules.js');\n type RulesFormat = 'cursor' | 'claude' | 'copilot';\n\n const defaultFormats: RulesFormat[] = ['cursor', 'claude', 'copilot'];\n const formats: RulesFormat[] = rulesFormats\n ? (rulesFormats.split(',').map(f => f.trim()) as RulesFormat[])\n : defaultFormats;\n\n // Validate formats\n const validFormats = new Set(['cursor', 'claude', 'copilot']);\n for (const fmt of formats) {\n if (!validFormats.has(fmt)) {\n console.error(`Invalid format: \"${fmt}\". Valid formats: cursor, claude, copilot`);\n process.exit(1);\n }\n }\n\n // Load data using the same adapter\n const data = await adapter.load(projectRoot);\n\n // Resolve brand name from config\n const { BRAND } = await import('./constants.js');\n const brandName = fileConfig?.brand?.name ?? BRAND.name;\n\n // Generate rules\n const files = generateRulesFiles({\n data,\n brandName,\n outputDir: projectRoot,\n formats,\n force,\n });\n\n if (files.length === 0) {\n console.log('No rules files generated.');\n } else {\n console.log(`Generated ${files.length} rules file(s):`);\n for (const file of files) {\n console.log(` ${file}`);\n }\n }\n\n process.exit(0);\n })().catch((error) => {\n console.error('Failed to generate rules:', error instanceof Error ? error.message : error);\n process.exit(1);\n });\n} else {\n // Default: start MCP server\n startMcpServer({\n projectRoot,\n searchApiKey: resolveSearchApiKey({ projectRoot }, fileConfig),\n cloudApiKey,\n source,\n fileConfig,\n adapter,\n }).catch((error) => {\n console.error('Failed to start MCP server:', error);\n process.exit(1);\n });\n}\n"],"mappings":"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","names":["projectRoot"]}
@@ -1,11 +1,31 @@
1
- import {
2
- getGuidanceWhen,
3
- getGuidanceWhenNot
4
- } from "./chunk-YSRGQDEB.js";
5
-
6
1
  // src/rules.ts
7
2
  import { join } from "path";
8
3
  import { writeFileSync, mkdirSync, existsSync } from "fs";
4
+
5
+ // src/snapshot-helpers.ts
6
+ function getGuidanceWhen(component) {
7
+ if (component.guidance.when.length > 0) {
8
+ return component.guidance.when;
9
+ }
10
+ if (component.guidance.usageGuidance) {
11
+ return [component.guidance.usageGuidance];
12
+ }
13
+ if (component.guidance.dos.length > 0) {
14
+ return component.guidance.dos;
15
+ }
16
+ return [];
17
+ }
18
+ function getGuidanceWhenNot(component) {
19
+ if (component.guidance.whenNot.length > 0) {
20
+ return component.guidance.whenNot;
21
+ }
22
+ if (component.guidance.donts.length > 0) {
23
+ return component.guidance.donts;
24
+ }
25
+ return [];
26
+ }
27
+
28
+ // src/rules.ts
9
29
  function generateRulesFiles(options) {
10
30
  const { data, brandName, outputDir, formats, force = false } = options;
11
31
  const content = buildRulesContent(data, brandName);
@@ -49,6 +69,17 @@ function buildRulesContent(data, brandName) {
49
69
  `);
50
70
  sections.push(`Always use ${brandName} components and design tokens when building UI. Do not use raw HTML elements, Tailwind classes, or hardcoded CSS values when a design system component or token exists.
51
71
  `);
72
+ sections.push("## Agent Validation Loop\n");
73
+ sections.push("After generating or editing JSX, component props, class names, or CSS, call the Fragments MCP `govern` tool with the candidate UI spec before treating the work as complete.");
74
+ sections.push("If `govern` returns violations or a `nextAction` that asks for revision, call `validate_and_fix` with the same spec, apply any deterministic repair it returns, then call `govern` again.");
75
+ sections.push("Before writing a hardcoded CSS value, call `tokens.suggest` with the CSS property and value. Use the returned `cssValue` when a recommendation is present; keep the raw value only when the tool returns `noSuggestion: true`.\n");
76
+ sections.push("Example loop:");
77
+ sections.push("```text");
78
+ sections.push("1. Draft the UI spec for the component or screen.");
79
+ sections.push("2. govern({ spec })");
80
+ sections.push("3. If it fails: validate_and_fix({ spec, applyFixes: true })");
81
+ sections.push("4. Re-run govern({ spec: fixedSpec }) before finalizing.");
82
+ sections.push("```\n");
52
83
  const components = Object.values(data.components);
53
84
  if (components.length > 0) {
54
85
  sections.push("## Components\n");
@@ -109,4 +140,4 @@ function buildRulesContent(data, brandName) {
109
140
  export {
110
141
  generateRulesFiles
111
142
  };
112
- //# sourceMappingURL=chunk-VV2PJ75X.js.map
143
+ //# sourceMappingURL=chunk-WDQPNHZ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rules.ts","../src/snapshot-helpers.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { writeFileSync, mkdirSync, existsSync } from 'node:fs';\nimport type { McpComponent } from '@fragments-sdk/core';\nimport type { DesignSystemData } from './types.js';\nimport { getGuidanceWhen, getGuidanceWhenNot } from './snapshot-helpers.js';\n\nexport type RulesFormat = 'cursor' | 'claude' | 'copilot';\n\nexport interface RulesGeneratorOptions {\n data: DesignSystemData;\n brandName: string;\n outputDir: string;\n formats: RulesFormat[];\n /** Overwrite existing files (default: false — skip and warn) */\n force?: boolean;\n}\n\n/**\n * Generate IDE rules files from design system data.\n * Returns list of files written. Skips existing files unless force is true.\n */\nexport function generateRulesFiles(options: RulesGeneratorOptions): string[] {\n const { data, brandName, outputDir, formats, force = false } = options;\n const content = buildRulesContent(data, brandName);\n const files: string[] = [];\n\n for (const format of formats) {\n let filePath: string;\n let fileContent: string;\n\n switch (format) {\n case 'cursor':\n filePath = join(outputDir, '.cursorrules');\n fileContent = content;\n break;\n case 'claude':\n filePath = join(outputDir, 'CLAUDE.md');\n fileContent = `# CLAUDE.md\\n\\nThis file provides guidance to Claude Code when working with code in this repository.\\n\\n${content}`;\n break;\n case 'copilot': {\n const dir = join(outputDir, '.github');\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n filePath = join(dir, 'copilot-instructions.md');\n fileContent = content;\n break;\n }\n }\n\n if (!force && existsSync(filePath)) {\n console.error(` Skipped ${filePath} (already exists, use --force to overwrite)`);\n continue;\n }\n\n writeFileSync(filePath, fileContent, 'utf-8');\n files.push(filePath);\n }\n\n return files;\n}\n\nfunction buildRulesContent(data: DesignSystemData, brandName: string): string {\n const sections: string[] = [];\n\n // Header\n sections.push(`# ${brandName} Design System Rules\\n`);\n sections.push(`Always use ${brandName} components and design tokens when building UI. Do not use raw HTML elements, Tailwind classes, or hardcoded CSS values when a design system component or token exists.\\n`);\n\n sections.push('## Agent Validation Loop\\n');\n sections.push('After generating or editing JSX, component props, class names, or CSS, call the Fragments MCP `govern` tool with the candidate UI spec before treating the work as complete.');\n sections.push('If `govern` returns violations or a `nextAction` that asks for revision, call `validate_and_fix` with the same spec, apply any deterministic repair it returns, then call `govern` again.');\n sections.push('Before writing a hardcoded CSS value, call `tokens.suggest` with the CSS property and value. Use the returned `cssValue` when a recommendation is present; keep the raw value only when the tool returns `noSuggestion: true`.\\n');\n sections.push('Example loop:');\n sections.push('```text');\n sections.push('1. Draft the UI spec for the component or screen.');\n sections.push('2. govern({ spec })');\n sections.push('3. If it fails: validate_and_fix({ spec, applyFixes: true })');\n sections.push('4. Re-run govern({ spec: fixedSpec }) before finalizing.');\n sections.push('```\\n');\n\n // Component inventory\n const components: McpComponent[] = Object.values(data.components);\n if (components.length > 0) {\n sections.push('## Components\\n');\n const byCategory = new Map<string, string[]>();\n for (const c of components) {\n const cat = c.category || 'uncategorized';\n if (!byCategory.has(cat)) byCategory.set(cat, []);\n byCategory.get(cat)!.push(c.name);\n }\n for (const [cat, names] of byCategory) {\n sections.push(`### ${cat}\\n`);\n sections.push(names.map(n => `- ${n}`).join('\\n'));\n sections.push('');\n }\n }\n\n // Import pattern\n if (data.defaultPackageName) {\n sections.push('## Imports\\n');\n sections.push(`Import components from \\`${data.defaultPackageName}\\`:\\n`);\n sections.push('```typescript');\n const exampleNames = components.slice(0, 3).map(c => c.name);\n sections.push(`import { ${exampleNames.join(', ')} } from '${data.defaultPackageName}';`);\n sections.push('```\\n');\n }\n\n // Tokens\n if (data.tokens && data.tokens.total > 0) {\n sections.push('## Design Tokens\\n');\n sections.push(`Prefix: \\`${data.tokens.prefix}\\`. Use \\`var(--token-name)\\` in CSS/styles.\\n`);\n sections.push('Available categories:');\n for (const [cat, tokens] of Object.entries(data.tokens.categories) as Array<\n [string, typeof data.tokens.categories[string]]\n >) {\n sections.push(`- **${cat}** (${tokens.length} tokens)`);\n }\n sections.push('');\n }\n\n // Usage guidelines (aggregated from all components)\n const allDos: string[] = [];\n const allDonts: string[] = [];\n for (const c of components) {\n allDos.push(...getGuidanceWhen(c).slice(0, 1).map(w => `${c.name}: ${w}`));\n allDonts.push(...getGuidanceWhenNot(c).slice(0, 1).map(w => `${c.name}: ${w}`));\n }\n if (allDos.length > 0 || allDonts.length > 0) {\n sections.push('## Usage Guidelines\\n');\n if (allDos.length > 0) {\n sections.push('**Do:**');\n sections.push(allDos.slice(0, 10).map(d => `- ${d}`).join('\\n'));\n sections.push('');\n }\n if (allDonts.length > 0) {\n sections.push('**Don\\'t:**');\n sections.push(allDonts.slice(0, 10).map(d => `- ${d}`).join('\\n'));\n sections.push('');\n }\n }\n\n return sections.join('\\n');\n}\n","import type { McpBlock, McpComponent, McpSnapshot } from '@fragments-sdk/core';\n\nexport type ResolvedMcpComponent = McpComponent & {\n isCanonical?: boolean;\n};\n\nexport function listComponents(\n snapshot: Pick<McpSnapshot, 'components'>,\n): ResolvedMcpComponent[] {\n return Object.values(snapshot.components);\n}\n\nexport function componentNames(snapshot: Pick<McpSnapshot, 'components'>): string[] {\n return listComponents(snapshot).map((component) => component.name);\n}\n\nexport function listBlocks(snapshot: Pick<McpSnapshot, 'blocks'>): McpBlock[] {\n return Object.values(snapshot.blocks ?? {});\n}\n\nexport function findComponentByName(\n snapshot: Pick<McpSnapshot, 'components'>,\n name: string,\n): ResolvedMcpComponent | undefined {\n const query = name.toLowerCase();\n return listComponents(snapshot).find(\n (component) => component.name.toLowerCase() === query,\n );\n}\n\nfunction normalizeComponentRef(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction buildCompoundComponent(\n parent: ResolvedMcpComponent,\n childName: string,\n): ResolvedMcpComponent | undefined {\n const child = parent.compoundChildren.find(\n (entry) => normalizeComponentRef(entry.name) === normalizeComponentRef(childName),\n );\n if (!child) return undefined;\n\n const fullName = child.name.includes('.')\n ? child.name\n : `${parent.name}.${child.name}`;\n\n return {\n ...parent,\n id: child.componentId ?? `${parent.id}::${fullName}`,\n name: fullName,\n description: child.description ?? '',\n props: {},\n propsSummary: [],\n examples: [],\n relations: [],\n compoundChildren: [],\n publicRef: child.componentId ?? fullName,\n publicSlug: null,\n parentComponentId: parent.id,\n parentComponentName: parent.name,\n };\n}\n\nexport function findComponent(\n snapshot: Pick<McpSnapshot, 'components'>,\n reference: string,\n): ResolvedMcpComponent | undefined {\n const query = normalizeComponentRef(reference);\n const components = listComponents(snapshot);\n\n const exact = components.find((component) => {\n return (\n normalizeComponentRef(component.name) === query ||\n normalizeComponentRef(component.id) === query ||\n normalizeComponentRef(component.publicRef ?? '') === query\n );\n });\n if (exact) return exact;\n\n if (reference.includes('.')) {\n const [parentName, ...childParts] = reference.split('.');\n const childName = childParts.join('.');\n const parent = components.find((component) => {\n return (\n normalizeComponentRef(component.name) === normalizeComponentRef(parentName) ||\n normalizeComponentRef(component.publicRef ?? '') ===\n normalizeComponentRef(parentName) ||\n normalizeComponentRef(component.id) === normalizeComponentRef(parentName)\n );\n });\n\n if (parent) {\n const child = buildCompoundComponent(parent, childName);\n if (child) return child;\n }\n }\n\n return undefined;\n}\n\nexport function getGuidanceWhen(component: ResolvedMcpComponent): string[] {\n if (component.guidance.when.length > 0) {\n return component.guidance.when;\n }\n if (component.guidance.usageGuidance) {\n return [component.guidance.usageGuidance];\n }\n if (component.guidance.dos.length > 0) {\n return component.guidance.dos;\n }\n return [];\n}\n\nexport function getGuidanceWhenNot(component: ResolvedMcpComponent): string[] {\n if (component.guidance.whenNot.length > 0) {\n return component.guidance.whenNot;\n }\n if (component.guidance.donts.length > 0) {\n return component.guidance.donts;\n }\n return [];\n}\n"],"mappings":"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","names":[]}