@fragments-sdk/mcp 0.10.1 → 1.0.2

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.js CHANGED
@@ -1,223 +1,1230 @@
1
- import {
2
- AutoExtractionAdapter,
3
- BUILTIN_TOOLS,
4
- CORE_TOOLS,
5
- FragmentsJsonAdapter,
6
- INFRA_TOOLS,
7
- ToolRegistry,
8
- VIEWER_TOOLS,
9
- blockFromCompiledBlock,
10
- buildCapabilities,
11
- componentFromCompiledFragment,
12
- createMcpServer,
13
- createSandboxServer,
14
- executeWithMiddleware,
15
- loadConfigFile,
16
- startMcpServer,
17
- telemetryMiddleware,
18
- tokensFromCompiledTokenData,
19
- validateSnapshot
20
- } from "./chunk-KGFM5SCE.js";
21
- import {
22
- generateRulesFiles
23
- } from "./chunk-WDQPNHZ2.js";
24
- import "./chunk-7D4SUZUM.js";
1
+ // src/apps/resources.ts
2
+ var MCP_APP_RESOURCES = [];
3
+ function getResource(_uri) {
4
+ return null;
5
+ }
6
+ function listResources() {
7
+ return [];
8
+ }
25
9
 
26
- // src/adapters/custom-json.ts
27
- import { readFile } from "fs/promises";
28
- import { existsSync } from "fs";
29
- import { mcpSnapshotSchema } from "@fragments-sdk/core";
30
- var CustomJsonAdapter = class {
31
- constructor(filePath) {
32
- this.filePath = filePath;
33
- }
34
- filePath;
35
- name = "custom-json";
36
- discover() {
37
- return existsSync(this.filePath) ? [this.filePath] : [];
38
- }
39
- async load(_projectRoot = "") {
40
- if (!existsSync(this.filePath)) {
41
- throw new Error(`Custom data file not found: ${this.filePath}`);
42
- }
43
- const content = await readFile(this.filePath, "utf-8");
44
- const raw = JSON.parse(content);
45
- if (raw.schemaVersion === 1 && typeof raw.sourceType === "string" && raw.components && typeof raw.components === "object") {
46
- const snapshot2 = mcpSnapshotSchema.parse(raw);
47
- return {
48
- snapshot: snapshot2,
49
- components: snapshot2.components,
50
- blocks: snapshot2.blocks,
51
- tokens: snapshot2.tokens,
52
- graph: snapshot2.graph,
53
- performanceSummary: snapshot2.performanceSummary,
54
- packageMap: snapshot2.packageMap,
55
- defaultPackageName: snapshot2.defaultPackageName,
56
- capabilities: new Set(snapshot2.capabilities)
10
+ // src/tasks/store.ts
11
+ var nextTaskId = 1;
12
+ function createTaskId() {
13
+ const id = `task_${String(nextTaskId).padStart(6, "0")}`;
14
+ nextTaskId += 1;
15
+ return id;
16
+ }
17
+ var MemoryTaskStore = class {
18
+ tasks = /* @__PURE__ */ new Map();
19
+ async create(input) {
20
+ const now = Date.now();
21
+ const result = input.result ?? completedWorkflowResult(input.workflow?.kind);
22
+ const task = {
23
+ taskId: createTaskId(),
24
+ title: input.title,
25
+ status: result ? "completed" : "working",
26
+ createdAt: now,
27
+ updatedAt: now,
28
+ ttlMs: input.ttlMs ?? 15 * 60 * 1e3,
29
+ pollIntervalMs: input.pollIntervalMs ?? 1e3,
30
+ result,
31
+ progress: result ? { current: 1, total: 1, message: "Completed" } : { current: 0, total: 1, message: "Queued" }
32
+ };
33
+ this.tasks.set(task.taskId, task);
34
+ return task;
35
+ }
36
+ async get(taskId) {
37
+ return this.tasks.get(taskId) ?? null;
38
+ }
39
+ async update(taskId, patch) {
40
+ const existing = this.tasks.get(taskId);
41
+ if (!existing) return null;
42
+ const next = {
43
+ ...existing,
44
+ ...patch,
45
+ taskId,
46
+ updatedAt: Date.now()
47
+ };
48
+ this.tasks.set(taskId, next);
49
+ return next;
50
+ }
51
+ async cancel(taskId) {
52
+ return this.update(taskId, {
53
+ status: "cancelled",
54
+ progress: { message: "Cancellation requested" }
55
+ });
56
+ }
57
+ };
58
+ function completedWorkflowResult(kind) {
59
+ if (!kind) return void 0;
60
+ return {
61
+ content: [{ type: "text", text: `${kind} queued.` }],
62
+ structuredContent: {
63
+ kind,
64
+ status: "queued",
65
+ message: "Fragments accepted the workflow request. The hosted runtime attaches concrete job details."
66
+ }
67
+ };
68
+ }
69
+
70
+ // src/spec/generated/schema.ts
71
+ var LATEST_PROTOCOL_VERSION = "DRAFT-2026-v1";
72
+ var JSONRPC_VERSION = "2.0";
73
+
74
+ // src/tools/primitives.ts
75
+ var primitiveSchema = {
76
+ type: "object",
77
+ properties: {
78
+ id: { type: "string" },
79
+ name: { type: "string" },
80
+ primitiveName: { type: "string" },
81
+ filePath: { type: ["string", "null"] },
82
+ importPath: { type: ["string", "null"] },
83
+ packageName: { type: ["string", "null"] },
84
+ description: { type: "string" },
85
+ usageGuidance: { type: "string" },
86
+ props: { type: "object" },
87
+ examples: { type: "array" },
88
+ sourceRepoFullName: { type: ["string", "null"] }
89
+ },
90
+ required: ["id", "name", "primitiveName", "filePath", "importPath"],
91
+ additionalProperties: true
92
+ };
93
+ var primitiveTools = [
94
+ {
95
+ name: "design_system/list_primitives",
96
+ title: "List Primitives",
97
+ description: "List the design-system primitives curated in Fragments Cloud. Returns only reviewed primitives with their source file paths.",
98
+ inputSchema: {
99
+ type: "object",
100
+ properties: {},
101
+ additionalProperties: false,
102
+ maxProperties: 0
103
+ },
104
+ outputSchema: {
105
+ type: "object",
106
+ properties: {
107
+ primitiveCount: { type: "integer" },
108
+ primitives: { type: "array", items: primitiveSchema }
109
+ },
110
+ required: ["primitiveCount", "primitives"],
111
+ additionalProperties: false
112
+ },
113
+ annotations: {
114
+ readOnlyHint: true,
115
+ idempotentHint: true
116
+ },
117
+ async call(_args, context) {
118
+ const primitives = await context.provider.listPrimitives();
119
+ const metadata = await context.provider.getCatalogMetadata?.();
120
+ return primitiveResult(primitives, metadata ?? null);
121
+ }
122
+ }
123
+ ];
124
+ function primitiveResult(primitives, metadata) {
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: primitives.length === 0 ? "No primitives have been selected in Fragments Cloud yet." : `Found ${primitives.length} primitives.`
130
+ }
131
+ ],
132
+ structuredContent: {
133
+ primitiveCount: primitives.length,
134
+ primitives: primitives.map(normalizePrimitive)
135
+ },
136
+ _meta: {
137
+ catalog: metadata
138
+ }
139
+ };
140
+ }
141
+ function normalizePrimitive(primitive) {
142
+ return {
143
+ id: primitive.id,
144
+ name: primitive.name,
145
+ primitiveName: primitive.primitiveName ?? primitive.name,
146
+ filePath: primitive.filePath ?? null,
147
+ importPath: primitive.importPath ?? null,
148
+ packageName: primitive.packageName ?? null,
149
+ description: primitive.description ?? "",
150
+ usageGuidance: primitive.usageGuidance ?? "",
151
+ props: primitive.props ?? {},
152
+ examples: primitive.examples ?? [],
153
+ sourceRepoFullName: primitive.sourceRepoFullName ?? null
154
+ };
155
+ }
156
+
157
+ // src/tools/tokens.ts
158
+ var tokenSchema = {
159
+ type: "object",
160
+ properties: {
161
+ name: { type: "string" },
162
+ value: { type: "string" },
163
+ category: { type: "string" },
164
+ path: { type: "array", items: { type: "string" } },
165
+ originalName: { type: "string" },
166
+ tier: { type: "string" },
167
+ type: { type: "string" },
168
+ description: { type: "string" }
169
+ },
170
+ required: ["name", "value", "category"],
171
+ additionalProperties: false
172
+ };
173
+ var tokenTools = [
174
+ {
175
+ name: "design_system/list_tokens",
176
+ title: "List Tokens",
177
+ description: "List the design-system tokens Fragments Cloud has indexed for this project. Returns factual token names, values, categories, and source metadata.",
178
+ inputSchema: {
179
+ type: "object",
180
+ properties: {},
181
+ additionalProperties: false,
182
+ maxProperties: 0
183
+ },
184
+ outputSchema: {
185
+ type: "object",
186
+ properties: {
187
+ tokenCount: { type: "integer" },
188
+ tokens: { type: "array", items: tokenSchema }
189
+ },
190
+ required: ["tokenCount", "tokens"],
191
+ additionalProperties: false
192
+ },
193
+ annotations: {
194
+ readOnlyHint: true,
195
+ idempotentHint: true
196
+ },
197
+ async call(_args, context) {
198
+ const tokens = await context.provider.listTokens();
199
+ const metadata = await context.provider.getCatalogMetadata?.();
200
+ return tokenResult(tokens, metadata ?? null);
201
+ }
202
+ }
203
+ ];
204
+ function tokenResult(tokens, metadata) {
205
+ return {
206
+ content: [
207
+ {
208
+ type: "text",
209
+ text: tokens.length === 0 ? "No design-system tokens have been indexed in Fragments Cloud yet." : `Found ${tokens.length} design-system tokens.`
210
+ }
211
+ ],
212
+ structuredContent: {
213
+ tokenCount: tokens.length,
214
+ tokens: tokens.map(normalizeToken)
215
+ },
216
+ _meta: {
217
+ catalog: metadata
218
+ }
219
+ };
220
+ }
221
+ function normalizeToken(token) {
222
+ return {
223
+ name: token.name,
224
+ value: token.value,
225
+ category: token.category,
226
+ path: token.path ?? [],
227
+ ...token.originalName ? { originalName: token.originalName } : {},
228
+ ...token.tier ? { tier: token.tier } : {},
229
+ ...token.type ? { type: token.type } : {},
230
+ ...token.description ? { description: token.description } : {}
231
+ };
232
+ }
233
+
234
+ // src/tools/conform.ts
235
+ var locationSchema = {
236
+ type: "object",
237
+ properties: {
238
+ line: { type: "integer" },
239
+ column: { type: "integer" },
240
+ endLine: { type: "integer" },
241
+ endColumn: { type: "integer" }
242
+ },
243
+ additionalProperties: false
244
+ };
245
+ var changeSchema = {
246
+ type: "object",
247
+ properties: {
248
+ kind: {
249
+ type: "string",
250
+ enum: ["swap-component", "rewrite-import", "rename-prop", "replace-token", "add-fallback"]
251
+ },
252
+ ruleId: { type: "string" },
253
+ code: { type: "string" },
254
+ severity: { type: "string", enum: ["error", "warning", "info"] },
255
+ message: { type: "string" },
256
+ before: { type: "string" },
257
+ after: { type: "string" },
258
+ canonical: { type: "string" },
259
+ confidence: { type: "string", enum: ["high", "medium", "low"] },
260
+ location: locationSchema
261
+ },
262
+ required: ["kind", "severity", "message", "confidence"],
263
+ additionalProperties: false
264
+ };
265
+ var suggestionSchema = {
266
+ type: "object",
267
+ properties: {
268
+ kind: {
269
+ type: "string",
270
+ enum: ["map-prop-value", "choose-among-alternates", "review-low-confidence"]
271
+ },
272
+ message: { type: "string" },
273
+ canonical: { type: "string" },
274
+ hint: { type: "string" },
275
+ confidence: { type: "string", enum: ["high", "medium", "low"] },
276
+ location: locationSchema
277
+ },
278
+ required: ["kind", "message", "confidence"],
279
+ additionalProperties: false
280
+ };
281
+ var unresolvedSchema = {
282
+ type: "object",
283
+ properties: {
284
+ element: { type: "string" },
285
+ canonical: { type: "string" },
286
+ reason: { type: "string" },
287
+ location: locationSchema
288
+ },
289
+ required: ["reason"],
290
+ additionalProperties: false
291
+ };
292
+ var conformTools = [
293
+ {
294
+ name: "design_system/conform",
295
+ title: "Conform to Design System",
296
+ 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.",
297
+ inputSchema: {
298
+ type: "object",
299
+ properties: {
300
+ code: {
301
+ type: "string",
302
+ description: "The UI code to conform (JSX/TSX, HTML, or a CSS/SCSS block)."
303
+ },
304
+ filename: {
305
+ type: "string",
306
+ description: "Filename or extension hint for parser selection (e.g. 'Form.tsx', 'styles.scss'). Defaults to TSX."
307
+ },
308
+ apply: {
309
+ type: "string",
310
+ enum: ["deterministic", "none"],
311
+ description: "deterministic (default): return code with safe edits applied. none: findings only."
312
+ }
313
+ },
314
+ required: ["code"],
315
+ additionalProperties: false
316
+ },
317
+ outputSchema: {
318
+ type: "object",
319
+ properties: {
320
+ conformed: { type: "string" },
321
+ changed: { type: "boolean" },
322
+ summary: { type: "string" },
323
+ designSystem: {
324
+ type: "object",
325
+ properties: {
326
+ name: { type: "string" },
327
+ importsAdded: { type: "array", items: { type: "string" } }
328
+ },
329
+ required: ["importsAdded"],
330
+ additionalProperties: false
331
+ },
332
+ changes: { type: "array", items: changeSchema },
333
+ suggestions: { type: "array", items: suggestionSchema },
334
+ unresolved: { type: "array", items: unresolvedSchema }
335
+ },
336
+ required: ["conformed", "changed", "changes", "suggestions"],
337
+ additionalProperties: false
338
+ },
339
+ annotations: {
340
+ readOnlyHint: true,
341
+ idempotentHint: true,
342
+ // Output depends on the `code` argument, so it must not be served from a
343
+ // tenant-wide cache the way argument-free tools (list_primitives) are.
344
+ cacheScope: "none",
345
+ ttlMs: 0
346
+ },
347
+ async call(args, context) {
348
+ const input = {
349
+ code: typeof args.code === "string" ? args.code : "",
350
+ filename: typeof args.filename === "string" ? args.filename : void 0,
351
+ apply: args.apply === "none" ? "none" : "deterministic"
57
352
  };
353
+ const result = await context.provider.conform(input);
354
+ return conformResult(result);
58
355
  }
59
- if (!raw.components || typeof raw.components !== "object") {
60
- throw new Error(
61
- `Invalid design system data: "components" field is required and must be an object. File: ${this.filePath}`
62
- );
63
- }
64
- const packageMap = raw.packageMap ?? {};
65
- const defaultPackageName = raw.defaultPackageName;
66
- const metadata = raw.metadata;
67
- const components = Object.fromEntries(
68
- Object.entries(raw.components).map(
69
- ([key, fragment]) => [
70
- key,
71
- componentFromCompiledFragment({
72
- id: key,
73
- fragment,
74
- sourceType: "custom-json",
75
- packageName: packageMap[fragment.meta.name] ?? defaultPackageName,
76
- importPath: packageMap[fragment.meta.name] ?? defaultPackageName
77
- })
78
- ]
79
- )
80
- );
81
- const blocks = raw.blocks ? Object.fromEntries(
82
- Object.entries(raw.blocks).map(
83
- ([key, block]) => [key, blockFromCompiledBlock(key, block)]
84
- )
85
- ) : void 0;
86
- const tokens = raw.tokens ? tokensFromCompiledTokenData(raw.tokens) : void 0;
87
- const snapshot = validateSnapshot({
88
- schemaVersion: 1,
89
- sourceType: "custom-json",
90
- sourceLabel: this.filePath,
91
- capabilities: buildCapabilities({
92
- components,
93
- blocks,
94
- tokens,
95
- graph: raw.graph,
96
- performanceSummary: raw.performanceSummary
97
- }),
98
- metadata: {
99
- designSystemName: metadata?.designSystemName,
100
- packageName: metadata?.packageName ?? defaultPackageName,
101
- importPath: metadata?.importPath ?? defaultPackageName,
102
- revision: metadata?.revision,
103
- updatedAt: metadata?.updatedAt
356
+ }
357
+ ];
358
+ function conformResult(result) {
359
+ const summary = result.summary || defaultSummary(result);
360
+ return {
361
+ content: [{ type: "text", text: summary }],
362
+ structuredContent: {
363
+ conformed: result.conformed,
364
+ changed: result.changed,
365
+ summary,
366
+ ...result.designSystem ? {
367
+ designSystem: {
368
+ ...result.designSystem.name ? { name: result.designSystem.name } : {},
369
+ importsAdded: result.designSystem.importsAdded ?? []
370
+ }
371
+ } : {},
372
+ changes: result.changes,
373
+ suggestions: result.suggestions,
374
+ unresolved: result.unresolved
375
+ }
376
+ };
377
+ }
378
+ function defaultSummary(result) {
379
+ if (!result.changed && result.changes.length === 0) {
380
+ return "No conformance changes were applied.";
381
+ }
382
+ return `Applied ${result.changes.length} change(s); ${result.suggestions.length} suggestion(s) left for review.`;
383
+ }
384
+
385
+ // src/tools/prove-compliant-schema.ts
386
+ var locationSchema2 = {
387
+ type: "object",
388
+ properties: {
389
+ line: { type: "integer" },
390
+ column: { type: "integer" },
391
+ endLine: { type: "integer" },
392
+ endColumn: { type: "integer" }
393
+ },
394
+ additionalProperties: false
395
+ };
396
+ var residualSchema = {
397
+ type: "object",
398
+ properties: {
399
+ reason: { type: "string" },
400
+ message: { type: "string" },
401
+ canonical: { type: "string" },
402
+ element: { type: "string" },
403
+ hint: { type: "string" },
404
+ confidence: { type: "string" },
405
+ location: locationSchema2
406
+ },
407
+ additionalProperties: true
408
+ };
409
+ var proveCompliantOutputSchema = {
410
+ type: "object",
411
+ properties: {
412
+ provedCompliant: { type: "boolean" },
413
+ conformed: { type: "string" },
414
+ changed: { type: "boolean" },
415
+ passes: { type: "integer" },
416
+ maxPasses: { type: "integer" },
417
+ initialIssueCount: { type: "integer" },
418
+ finalIssueCount: { type: "integer" },
419
+ corrections: { type: "integer" },
420
+ summary: { type: "string" },
421
+ taskId: { type: "string" },
422
+ changes: { type: "array", items: { type: "object", additionalProperties: true } },
423
+ suggestions: { type: "array", items: residualSchema },
424
+ unresolved: { type: "array", items: residualSchema },
425
+ passHistory: { type: "array", items: { type: "object", additionalProperties: true } }
426
+ },
427
+ required: [
428
+ "provedCompliant",
429
+ "conformed",
430
+ "changed",
431
+ "passes",
432
+ "maxPasses",
433
+ "initialIssueCount",
434
+ "finalIssueCount",
435
+ "corrections",
436
+ "summary",
437
+ "changes",
438
+ "suggestions",
439
+ "unresolved",
440
+ "passHistory"
441
+ ],
442
+ additionalProperties: false
443
+ };
444
+
445
+ // src/tools/prove-compliant.ts
446
+ var TOOL_NAME = "design_system/prove_compliant";
447
+ var DEFAULT_MAX_PASSES = 4;
448
+ var MAX_PASSES = 8;
449
+ var DEFAULT_TTL_MS = 15 * 60 * 1e3;
450
+ var proveCompliantTools = [
451
+ {
452
+ name: TOOL_NAME,
453
+ title: "Prove Design-System Compliance",
454
+ description: "Run a closed validate-fix-validate loop over generated UI code until Fragments can prove it has zero design-system findings, or return the remaining issues. Uses deterministic fixes first, then MCP Sampling when the connected client supports it.",
455
+ inputSchema: {
456
+ type: "object",
457
+ properties: {
458
+ code: {
459
+ type: "string",
460
+ description: "The UI code to prove compliant."
461
+ },
462
+ filename: {
463
+ type: "string",
464
+ description: "Filename or extension hint. Defaults to TSX."
465
+ },
466
+ maxPasses: {
467
+ type: "integer",
468
+ minimum: 1,
469
+ maximum: MAX_PASSES,
470
+ description: "Maximum validation/fix passes. Defaults to 4."
471
+ },
472
+ allowSampling: {
473
+ type: "boolean",
474
+ description: "Allow MCP Sampling for residual issues. Defaults to true."
475
+ }
104
476
  },
105
- components,
106
- blocks,
107
- tokens,
108
- graph: raw.graph,
109
- performanceSummary: raw.performanceSummary,
110
- packageMap,
111
- defaultPackageName
477
+ required: ["code"],
478
+ additionalProperties: false
479
+ },
480
+ outputSchema: proveCompliantOutputSchema,
481
+ annotations: {
482
+ readOnlyHint: true,
483
+ idempotentHint: false,
484
+ cacheScope: "none",
485
+ ttlMs: 0
486
+ },
487
+ async call(args, context) {
488
+ return proveCompliant(args, context);
489
+ }
490
+ }
491
+ ];
492
+ async function proveCompliant(args, context) {
493
+ const restored = decodeState(context.requestState);
494
+ const input = restored ?? initialState(args);
495
+ const task = await ensureTask(context, input);
496
+ if (task) input.taskId = task.taskId;
497
+ const sampled = consumeSamplingResponse(input, context.inputResponses);
498
+ if (sampled) {
499
+ input.samplingAttempts += 1;
500
+ if (sampled.code) {
501
+ input.code = sampled.code;
502
+ }
503
+ const last = input.passHistory[input.passHistory.length - 1];
504
+ if (last) {
505
+ last.sampling = {
506
+ ...last.sampling ?? {},
507
+ used: Boolean(sampled.code),
508
+ model: sampled.model,
509
+ stopReason: sampled.stopReason,
510
+ reason: sampled.reason
511
+ };
512
+ }
513
+ }
514
+ let current = input.code;
515
+ let lastResult = null;
516
+ while (input.passHistory.length < input.maxPasses) {
517
+ const pass = input.passHistory.length + 1;
518
+ await updateTask(context, input.taskId, {
519
+ status: "working",
520
+ progress: {
521
+ current: pass,
522
+ total: input.maxPasses,
523
+ message: `Validating pass ${pass}`
524
+ }
525
+ });
526
+ const result = await context.provider.conform({
527
+ code: current,
528
+ filename: input.filename,
529
+ apply: "deterministic"
112
530
  });
531
+ lastResult = result;
532
+ const issues = issueCount(result);
533
+ input.passHistory.push({
534
+ pass,
535
+ issues,
536
+ deterministicChanges: result.changes.length,
537
+ suggestions: result.suggestions.length,
538
+ unresolved: result.unresolved.length,
539
+ changed: result.changed
540
+ });
541
+ input.corrections += result.changes.length;
542
+ if (issues === 0) {
543
+ const final2 = completeResult({
544
+ state: input,
545
+ result,
546
+ originalCode: args.code,
547
+ provedCompliant: true,
548
+ reason: `0 findings after ${pass} pass${pass === 1 ? "" : "es"}.`
549
+ });
550
+ return finish(context, input.taskId, final2);
551
+ }
552
+ if (result.changed && result.conformed !== current) {
553
+ current = result.conformed;
554
+ input.code = current;
555
+ continue;
556
+ }
557
+ const residual = {
558
+ suggestions: result.suggestions,
559
+ unresolved: result.unresolved
560
+ };
561
+ if (canUseSampling(input, context, residual)) {
562
+ return requestSampling({ state: input, context, current, residual });
563
+ }
564
+ break;
565
+ }
566
+ const fallback = lastResult ?? emptyConformResult(current);
567
+ const final = completeResult({
568
+ state: input,
569
+ result: fallback,
570
+ originalCode: args.code,
571
+ provedCompliant: false,
572
+ reason: incompleteReason(input, context, fallback)
573
+ });
574
+ return finish(context, input.taskId, final);
575
+ }
576
+ function initialState(args) {
577
+ return {
578
+ tool: TOOL_NAME,
579
+ code: typeof args.code === "string" ? args.code : "",
580
+ filename: typeof args.filename === "string" ? args.filename : void 0,
581
+ maxPasses: typeof args.maxPasses === "number" ? Math.min(MAX_PASSES, Math.max(1, Math.trunc(args.maxPasses))) : DEFAULT_MAX_PASSES,
582
+ allowSampling: args.allowSampling !== false,
583
+ passHistory: [],
584
+ corrections: 0,
585
+ samplingAttempts: 0
586
+ };
587
+ }
588
+ async function ensureTask(context, state) {
589
+ try {
590
+ if (state.taskId) return await context.tasks.get(state.taskId);
591
+ return await context.tasks.create({
592
+ title: "Prove UI compliance",
593
+ ttlMs: DEFAULT_TTL_MS,
594
+ pollIntervalMs: 500
595
+ });
596
+ } catch {
597
+ return null;
598
+ }
599
+ }
600
+ async function requestSampling(args) {
601
+ const requestKey = `prove_compliant_sampling_${args.state.passHistory.length}`;
602
+ args.state.requestKey = requestKey;
603
+ const last = args.state.passHistory[args.state.passHistory.length - 1];
604
+ if (last) last.sampling = { requested: true };
605
+ await updateTask(args.context, args.state.taskId, {
606
+ status: "input_required",
607
+ progress: {
608
+ current: args.state.passHistory.length,
609
+ total: args.state.maxPasses,
610
+ message: "Waiting for MCP Sampling to repair residual issues"
611
+ }
612
+ });
613
+ return {
614
+ resultType: "input_required",
615
+ inputRequests: {
616
+ [requestKey]: {
617
+ method: "sampling/createMessage",
618
+ params: {
619
+ messages: [
620
+ {
621
+ role: "user",
622
+ content: {
623
+ type: "text",
624
+ text: samplingPrompt(args.current, args.residual)
625
+ }
626
+ }
627
+ ],
628
+ systemPrompt: "You repair UI code for Fragments design-system governance. Return only JSON with a single string field named code.",
629
+ includeContext: "none",
630
+ maxTokens: 4e3,
631
+ temperature: 0.1,
632
+ modelPreferences: {
633
+ costPriority: 0.2,
634
+ speedPriority: 0.3,
635
+ intelligencePriority: 0.8
636
+ }
637
+ }
638
+ }
639
+ },
640
+ requestState: encodeState(args.state)
641
+ };
642
+ }
643
+ function canUseSampling(state, context, residual) {
644
+ if (!state.allowSampling) return false;
645
+ if (state.samplingAttempts >= state.maxPasses - 1) return false;
646
+ if (residual.suggestions.length === 0 && residual.unresolved.length === 0) return false;
647
+ const sampling = context.clientCapabilities?.sampling;
648
+ return Boolean(sampling && typeof sampling === "object");
649
+ }
650
+ function consumeSamplingResponse(state, responses) {
651
+ if (!state.requestKey || !responses) return null;
652
+ const response = responses[state.requestKey];
653
+ if (!response) return null;
654
+ const text = extractText(response.content);
655
+ const code = extractSampledCode(text);
656
+ state.requestKey = void 0;
657
+ if (!code) {
113
658
  return {
114
- snapshot,
115
- components: snapshot.components,
116
- blocks: snapshot.blocks,
117
- tokens: snapshot.tokens,
118
- graph: snapshot.graph,
119
- performanceSummary: snapshot.performanceSummary,
120
- packageMap: snapshot.packageMap,
121
- defaultPackageName: snapshot.defaultPackageName,
122
- capabilities: new Set(snapshot.capabilities)
659
+ attempted: true,
660
+ reason: "sampling response did not include code",
661
+ model: typeof response.model === "string" ? response.model : void 0,
662
+ stopReason: typeof response.stopReason === "string" ? response.stopReason : void 0
123
663
  };
124
664
  }
125
- };
665
+ return {
666
+ attempted: true,
667
+ code,
668
+ model: typeof response.model === "string" ? response.model : void 0,
669
+ stopReason: typeof response.stopReason === "string" ? response.stopReason : void 0
670
+ };
671
+ }
672
+ function completeResult(args) {
673
+ const initialIssueCount = args.state.passHistory[0]?.issues ?? 0;
674
+ const finalIssueCount = issueCount(args.result);
675
+ const changed = typeof args.originalCode === "string" && args.result.conformed !== args.originalCode;
676
+ const summary = args.provedCompliant ? `Proved compliant: ${args.reason}` : `Could not prove compliance: ${args.reason}`;
677
+ return {
678
+ content: [{ type: "text", text: summary }],
679
+ structuredContent: {
680
+ provedCompliant: args.provedCompliant,
681
+ conformed: args.result.conformed,
682
+ changed,
683
+ passes: args.state.passHistory.length,
684
+ maxPasses: args.state.maxPasses,
685
+ initialIssueCount,
686
+ finalIssueCount,
687
+ corrections: args.state.corrections,
688
+ summary,
689
+ taskId: args.state.taskId,
690
+ changes: args.result.changes,
691
+ suggestions: args.result.suggestions,
692
+ unresolved: args.result.unresolved,
693
+ passHistory: args.state.passHistory
694
+ }
695
+ };
696
+ }
697
+ async function finish(context, taskId, result) {
698
+ await updateTask(context, taskId, {
699
+ status: "completed",
700
+ result,
701
+ progress: { current: 1, total: 1, message: "Proof loop completed" }
702
+ });
703
+ return result;
704
+ }
705
+ async function updateTask(context, taskId, patch) {
706
+ if (!taskId) return null;
707
+ try {
708
+ return await context.tasks.update(taskId, patch);
709
+ } catch {
710
+ return null;
711
+ }
712
+ }
713
+ function issueCount(result) {
714
+ return result.changes.length + result.suggestions.length + result.unresolved.length;
715
+ }
716
+ function incompleteReason(state, context, result) {
717
+ if (state.passHistory.length >= state.maxPasses) {
718
+ return `stopped after ${state.maxPasses} passes with ${issueCount(result)} finding(s).`;
719
+ }
720
+ if (!state.allowSampling) return "sampling was disabled and residual issues remain.";
721
+ if (!context.clientCapabilities?.sampling) {
722
+ return "the MCP client did not declare sampling support and residual issues remain.";
723
+ }
724
+ return `${issueCount(result)} residual finding(s) remain after deterministic and sampled fixes.`;
725
+ }
726
+ function samplingPrompt(code, residual) {
727
+ return [
728
+ "Repair this UI code so it satisfies the remaining Fragments findings.",
729
+ "Preserve behavior, text, event handlers, and existing imports unless a finding requires a change.",
730
+ 'Return JSON only: {"code":"...full revised code..."}.',
731
+ "",
732
+ "Remaining findings:",
733
+ JSON.stringify(residual, null, 2),
734
+ "",
735
+ "Current code:",
736
+ "```tsx",
737
+ code,
738
+ "```"
739
+ ].join("\n");
740
+ }
741
+ function extractText(content) {
742
+ if (!content) return "";
743
+ if (Array.isArray(content)) return content.map(extractText).join("\n");
744
+ if (typeof content === "object") {
745
+ const record = content;
746
+ return typeof record.text === "string" ? record.text : "";
747
+ }
748
+ return typeof content === "string" ? content : "";
749
+ }
750
+ function extractSampledCode(text) {
751
+ const trimmed = text.trim();
752
+ if (!trimmed) return null;
753
+ try {
754
+ const parsed = JSON.parse(trimmed);
755
+ if (typeof parsed.code === "string" && parsed.code.trim()) return parsed.code;
756
+ } catch {
757
+ }
758
+ const fence = /```(?:[A-Za-z0-9_-]+)?\n([\s\S]*?)```/.exec(trimmed);
759
+ if (fence?.[1]?.trim()) return fence[1].trim();
760
+ return trimmed;
761
+ }
762
+ function encodeState(state) {
763
+ return JSON.stringify(state);
764
+ }
765
+ function decodeState(value) {
766
+ if (!value) return null;
767
+ try {
768
+ const parsed = JSON.parse(value);
769
+ return parsed?.tool === TOOL_NAME ? parsed : null;
770
+ } catch {
771
+ return null;
772
+ }
773
+ }
774
+ function emptyConformResult(code) {
775
+ return {
776
+ conformed: code,
777
+ changed: false,
778
+ summary: "No conform result was produced.",
779
+ changes: [],
780
+ suggestions: [],
781
+ unresolved: []
782
+ };
783
+ }
126
784
 
127
- // src/orama-index.ts
128
- import { create, insertMultiple, search } from "@orama/orama";
129
- var SYNONYM_MAP = {
130
- "form": ["input", "field", "submit", "validation"],
131
- "input": ["form", "field", "text", "entry"],
132
- "button": ["action", "click", "submit", "trigger"],
133
- "action": ["button", "click", "trigger"],
134
- "submit": ["button", "form", "action", "send"],
135
- "alert": ["notification", "message", "warning", "error", "feedback"],
136
- "notification": ["alert", "message", "toast"],
137
- "feedback": ["form", "comment", "review", "rating"],
138
- "card": ["container", "panel", "box", "content"],
139
- "toggle": ["switch", "checkbox", "boolean", "on/off"],
140
- "switch": ["toggle", "checkbox", "boolean"],
141
- "badge": ["tag", "label", "status", "indicator"],
142
- "status": ["badge", "indicator", "state"],
143
- "login": ["auth", "signin", "authentication", "form"],
144
- "auth": ["login", "signin", "authentication"],
145
- "chat": ["message", "conversation", "ai"],
146
- "table": ["data", "grid", "list", "rows"],
147
- "textarea": ["text", "input", "multiline", "area", "comment"],
148
- "area": ["textarea", "multiline", "text"],
149
- "landing": ["page", "hero", "marketing", "section", "layout"],
150
- "hero": ["landing", "marketing", "banner", "headline", "section"],
151
- "marketing": ["landing", "hero", "pricing", "testimonial", "cta"],
152
- "cta": ["marketing", "banner", "action", "button"],
153
- "testimonial": ["marketing", "review", "quote", "feedback"],
154
- "layout": ["stack", "grid", "box", "container", "page"],
155
- "page": ["layout", "landing", "section", "container"],
156
- "section": ["hero", "feature", "testimonial", "cta", "faq"],
157
- "pricing": ["card", "plan", "tier", "marketing"],
158
- "plan": ["pricing", "card", "tier", "subscription"],
159
- "dashboard": ["metrics", "stats", "chart", "card", "grid"],
160
- "metrics": ["dashboard", "stats", "progress", "number"],
161
- "stats": ["metrics", "dashboard", "progress", "badge"],
162
- "chart": ["dashboard", "metrics", "data", "graph"]
785
+ // src/tools/registry.ts
786
+ var MAX_SCHEMA_DEPTH = 16;
787
+ var MCP_TOOLS = [
788
+ ...primitiveTools,
789
+ ...tokenTools,
790
+ ...conformTools,
791
+ ...proveCompliantTools
792
+ ];
793
+ function getTool(name) {
794
+ return MCP_TOOLS.find((tool) => tool.name === name) ?? null;
795
+ }
796
+ function listToolDescriptors() {
797
+ return MCP_TOOLS.map(({ call: _call, ...tool }) => ({
798
+ ...tool,
799
+ inputSchema: validateToolSchema(tool.name, tool.inputSchema),
800
+ ...tool.outputSchema ? { outputSchema: validateToolSchema(tool.name, tool.outputSchema) } : {},
801
+ annotations: {
802
+ cacheScope: "tenant",
803
+ ttlMs: 6e4,
804
+ ...tool.annotations ?? {}
805
+ }
806
+ }));
807
+ }
808
+ function validateToolSchema(toolName, schema) {
809
+ assertSchemaNode(toolName, schema, 0);
810
+ return schema;
811
+ }
812
+ function assertSchemaNode(toolName, value, depth) {
813
+ if (depth > MAX_SCHEMA_DEPTH) {
814
+ throw new Error(`Tool ${toolName} schema exceeds maximum depth`);
815
+ }
816
+ if (!value || typeof value !== "object") return;
817
+ if (Array.isArray(value)) {
818
+ for (const item of value) assertSchemaNode(toolName, item, depth + 1);
819
+ return;
820
+ }
821
+ const record = value;
822
+ const ref = record.$ref;
823
+ if (typeof ref === "string" && !ref.startsWith("#/")) {
824
+ throw new Error(`Tool ${toolName} schema contains external $ref`);
825
+ }
826
+ for (const child of Object.values(record)) {
827
+ assertSchemaNode(toolName, child, depth + 1);
828
+ }
829
+ }
830
+
831
+ // src/protocol/server.ts
832
+ var McpProtocolError = class extends Error {
833
+ code;
834
+ data;
835
+ constructor(code, message, data) {
836
+ super(message);
837
+ this.code = code;
838
+ this.data = data;
839
+ }
163
840
  };
164
- var USE_CASE_TOKEN_CATEGORIES = {
165
- "table": ["spacing", "borders", "surfaces", "text"],
166
- "data": ["spacing", "borders", "surfaces"],
167
- "grid": ["spacing", "layout"],
168
- "form": ["spacing", "borders", "radius", "focus"],
169
- "input": ["spacing", "borders", "radius", "focus"],
170
- "card": ["surfaces", "shadows", "radius", "borders", "spacing"],
171
- "button": ["colors", "radius", "spacing", "focus"],
172
- "layout": ["spacing", "layout", "surfaces"],
173
- "dashboard": ["spacing", "surfaces", "borders", "shadows"],
174
- "chat": ["spacing", "surfaces", "radius", "shadows"],
175
- "modal": ["shadows", "surfaces", "radius", "spacing"],
176
- "dialog": ["shadows", "surfaces", "radius", "spacing"],
177
- "navigation": ["spacing", "surfaces", "borders"],
178
- "sidebar": ["spacing", "surfaces", "borders"],
179
- "hero": ["spacing", "typography", "colors"],
180
- "landing": ["spacing", "typography", "colors"],
181
- "pricing": ["spacing", "surfaces", "borders", "radius"],
182
- "auth": ["spacing", "borders", "radius", "focus"],
183
- "login": ["spacing", "borders", "radius", "focus"],
184
- "dark": ["colors", "surfaces"],
185
- "theme": ["colors", "surfaces", "text"]
841
+ var FragmentsMcpServer = class {
842
+ provider;
843
+ tasks;
844
+ constructor(options) {
845
+ this.provider = options.provider;
846
+ this.tasks = options.tasks ?? new MemoryTaskStore();
847
+ }
848
+ async handleJsonRpc(request) {
849
+ try {
850
+ const result = await this.dispatch(request);
851
+ return success(request.id ?? null, result);
852
+ } catch (error) {
853
+ return failure(request.id ?? null, toProtocolError(error));
854
+ }
855
+ }
856
+ async dispatch(request) {
857
+ const params = request.params ?? {};
858
+ switch (request.method) {
859
+ case "server/discover":
860
+ return this.discover();
861
+ case "tools/list":
862
+ return { tools: listToolDescriptors() };
863
+ case "tools/call":
864
+ return this.callTool(params);
865
+ case "resources/list":
866
+ return { resources: listResources() };
867
+ case "resources/read":
868
+ return this.readResource(params);
869
+ case "tasks/get":
870
+ return this.getTask(params);
871
+ case "tasks/update":
872
+ return this.updateTask(params);
873
+ case "tasks/cancel":
874
+ return this.cancelTask(params);
875
+ default:
876
+ throw new McpProtocolError(-32601, `Unknown MCP method ${request.method}`);
877
+ }
878
+ }
879
+ discover() {
880
+ return {
881
+ protocolVersion: LATEST_PROTOCOL_VERSION,
882
+ serverInfo: {
883
+ name: "@fragments-sdk/mcp",
884
+ version: "0.11.0-rc"
885
+ },
886
+ capabilities: {
887
+ tools: { listChanged: false }
888
+ }
889
+ };
890
+ }
891
+ async callTool(params) {
892
+ const name = requireString(params.name, "name");
893
+ const tool = getTool(name);
894
+ if (!tool) {
895
+ throw new McpProtocolError(-32602, `Unknown tool ${name}`);
896
+ }
897
+ const context = {
898
+ provider: this.provider,
899
+ tasks: this.tasks,
900
+ requestMeta: readMeta(params),
901
+ clientCapabilities: readClientCapabilities(params),
902
+ inputResponses: readInputResponses(params),
903
+ requestState: readRequestState(params)
904
+ };
905
+ const args = readArgs(params);
906
+ validateArgs(tool.name, tool.inputSchema, args);
907
+ return tool.call(args, context);
908
+ }
909
+ async readResource(params) {
910
+ const uri = requireString(params.uri, "uri");
911
+ const resource = getResource(uri);
912
+ if (!resource) {
913
+ throw new McpProtocolError(-32602, `Unknown resource ${uri}`);
914
+ }
915
+ return resource.read({
916
+ provider: this.provider,
917
+ tasks: this.tasks,
918
+ requestMeta: readMeta(params)
919
+ });
920
+ }
921
+ async getTask(params) {
922
+ const taskId = requireString(params.taskId, "taskId");
923
+ const task = await this.tasks.get(taskId);
924
+ if (!task) {
925
+ throw new McpProtocolError(-32602, `Unknown task ${taskId}`);
926
+ }
927
+ return { task };
928
+ }
929
+ async updateTask(params) {
930
+ const taskId = requireString(params.taskId, "taskId");
931
+ const task = await this.tasks.update(taskId, {});
932
+ if (!task) {
933
+ throw new McpProtocolError(-32602, `Unknown task ${taskId}`);
934
+ }
935
+ return { task };
936
+ }
937
+ async cancelTask(params) {
938
+ const taskId = requireString(params.taskId, "taskId");
939
+ const task = await this.tasks.cancel(taskId);
940
+ if (!task) {
941
+ throw new McpProtocolError(-32602, `Unknown task ${taskId}`);
942
+ }
943
+ return { task };
944
+ }
186
945
  };
946
+ async function handleMcpJsonRpc(request, options) {
947
+ return new FragmentsMcpServer(options).handleJsonRpc(request);
948
+ }
949
+ async function handleMcpHttpRequest(request, options) {
950
+ let body;
951
+ try {
952
+ body = await request.json();
953
+ } catch {
954
+ return Response.json(failure(null, new McpProtocolError(-32700, "Invalid JSON")), {
955
+ status: 400
956
+ });
957
+ }
958
+ const response = await handleMcpJsonRpc(body, options);
959
+ return Response.json(response, {
960
+ status: "error" in response ? 400 : 200,
961
+ headers: {
962
+ "Cache-Control": "no-store",
963
+ "Mcp-Protocol-Version": LATEST_PROTOCOL_VERSION
964
+ }
965
+ });
966
+ }
967
+ function success(id, result) {
968
+ return { jsonrpc: "2.0", id, result };
969
+ }
970
+ function failure(id, error) {
971
+ return {
972
+ jsonrpc: "2.0",
973
+ id,
974
+ error: {
975
+ code: error.code,
976
+ message: error.message,
977
+ data: error.data
978
+ }
979
+ };
980
+ }
981
+ function toProtocolError(error) {
982
+ if (error instanceof McpProtocolError) return error;
983
+ return new McpProtocolError(
984
+ -32603,
985
+ error instanceof Error ? error.message : "Internal MCP error"
986
+ );
987
+ }
988
+ function requireString(value, key) {
989
+ if (typeof value === "string" && value.trim()) return value.trim();
990
+ throw new McpProtocolError(-32602, `Missing ${key}`);
991
+ }
992
+ function readArgs(params) {
993
+ const args = params.arguments ?? {};
994
+ if (!args || typeof args !== "object" || Array.isArray(args)) return {};
995
+ return args;
996
+ }
997
+ function readMeta(params) {
998
+ const meta = params._meta;
999
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
1000
+ return meta;
1001
+ }
1002
+ function readClientCapabilities(params) {
1003
+ const meta = readMeta(params);
1004
+ const capabilities = meta?.["io.modelcontextprotocol/clientCapabilities"];
1005
+ if (!capabilities || typeof capabilities !== "object" || Array.isArray(capabilities)) {
1006
+ return void 0;
1007
+ }
1008
+ return capabilities;
1009
+ }
1010
+ function readInputResponses(params) {
1011
+ const responses = params.inputResponses;
1012
+ if (!responses || typeof responses !== "object" || Array.isArray(responses)) {
1013
+ return void 0;
1014
+ }
1015
+ return responses;
1016
+ }
1017
+ function readRequestState(params) {
1018
+ return typeof params.requestState === "string" ? params.requestState : void 0;
1019
+ }
1020
+ function validateArgs(toolName, schema, args) {
1021
+ if (schema.type !== "object") return;
1022
+ const properties = readProperties(schema.properties);
1023
+ const required = Array.isArray(schema.required) ? schema.required.filter((item) => typeof item === "string") : [];
1024
+ const argCount = Object.keys(args).filter(
1025
+ (key) => args[key] !== void 0 && args[key] !== null
1026
+ ).length;
1027
+ if (typeof schema.minProperties === "number" && argCount < schema.minProperties) {
1028
+ throw new McpProtocolError(
1029
+ -32602,
1030
+ `Invalid arguments for ${toolName}: expected at least ${schema.minProperties} argument(s)`
1031
+ );
1032
+ }
1033
+ if (typeof schema.maxProperties === "number" && argCount > schema.maxProperties) {
1034
+ throw new McpProtocolError(
1035
+ -32602,
1036
+ `Invalid arguments for ${toolName}: expected at most ${schema.maxProperties} argument(s)`
1037
+ );
1038
+ }
1039
+ for (const key of required) {
1040
+ if (!(key in args) || args[key] === void 0 || args[key] === null) {
1041
+ throw new McpProtocolError(-32602, `Invalid arguments for ${toolName}: missing ${key}`);
1042
+ }
1043
+ if (properties[key]?.type === "string" && typeof args[key] === "string" && !args[key].trim()) {
1044
+ throw new McpProtocolError(-32602, `Invalid arguments for ${toolName}: missing ${key}`);
1045
+ }
1046
+ }
1047
+ if (schema.additionalProperties === false) {
1048
+ for (const key of Object.keys(args)) {
1049
+ if (!(key in properties)) {
1050
+ throw new McpProtocolError(-32602, `Invalid arguments for ${toolName}: unknown ${key}`);
1051
+ }
1052
+ }
1053
+ }
1054
+ for (const [key, value] of Object.entries(args)) {
1055
+ const property = properties[key];
1056
+ if (!property || value === void 0 || value === null) continue;
1057
+ validateValue(toolName, key, property, value);
1058
+ }
1059
+ }
1060
+ function readProperties(value) {
1061
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1062
+ const properties = {};
1063
+ for (const [key, child] of Object.entries(value)) {
1064
+ if (child && typeof child === "object" && !Array.isArray(child)) {
1065
+ properties[key] = child;
1066
+ }
1067
+ }
1068
+ return properties;
1069
+ }
1070
+ function validateValue(toolName, key, schema, value) {
1071
+ if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
1072
+ throw new McpProtocolError(
1073
+ -32602,
1074
+ `Invalid arguments for ${toolName}: ${key} must be one of ${schema.enum.join(", ")}`
1075
+ );
1076
+ }
1077
+ const type = schema.type;
1078
+ if (typeof type !== "string") return;
1079
+ const valid = type === "integer" ? typeof value === "number" && Number.isInteger(value) : type === "number" ? typeof value === "number" && Number.isFinite(value) : type === "string" ? typeof value === "string" : type === "boolean" ? typeof value === "boolean" : type === "object" ? !!value && typeof value === "object" && !Array.isArray(value) : true;
1080
+ if (!valid) {
1081
+ throw new McpProtocolError(-32602, `Invalid arguments for ${toolName}: ${key} must be ${type}`);
1082
+ }
1083
+ if (typeof value === "number" && typeof schema.minimum === "number" && value < schema.minimum) {
1084
+ throw new McpProtocolError(
1085
+ -32602,
1086
+ `Invalid arguments for ${toolName}: ${key} must be >= ${schema.minimum}`
1087
+ );
1088
+ }
1089
+ if (typeof value === "number" && typeof schema.maximum === "number" && value > schema.maximum) {
1090
+ throw new McpProtocolError(
1091
+ -32602,
1092
+ `Invalid arguments for ${toolName}: ${key} must be <= ${schema.maximum}`
1093
+ );
1094
+ }
1095
+ }
187
1096
 
188
- // src/scoring.ts
189
- var MINIMUM_SCORE_THRESHOLD = 5;
190
- var BLOCK_BOOST_PER_OCCURRENCE = 5;
191
-
192
- // src/search-config.ts
193
- var DEFAULT_SEARCH_CONFIG = {
194
- synonyms: SYNONYM_MAP,
195
- useCaseTokenCategories: USE_CASE_TOKEN_CATEGORIES,
196
- minimumScoreThreshold: MINIMUM_SCORE_THRESHOLD,
197
- blockBoostPerOccurrence: BLOCK_BOOST_PER_OCCURRENCE,
198
- vectorSearchUrl: "https://combative-jay-834.convex.site/search",
199
- vectorSearchTimeoutMs: 3e3
1097
+ // src/testing/fixtures.ts
1098
+ var fixturePrimitives = [
1099
+ {
1100
+ id: "button",
1101
+ name: "Button",
1102
+ primitiveName: "Button",
1103
+ category: "Actions",
1104
+ status: "ready",
1105
+ description: "Primary action trigger.",
1106
+ usageGuidance: "Use for explicit user actions.",
1107
+ importPath: "@fixture/ui/button",
1108
+ packageName: "@fixture/ui",
1109
+ filePath: "src/components/Button.tsx",
1110
+ sourceRepoFullName: "fixture/ui",
1111
+ props: {
1112
+ variant: {
1113
+ type: '"primary" | "secondary"',
1114
+ required: false,
1115
+ description: "Visual emphasis."
1116
+ }
1117
+ }
1118
+ }
1119
+ ];
1120
+ var fixtureTokens = [
1121
+ {
1122
+ name: "color.brand.primary",
1123
+ originalName: "--brand-600",
1124
+ value: "#2563eb",
1125
+ category: "color",
1126
+ path: ["color", "brand", "primary"],
1127
+ tier: "canonical",
1128
+ type: "color",
1129
+ description: "Primary brand color."
1130
+ },
1131
+ {
1132
+ name: "space.4",
1133
+ originalName: "--space-4",
1134
+ value: "16px",
1135
+ category: "spacing",
1136
+ path: ["space", "4"],
1137
+ tier: "canonical",
1138
+ type: "dimension"
1139
+ }
1140
+ ];
1141
+ var fixtureConformResult = {
1142
+ conformed: 'import { Button } from "@fixture/ui";\n\n<Button variant="primary" style={{ padding: "var(--space-4)" }}>Send</Button>',
1143
+ changed: true,
1144
+ summary: "Swapped <button> to Button (@fixture/ui), mapped #2563eb to --brand-600 and 16px to --space-4, flagged a missing accessible name.",
1145
+ designSystem: {
1146
+ name: "Fixture UI",
1147
+ importsAdded: ['import { Button } from "@fixture/ui";']
1148
+ },
1149
+ changes: [
1150
+ {
1151
+ kind: "swap-component",
1152
+ ruleId: "components/preferred-component",
1153
+ code: "FUI0410",
1154
+ severity: "warning",
1155
+ message: "Replaced native <button> with the design-system Button.",
1156
+ before: "<button>",
1157
+ after: '<Button variant="primary">',
1158
+ canonical: "Button",
1159
+ confidence: "high",
1160
+ location: { line: 1, column: 1 }
1161
+ },
1162
+ {
1163
+ kind: "replace-token",
1164
+ ruleId: "styles/no-raw-color",
1165
+ code: "FUI0601",
1166
+ severity: "error",
1167
+ message: "Replaced hardcoded color #2563eb with the matching design token.",
1168
+ before: "#2563eb",
1169
+ after: "var(--brand-600)",
1170
+ confidence: "high",
1171
+ location: { line: 1, column: 16 }
1172
+ },
1173
+ {
1174
+ kind: "replace-token",
1175
+ ruleId: "styles/no-raw-spacing",
1176
+ code: "FUI0602",
1177
+ severity: "warning",
1178
+ message: "Replaced hardcoded 16px with the matching spacing token.",
1179
+ before: "16px",
1180
+ after: "var(--space-4)",
1181
+ confidence: "high",
1182
+ location: { line: 1, column: 38 }
1183
+ }
1184
+ ],
1185
+ suggestions: [
1186
+ {
1187
+ kind: "review-low-confidence",
1188
+ confidence: "medium",
1189
+ message: 'Button has no accessible name beyond its text. Confirm "Send" is descriptive or add an aria-label.',
1190
+ canonical: "Button",
1191
+ location: { line: 1, column: 1 }
1192
+ }
1193
+ ],
1194
+ unresolved: []
200
1195
  };
201
-
202
- // src/index.ts
203
- import { BRAND } from "@fragments-sdk/core";
1196
+ function createFixtureProvider(args) {
1197
+ const primitives = args?.primitives ?? fixturePrimitives;
1198
+ const tokens = args?.tokens ?? fixtureTokens;
1199
+ const conformResult2 = args?.conform ?? fixtureConformResult;
1200
+ return {
1201
+ async listPrimitives() {
1202
+ return primitives;
1203
+ },
1204
+ async listTokens() {
1205
+ return tokens;
1206
+ },
1207
+ async conform() {
1208
+ return conformResult2;
1209
+ }
1210
+ };
1211
+ }
204
1212
  export {
205
- AutoExtractionAdapter,
206
- BRAND,
207
- BUILTIN_TOOLS,
208
- CORE_TOOLS,
209
- CustomJsonAdapter,
210
- DEFAULT_SEARCH_CONFIG,
211
- FragmentsJsonAdapter,
212
- INFRA_TOOLS,
213
- ToolRegistry,
214
- VIEWER_TOOLS,
215
- createMcpServer,
216
- createSandboxServer,
217
- executeWithMiddleware,
218
- generateRulesFiles,
219
- loadConfigFile,
220
- startMcpServer,
221
- telemetryMiddleware
1213
+ FragmentsMcpServer,
1214
+ JSONRPC_VERSION,
1215
+ LATEST_PROTOCOL_VERSION,
1216
+ MCP_APP_RESOURCES,
1217
+ MCP_TOOLS,
1218
+ McpProtocolError,
1219
+ MemoryTaskStore,
1220
+ createFixtureProvider,
1221
+ fixtureConformResult,
1222
+ fixturePrimitives,
1223
+ getResource,
1224
+ getTool,
1225
+ handleMcpHttpRequest,
1226
+ handleMcpJsonRpc,
1227
+ listResources,
1228
+ listToolDescriptors
222
1229
  };
223
1230
  //# sourceMappingURL=index.js.map