@fragments-sdk/mcp 1.0.1 → 1.0.3

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/dist/index.d.ts CHANGED
@@ -23,9 +23,49 @@ interface McpPrimitive {
23
23
  donts?: string[];
24
24
  examples?: unknown[];
25
25
  }
26
+ interface McpToken {
27
+ name: string;
28
+ value: string;
29
+ category: string;
30
+ path?: string[];
31
+ originalName?: string;
32
+ tier?: string;
33
+ type?: string;
34
+ description?: string;
35
+ }
36
+ interface McpCatalogMetadata {
37
+ org?: {
38
+ id?: string;
39
+ name?: string;
40
+ slug?: string;
41
+ };
42
+ project?: {
43
+ id?: string;
44
+ name?: string;
45
+ slug?: string;
46
+ };
47
+ sourceBinding?: {
48
+ bindingId?: string;
49
+ projectId?: string;
50
+ projectName?: string;
51
+ projectSlug?: string;
52
+ repoFullName?: string;
53
+ path?: string | null;
54
+ resolution?: string;
55
+ };
56
+ designSystem?: {
57
+ name?: string;
58
+ packageName?: string | null;
59
+ importPath?: string | null;
60
+ };
61
+ revision?: string;
62
+ updatedAt?: number;
63
+ }
26
64
 
27
65
  interface DataProvider {
28
66
  listPrimitives(): Promise<McpPrimitive[]>;
67
+ listTokens(): Promise<McpToken[]>;
68
+ getCatalogMetadata?(): Promise<McpCatalogMetadata | null>;
29
69
  conform(input: ConformInput): Promise<ConformResult>;
30
70
  }
31
71
  interface SamplingInputResponse {
@@ -158,7 +198,9 @@ declare class FragmentsMcpServer {
158
198
  constructor(options: FragmentsMcpServerOptions);
159
199
  handleJsonRpc(request: JsonRpcRequest): Promise<JsonRpcResponse>;
160
200
  private dispatch;
201
+ private initialize;
161
202
  private discover;
203
+ private serverDescriptor;
162
204
  private callTool;
163
205
  private readResource;
164
206
  private getTask;
@@ -1437,7 +1479,8 @@ declare const fixturePrimitives: McpPrimitive[];
1437
1479
  declare const fixtureConformResult: ConformResult;
1438
1480
  declare function createFixtureProvider(args?: {
1439
1481
  primitives?: McpPrimitive[];
1482
+ tokens?: McpToken[];
1440
1483
  conform?: ConformResult;
1441
1484
  }): DataProvider;
1442
1485
 
1443
- export { type CallToolRequest, type CallToolResult, type ClientCapabilities, type DataProvider, FragmentsMcpServer, type Implementation, type InputRequiredToolResult, type JSONRPCMessage, JSONRPC_VERSION, type JsonObject, type JsonRpcRequest, type JsonRpcResponse, LATEST_PROTOCOL_VERSION, type ListResourcesResult, type ListToolsResult, MCP_APP_RESOURCES, MCP_TOOLS, type McpPrimitive, McpProtocolError, MemoryTaskStore, type ReadResourceRequest, type ReadResourceResult, type RequestMetaObject, type Resource, type ResourceDefinition, type ServerCapabilities, type TaskRecord, type TaskStore, type Tool, type ToolDefinition, type ToolResult, type WorkflowTaskInput, createFixtureProvider, fixtureConformResult, fixturePrimitives, getResource, getTool, handleMcpHttpRequest, handleMcpJsonRpc, listResources, listToolDescriptors };
1486
+ export { type CallToolRequest, type CallToolResult, type ClientCapabilities, type DataProvider, FragmentsMcpServer, type Implementation, type InputRequiredToolResult, type JSONRPCMessage, JSONRPC_VERSION, type JsonObject, type JsonRpcRequest, type JsonRpcResponse, LATEST_PROTOCOL_VERSION, type ListResourcesResult, type ListToolsResult, MCP_APP_RESOURCES, MCP_TOOLS, type McpCatalogMetadata, type McpPrimitive, McpProtocolError, type McpToken, MemoryTaskStore, type ReadResourceRequest, type ReadResourceResult, type RequestMetaObject, type Resource, type ResourceDefinition, type ServerCapabilities, type TaskRecord, type TaskStore, type Tool, type ToolDefinition, type ToolResult, type WorkflowTaskInput, createFixtureProvider, fixtureConformResult, fixturePrimitives, getResource, getTool, handleMcpHttpRequest, handleMcpJsonRpc, listResources, listToolDescriptors };
package/dist/index.js CHANGED
@@ -72,19 +72,34 @@ var LATEST_PROTOCOL_VERSION = "DRAFT-2026-v1";
72
72
  var JSONRPC_VERSION = "2.0";
73
73
 
74
74
  // src/tools/primitives.ts
75
+ var variantSchema = {
76
+ type: "object",
77
+ properties: {
78
+ prop: { type: "string" },
79
+ values: { type: "array", items: { type: "string" } }
80
+ },
81
+ required: ["prop", "values"],
82
+ additionalProperties: false
83
+ };
75
84
  var primitiveSchema = {
76
85
  type: "object",
77
86
  properties: {
78
87
  id: { type: "string" },
79
88
  name: { type: "string" },
80
89
  primitiveName: { type: "string" },
90
+ category: { type: ["string", "null"] },
91
+ status: { type: ["string", "null"] },
81
92
  filePath: { type: ["string", "null"] },
82
93
  importPath: { type: ["string", "null"] },
83
94
  packageName: { type: ["string", "null"] },
84
95
  description: { type: "string" },
85
96
  usageGuidance: { type: "string" },
86
97
  props: { type: "object" },
98
+ variants: { type: "array", items: variantSchema },
87
99
  examples: { type: "array" },
100
+ dos: { type: "array", items: { type: "string" } },
101
+ donts: { type: "array", items: { type: "string" } },
102
+ parentComponentName: { type: ["string", "null"] },
88
103
  sourceRepoFullName: { type: ["string", "null"] }
89
104
  },
90
105
  required: ["id", "name", "primitiveName", "filePath", "importPath"],
@@ -116,11 +131,12 @@ var primitiveTools = [
116
131
  },
117
132
  async call(_args, context) {
118
133
  const primitives = await context.provider.listPrimitives();
119
- return primitiveResult(primitives);
134
+ const metadata = await context.provider.getCatalogMetadata?.();
135
+ return primitiveResult(primitives, metadata ?? null);
120
136
  }
121
137
  }
122
138
  ];
123
- function primitiveResult(primitives) {
139
+ function primitiveResult(primitives, metadata) {
124
140
  return {
125
141
  content: [
126
142
  {
@@ -131,24 +147,132 @@ function primitiveResult(primitives) {
131
147
  structuredContent: {
132
148
  primitiveCount: primitives.length,
133
149
  primitives: primitives.map(normalizePrimitive)
150
+ },
151
+ _meta: {
152
+ catalog: metadata
134
153
  }
135
154
  };
136
155
  }
137
156
  function normalizePrimitive(primitive) {
157
+ const props = primitive.props ?? {};
138
158
  return {
139
159
  id: primitive.id,
140
160
  name: primitive.name,
141
161
  primitiveName: primitive.primitiveName ?? primitive.name,
162
+ category: primitive.category ?? null,
163
+ status: primitive.status ?? null,
142
164
  filePath: primitive.filePath ?? null,
143
165
  importPath: primitive.importPath ?? null,
144
166
  packageName: primitive.packageName ?? null,
145
167
  description: primitive.description ?? "",
146
168
  usageGuidance: primitive.usageGuidance ?? "",
147
- props: primitive.props ?? {},
169
+ props,
170
+ variants: deriveVariants(props),
148
171
  examples: primitive.examples ?? [],
172
+ dos: primitive.dos ?? [],
173
+ donts: primitive.donts ?? [],
174
+ parentComponentName: primitive.parentComponentName ?? null,
149
175
  sourceRepoFullName: primitive.sourceRepoFullName ?? null
150
176
  };
151
177
  }
178
+ function deriveVariants(props) {
179
+ const variants = [];
180
+ for (const [prop, def] of Object.entries(props)) {
181
+ if (typeof def !== "object" || def === null) continue;
182
+ const type = def.type;
183
+ if (typeof type !== "string") continue;
184
+ const values = parseLiteralUnion(type);
185
+ if (values.length >= 2) variants.push({ prop, values });
186
+ }
187
+ return variants;
188
+ }
189
+ function parseLiteralUnion(type) {
190
+ const segments = type.split("|").map((segment) => segment.trim());
191
+ const values = [];
192
+ for (const segment of segments) {
193
+ const match = /^(["'])(.+)\1$/.exec(segment);
194
+ if (!match) return [];
195
+ values.push(match[2]);
196
+ }
197
+ return values;
198
+ }
199
+
200
+ // src/tools/tokens.ts
201
+ var tokenSchema = {
202
+ type: "object",
203
+ properties: {
204
+ name: { type: "string" },
205
+ value: { type: "string" },
206
+ category: { type: "string" },
207
+ path: { type: "array", items: { type: "string" } },
208
+ originalName: { type: "string" },
209
+ tier: { type: "string" },
210
+ type: { type: "string" },
211
+ description: { type: "string" }
212
+ },
213
+ required: ["name", "value", "category"],
214
+ additionalProperties: false
215
+ };
216
+ var tokenTools = [
217
+ {
218
+ name: "design_system/list_tokens",
219
+ title: "List Tokens",
220
+ description: "List the design-system tokens Fragments Cloud has indexed for this project. Returns factual token names, values, categories, and source metadata.",
221
+ inputSchema: {
222
+ type: "object",
223
+ properties: {},
224
+ additionalProperties: false,
225
+ maxProperties: 0
226
+ },
227
+ outputSchema: {
228
+ type: "object",
229
+ properties: {
230
+ tokenCount: { type: "integer" },
231
+ tokens: { type: "array", items: tokenSchema }
232
+ },
233
+ required: ["tokenCount", "tokens"],
234
+ additionalProperties: false
235
+ },
236
+ annotations: {
237
+ readOnlyHint: true,
238
+ idempotentHint: true
239
+ },
240
+ async call(_args, context) {
241
+ const tokens = await context.provider.listTokens();
242
+ const metadata = await context.provider.getCatalogMetadata?.();
243
+ return tokenResult(tokens, metadata ?? null);
244
+ }
245
+ }
246
+ ];
247
+ function tokenResult(tokens, metadata) {
248
+ return {
249
+ content: [
250
+ {
251
+ type: "text",
252
+ text: tokens.length === 0 ? "No design-system tokens have been indexed in Fragments Cloud yet." : `Found ${tokens.length} design-system tokens.`
253
+ }
254
+ ],
255
+ structuredContent: {
256
+ tokenCount: tokens.length,
257
+ tokens: tokens.map(normalizeToken)
258
+ },
259
+ _meta: {
260
+ catalog: metadata
261
+ }
262
+ };
263
+ }
264
+ function normalizeToken(token) {
265
+ return {
266
+ name: token.name,
267
+ value: token.value,
268
+ category: token.category,
269
+ path: token.path ?? [],
270
+ ...token.originalName ? { originalName: token.originalName } : {},
271
+ ...token.tier ? { tier: token.tier } : {},
272
+ ...token.type ? { type: token.type } : {},
273
+ ...token.description ? { description: token.description } : {}
274
+ };
275
+ }
152
276
 
153
277
  // src/tools/conform.ts
154
278
  var locationSchema = {
@@ -186,7 +310,12 @@ var suggestionSchema = {
186
310
  properties: {
187
311
  kind: {
188
312
  type: "string",
189
- enum: ["map-prop-value", "choose-among-alternates", "review-low-confidence"]
313
+ enum: [
314
+ "map-prop-value",
315
+ "choose-among-alternates",
316
+ "review-low-confidence",
317
+ "flag-accessibility"
318
+ ]
190
319
  },
191
320
  message: { type: "string" },
192
321
  canonical: { type: "string" },
@@ -212,7 +341,7 @@ var conformTools = [
212
341
  {
213
342
  name: "design_system/conform",
214
343
  title: "Conform to Design System",
215
- description: "Rewrite a snippet of UI code so it matches THIS project's design system. Swaps raw HTML elements and components from other libraries (Material UI, shadcn/ui, Chakra, or custom) to the project's own components and import paths, replaces hardcoded colors/spacing/radii with the project's design tokens, and flags accessibility gaps. Call this whenever the user says 'fix this', 'make this match our design system', 'use our components here', right after pasting markup from elsewhere, or before committing UI you wrote. Input is the code itself \u2014 no repo access needed. Returns a conformed version plus a list of changes with rationale; ambiguous cross-library prop changes come back as suggestions for you to apply.",
344
+ description: "Rewrite a snippet of UI code so it matches THIS project's design system. Swaps raw HTML elements and components from other libraries (Material UI, shadcn/ui, Chakra, or custom) to the project's own components and import paths, renames cross-library props and translates their values when the mapping is confirmed, and replaces hardcoded colors/spacing/radii with the project's design tokens. Call this whenever the user says 'fix this', 'make this match our design system', 'use our components here', right after pasting markup from elsewhere, or before committing UI you wrote. Input is the code itself \u2014 no repo access needed. Returns a conformed version plus a list of changes with rationale. Ambiguous prop changes and accessibility gaps (e.g. a button or image with no accessible name) come back as suggestions for you to apply \u2014 they are never auto-rewritten.",
216
345
  inputSchema: {
217
346
  type: "object",
218
347
  properties: {
@@ -250,7 +379,17 @@ var conformTools = [
250
379
  },
251
380
  changes: { type: "array", items: changeSchema },
252
381
  suggestions: { type: "array", items: suggestionSchema },
253
- unresolved: { type: "array", items: unresolvedSchema }
382
+ unresolved: { type: "array", items: unresolvedSchema },
383
+ coverage: {
384
+ type: "object",
385
+ properties: {
386
+ tokens: { type: "integer" },
387
+ components: { type: "integer" },
388
+ evaluated: { type: "boolean" }
389
+ },
390
+ required: ["tokens", "components", "evaluated"],
391
+ additionalProperties: false
392
+ }
254
393
  },
255
394
  required: ["conformed", "changed", "changes", "suggestions"],
256
395
  additionalProperties: false
@@ -290,7 +429,8 @@ function conformResult(result) {
290
429
  } : {},
291
430
  changes: result.changes,
292
431
  suggestions: result.suggestions,
293
- unresolved: result.unresolved
432
+ unresolved: result.unresolved,
433
+ ...result.coverage ? { coverage: result.coverage } : {}
294
434
  }
295
435
  };
296
436
  }
@@ -325,6 +465,16 @@ var residualSchema = {
325
465
  },
326
466
  additionalProperties: true
327
467
  };
468
+ var coverageSchema = {
469
+ type: "object",
470
+ properties: {
471
+ tokens: { type: "integer" },
472
+ components: { type: "integer" },
473
+ evaluated: { type: "boolean" }
474
+ },
475
+ required: ["tokens", "components", "evaluated"],
476
+ additionalProperties: false
477
+ };
328
478
  var proveCompliantOutputSchema = {
329
479
  type: "object",
330
480
  properties: {
@@ -341,7 +491,8 @@ var proveCompliantOutputSchema = {
341
491
  changes: { type: "array", items: { type: "object", additionalProperties: true } },
342
492
  suggestions: { type: "array", items: residualSchema },
343
493
  unresolved: { type: "array", items: residualSchema },
344
- passHistory: { type: "array", items: { type: "object", additionalProperties: true } }
494
+ passHistory: { type: "array", items: { type: "object", additionalProperties: true } },
495
+ coverage: coverageSchema
345
496
  },
346
497
  required: [
347
498
  "provedCompliant",
@@ -459,12 +610,13 @@ async function proveCompliant(args, context) {
459
610
  });
460
611
  input.corrections += result.changes.length;
461
612
  if (issues === 0) {
613
+ const evaluated = result.coverage?.evaluated !== false;
462
614
  const final2 = completeResult({
463
615
  state: input,
464
616
  result,
465
617
  originalCode: args.code,
466
- provedCompliant: true,
467
- reason: `0 findings after ${pass} pass${pass === 1 ? "" : "es"}.`
618
+ provedCompliant: evaluated,
619
+ reason: evaluated ? `0 findings after ${pass} pass${pass === 1 ? "" : "es"}.` : "the design-system catalog has no tokens or components, so compliance could not be evaluated."
468
620
  });
469
621
  return finish(context, input.taskId, final2);
470
622
  }
@@ -609,7 +761,8 @@ function completeResult(args) {
609
761
  changes: args.result.changes,
610
762
  suggestions: args.result.suggestions,
611
763
  unresolved: args.result.unresolved,
612
- passHistory: args.state.passHistory
764
+ passHistory: args.state.passHistory,
765
+ ...args.result.coverage ? { coverage: args.result.coverage } : {}
613
766
  }
614
767
  };
615
768
  }
@@ -705,6 +858,7 @@ function emptyConformResult(code) {
705
858
  var MAX_SCHEMA_DEPTH = 16;
706
859
  var MCP_TOOLS = [
707
860
  ...primitiveTools,
861
+ ...tokenTools,
708
862
  ...conformTools,
709
863
  ...proveCompliantTools
710
864
  ];
@@ -773,7 +927,14 @@ var FragmentsMcpServer = class {
773
927
  }
774
928
  async dispatch(request) {
775
929
  const params = request.params ?? {};
930
+ if (request.method.startsWith("notifications/")) {
931
+ return void 0;
932
+ }
776
933
  switch (request.method) {
934
+ case "initialize":
935
+ return this.initialize(params);
936
+ case "ping":
937
+ return {};
777
938
  case "server/discover":
778
939
  return this.discover();
779
940
  case "tools/list":
@@ -794,7 +955,17 @@ var FragmentsMcpServer = class {
794
955
  throw new McpProtocolError(-32601, `Unknown MCP method ${request.method}`);
795
956
  }
796
957
  }
958
+ initialize(params) {
959
+ const requested = params.protocolVersion;
960
+ return {
961
+ ...this.serverDescriptor(),
962
+ protocolVersion: typeof requested === "string" ? requested : LATEST_PROTOCOL_VERSION
963
+ };
964
+ }
797
965
  discover() {
966
+ return this.serverDescriptor();
967
+ }
968
+ serverDescriptor() {
798
969
  return {
799
970
  protocolVersion: LATEST_PROTOCOL_VERSION,
800
971
  serverInfo: {
@@ -873,6 +1044,20 @@ async function handleMcpHttpRequest(request, options) {
873
1044
  status: 400
874
1045
  });
875
1046
  }
1047
+ const isNotification = body.id === void 0 || typeof body.method === "string" && body.method.startsWith("notifications/");
1048
+ if (isNotification) {
1049
+ try {
1050
+ await handleMcpJsonRpc(body, options);
1051
+ } catch {
1052
+ }
1053
+ return new Response(null, {
1054
+ status: 202,
1055
+ headers: {
1056
+ "Cache-Control": "no-store",
1057
+ "Mcp-Protocol-Version": LATEST_PROTOCOL_VERSION
1058
+ }
1059
+ });
1060
+ }
876
1061
  const response = await handleMcpJsonRpc(body, options);
877
1062
  return Response.json(response, {
878
1063
  status: "error" in response ? 400 : 200,
@@ -1035,6 +1220,27 @@ var fixturePrimitives = [
1035
1220
  }
1036
1221
  }
1037
1222
  ];
1223
+ var fixtureTokens = [
1224
+ {
1225
+ name: "color.brand.primary",
1226
+ originalName: "--brand-600",
1227
+ value: "#2563eb",
1228
+ category: "color",
1229
+ path: ["color", "brand", "primary"],
1230
+ tier: "canonical",
1231
+ type: "color",
1232
+ description: "Primary brand color."
1233
+ },
1234
+ {
1235
+ name: "space.4",
1236
+ originalName: "--space-4",
1237
+ value: "16px",
1238
+ category: "spacing",
1239
+ path: ["space", "4"],
1240
+ tier: "canonical",
1241
+ type: "dimension"
1242
+ }
1243
+ ];
1038
1244
  var fixtureConformResult = {
1039
1245
  conformed: 'import { Button } from "@fixture/ui";\n\n<Button variant="primary" style={{ padding: "var(--space-4)" }}>Send</Button>',
1040
1246
  changed: true,
@@ -1092,11 +1298,15 @@ var fixtureConformResult = {
1092
1298
  };
1093
1299
  function createFixtureProvider(args) {
1094
1300
  const primitives = args?.primitives ?? fixturePrimitives;
1301
+ const tokens = args?.tokens ?? fixtureTokens;
1095
1302
  const conformResult2 = args?.conform ?? fixtureConformResult;
1096
1303
  return {
1097
1304
  async listPrimitives() {
1098
1305
  return primitives;
1099
1306
  },
1307
+ async listTokens() {
1308
+ return tokens;
1309
+ },
1100
1310
  async conform() {
1101
1311
  return conformResult2;
1102
1312
  }