@mp3wizard/figma-console-mcp 1.22.5 → 1.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +15 -38
  2. package/dist/cloudflare/core/cloud-websocket-connector.js +19 -17
  3. package/dist/cloudflare/core/design-code-tools.js +23 -39
  4. package/dist/cloudflare/core/diff/changelog-formatter.js +275 -0
  5. package/dist/cloudflare/core/diff/diff-engine.js +334 -0
  6. package/dist/cloudflare/core/diff/property-compare.js +36 -0
  7. package/dist/cloudflare/core/diff/version-cache.js +74 -0
  8. package/dist/cloudflare/core/figma-api.js +19 -0
  9. package/dist/cloudflare/core/figma-tools.js +15 -6
  10. package/dist/cloudflare/core/version-tools.js +1014 -0
  11. package/dist/cloudflare/core/websocket-connector.js +24 -18
  12. package/dist/cloudflare/index.js +17 -13
  13. package/dist/core/design-code-tools.d.ts +1 -12
  14. package/dist/core/design-code-tools.d.ts.map +1 -1
  15. package/dist/core/design-code-tools.js +23 -39
  16. package/dist/core/design-code-tools.js.map +1 -1
  17. package/dist/core/diff/changelog-formatter.d.ts +35 -0
  18. package/dist/core/diff/changelog-formatter.d.ts.map +1 -0
  19. package/dist/core/diff/changelog-formatter.js +276 -0
  20. package/dist/core/diff/changelog-formatter.js.map +1 -0
  21. package/dist/core/diff/diff-engine.d.ts +113 -0
  22. package/dist/core/diff/diff-engine.d.ts.map +1 -0
  23. package/dist/core/diff/diff-engine.js +335 -0
  24. package/dist/core/diff/diff-engine.js.map +1 -0
  25. package/dist/core/diff/property-compare.d.ts +19 -0
  26. package/dist/core/diff/property-compare.d.ts.map +1 -0
  27. package/dist/core/diff/property-compare.js +37 -0
  28. package/dist/core/diff/property-compare.js.map +1 -0
  29. package/dist/core/diff/version-cache.d.ts +40 -0
  30. package/dist/core/diff/version-cache.d.ts.map +1 -0
  31. package/dist/core/diff/version-cache.js +75 -0
  32. package/dist/core/diff/version-cache.js.map +1 -0
  33. package/dist/core/figma-api.d.ts +29 -0
  34. package/dist/core/figma-api.d.ts.map +1 -1
  35. package/dist/core/figma-api.js +19 -0
  36. package/dist/core/figma-api.js.map +1 -1
  37. package/dist/core/figma-tools.d.ts.map +1 -1
  38. package/dist/core/figma-tools.js +15 -6
  39. package/dist/core/figma-tools.js.map +1 -1
  40. package/dist/core/version-tools.d.ts +30 -0
  41. package/dist/core/version-tools.d.ts.map +1 -0
  42. package/dist/core/version-tools.js +1015 -0
  43. package/dist/core/version-tools.js.map +1 -0
  44. package/dist/core/websocket-connector.d.ts.map +1 -1
  45. package/dist/core/websocket-connector.js +24 -18
  46. package/dist/core/websocket-connector.js.map +1 -1
  47. package/dist/local.d.ts.map +1 -1
  48. package/dist/local.js +8 -0
  49. package/dist/local.js.map +1 -1
  50. package/figma-desktop-bridge/code.js +1 -1
  51. package/package.json +108 -1
@@ -33,26 +33,32 @@ export class WebSocketConnector {
33
33
  return this.wsServer.sendCommand('GET_VARIABLES_DATA', {}, 10000, fileKey);
34
34
  }
35
35
  async getVariables(fileKey) {
36
- // Execute the same variables-fetching code in the plugin worker context
36
+ // Execute the same variables-fetching code in the plugin worker context.
37
+ //
38
+ // IMPORTANT: Do NOT wrap this in an inner `(async () => { ... })()` IIFE.
39
+ // figma-desktop-bridge/code.js already wraps every EXECUTE_CODE payload in
40
+ // `(async function() { <code> })()`. An inner IIFE turns `return X` into a
41
+ // statement-expression that builds (but doesn't return) a Promise — the
42
+ // outer async returns undefined, and the result is silently dropped. See
43
+ // issue #68. The bare try/catch with top-level `return` is the contract
44
+ // code.js expects.
37
45
  const code = `
38
- (async () => {
39
- try {
40
- if (typeof figma === 'undefined') {
41
- throw new Error('Figma API not available in this context');
42
- }
43
- const variables = await figma.variables.getLocalVariablesAsync();
44
- const collections = await figma.variables.getLocalVariableCollectionsAsync();
45
- return {
46
- success: true,
47
- timestamp: Date.now(),
48
- fileMetadata: { fileName: figma.root.name, fileKey: figma.fileKey || null },
49
- variables: variables.map(function(v) { return { id: v.id, name: v.name, key: v.key, resolvedType: v.resolvedType, valuesByMode: v.valuesByMode, variableCollectionId: v.variableCollectionId, scopes: v.scopes, description: v.description, hiddenFromPublishing: v.hiddenFromPublishing }; }),
50
- variableCollections: collections.map(function(c) { return { id: c.id, name: c.name, key: c.key, modes: c.modes, defaultModeId: c.defaultModeId, variableIds: c.variableIds }; })
51
- };
52
- } catch (error) {
53
- return { success: false, error: error.message };
46
+ try {
47
+ if (typeof figma === 'undefined') {
48
+ throw new Error('Figma API not available in this context');
54
49
  }
55
- })()
50
+ const variables = await figma.variables.getLocalVariablesAsync();
51
+ const collections = await figma.variables.getLocalVariableCollectionsAsync();
52
+ return {
53
+ success: true,
54
+ timestamp: Date.now(),
55
+ fileMetadata: { fileName: figma.root.name, fileKey: figma.fileKey || null },
56
+ variables: variables.map(function(v) { return { id: v.id, name: v.name, key: v.key, resolvedType: v.resolvedType, valuesByMode: v.valuesByMode, variableCollectionId: v.variableCollectionId, scopes: v.scopes, description: v.description, hiddenFromPublishing: v.hiddenFromPublishing }; }),
57
+ variableCollections: collections.map(function(c) { return { id: c.id, name: c.name, key: c.key, modes: c.modes, defaultModeId: c.defaultModeId, variableIds: c.variableIds }; })
58
+ };
59
+ } catch (error) {
60
+ return { success: false, error: error.message };
61
+ }
56
62
  `;
57
63
  return this.wsServer.sendCommand('EXECUTE_CODE', { code, timeout: 30000 }, 32000, fileKey);
58
64
  }
@@ -20,6 +20,7 @@ import { FigmaAPI, extractFileKey } from "./core/figma-api.js";
20
20
  import { registerFigmaAPITools } from "./core/figma-tools.js";
21
21
  import { registerDesignCodeTools } from "./core/design-code-tools.js";
22
22
  import { registerCommentTools } from "./core/comment-tools.js";
23
+ import { registerVersionTools } from "./core/version-tools.js";
23
24
  import { registerAnnotationTools } from "./core/annotation-tools.js";
24
25
  import { registerDeepComponentTools } from "./core/deep-component-tools.js";
25
26
  import { registerDesignSystemTools } from "./core/design-system-tools.js";
@@ -69,7 +70,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
69
70
  super(...arguments);
70
71
  this.server = new McpServer({
71
72
  name: "Figma Console MCP",
72
- version: "1.22.0",
73
+ version: "1.23.0",
73
74
  });
74
75
  this.browserManager = null;
75
76
  this.consoleMonitor = null;
@@ -793,6 +794,8 @@ export class FigmaConsoleMCPv3 extends McpAgent {
793
794
  { isRemoteMode: true }, getCloudDesktopConnector);
794
795
  // Register Comment tools
795
796
  registerCommentTools(this.server, async () => await this.getFigmaAPI(), () => this.browserManager?.getCurrentUrl() || null, { isRemoteMode: true });
797
+ // Register Version History tools
798
+ registerVersionTools(this.server, async () => await this.getFigmaAPI(), () => this.browserManager?.getCurrentUrl() || null, { isRemoteMode: true });
796
799
  // Register Design System Kit tool
797
800
  registerDesignSystemTools(this.server, async () => await this.getFigmaAPI(), () => this.browserManager?.getCurrentUrl() || null, undefined, // variablesCache
798
801
  { isRemoteMode: true });
@@ -1055,7 +1058,7 @@ export default {
1055
1058
  });
1056
1059
  const statelessServer = new McpServer({
1057
1060
  name: "Figma Console MCP",
1058
- version: "1.22.0",
1061
+ version: "1.23.0",
1059
1062
  });
1060
1063
  // ================================================================
1061
1064
  // Cloud Write Relay — Pairing Tool (stateless /mcp path)
@@ -1151,6 +1154,7 @@ export default {
1151
1154
  registerDesignCodeTools(statelessServer, async () => statelessApi, getCloudFileUrl, new Map(), // Fresh variables cache per request
1152
1155
  { isRemoteMode: true }, getCloudDesktopConnector);
1153
1156
  registerCommentTools(statelessServer, async () => statelessApi, getCloudFileUrl);
1157
+ registerVersionTools(statelessServer, async () => statelessApi, getCloudFileUrl);
1154
1158
  registerDesignSystemTools(statelessServer, async () => statelessApi, getCloudFileUrl, new Map(), // Fresh variables cache per request
1155
1159
  { isRemoteMode: true });
1156
1160
  await statelessServer.connect(transport);
@@ -1172,7 +1176,7 @@ export default {
1172
1176
  const metadata = {
1173
1177
  resource: url.origin,
1174
1178
  authorization_servers: [`${url.origin}/`],
1175
- scopes_supported: ["file_content:read", "file_variables:read", "library_content:read"],
1179
+ scopes_supported: ["file_content:read", "file_versions:read", "file_variables:read", "file_comments:read", "file_comments:write", "library_content:read"],
1176
1180
  bearer_methods_supported: ["header"],
1177
1181
  resource_signing_alg_values_supported: ["RS256"]
1178
1182
  };
@@ -1191,7 +1195,7 @@ export default {
1191
1195
  authorization_endpoint: `${url.origin}/authorize`,
1192
1196
  token_endpoint: `${url.origin}/token`,
1193
1197
  registration_endpoint: `${url.origin}/oauth/register`,
1194
- scopes_supported: ["file_content:read", "file_variables:read", "library_content:read"],
1198
+ scopes_supported: ["file_content:read", "file_versions:read", "file_variables:read", "file_comments:read", "file_comments:write", "library_content:read"],
1195
1199
  response_types_supported: ["code"],
1196
1200
  grant_types_supported: ["authorization_code", "refresh_token"],
1197
1201
  token_endpoint_auth_methods_supported: ["client_secret_basic", "client_secret_post"],
@@ -1254,7 +1258,7 @@ export default {
1254
1258
  const figmaAuthUrl = new URL("https://www.figma.com/oauth");
1255
1259
  figmaAuthUrl.searchParams.set("client_id", env.FIGMA_OAUTH_CLIENT_ID);
1256
1260
  figmaAuthUrl.searchParams.set("redirect_uri", `${oauthOrigin}/oauth/callback`);
1257
- figmaAuthUrl.searchParams.set("scope", "file_content:read,file_variables:read,library_content:read");
1261
+ figmaAuthUrl.searchParams.set("scope", "file_content:read,file_versions:read,file_variables:read,file_comments:read,file_comments:write,library_content:read");
1258
1262
  figmaAuthUrl.searchParams.set("state", stateToken);
1259
1263
  figmaAuthUrl.searchParams.set("response_type", "code");
1260
1264
  return Response.redirect(figmaAuthUrl.toString(), 302);
@@ -1295,7 +1299,7 @@ export default {
1295
1299
  token_type: "Bearer",
1296
1300
  expires_in: Math.max(0, Math.floor((tokenData.expiresAt - Date.now()) / 1000)),
1297
1301
  refresh_token: tokenData.refreshToken,
1298
- scope: "file_content:read file_variables:read library_content:read"
1302
+ scope: "file_content:read file_versions:read file_variables:read file_comments:read file_comments:write library_content:read"
1299
1303
  }), {
1300
1304
  headers: {
1301
1305
  "Content-Type": "application/json",
@@ -1372,7 +1376,7 @@ export default {
1372
1376
  token_type: "Bearer",
1373
1377
  expires_in: tokenData.expires_in,
1374
1378
  refresh_token: tokenData.refresh_token || refreshToken,
1375
- scope: "file_content:read file_variables:read library_content:read"
1379
+ scope: "file_content:read file_versions:read file_variables:read file_comments:read file_comments:write library_content:read"
1376
1380
  }), {
1377
1381
  headers: {
1378
1382
  "Content-Type": "application/json",
@@ -1444,7 +1448,7 @@ export default {
1444
1448
  const figmaAuthUrl = new URL("https://www.figma.com/oauth");
1445
1449
  figmaAuthUrl.searchParams.set("client_id", env.FIGMA_OAUTH_CLIENT_ID);
1446
1450
  figmaAuthUrl.searchParams.set("redirect_uri", redirectUri);
1447
- figmaAuthUrl.searchParams.set("scope", "file_content:read,file_variables:read,library_content:read");
1451
+ figmaAuthUrl.searchParams.set("scope", "file_content:read,file_versions:read,file_variables:read,file_comments:read,file_comments:write,library_content:read");
1448
1452
  figmaAuthUrl.searchParams.set("state", stateToken);
1449
1453
  figmaAuthUrl.searchParams.set("response_type", "code");
1450
1454
  return Response.redirect(figmaAuthUrl.toString(), 302);
@@ -1690,7 +1694,7 @@ export default {
1690
1694
  return new Response(JSON.stringify({
1691
1695
  status: "healthy",
1692
1696
  service: "Figma Console MCP",
1693
- version: "1.22.0",
1697
+ version: "1.23.0",
1694
1698
  endpoints: {
1695
1699
  mcp: ["/sse", "/mcp"],
1696
1700
  oauth_mcp_spec: ["/.well-known/oauth-authorization-server", "/authorize", "/token", "/oauth/register"],
@@ -1736,13 +1740,13 @@ export default {
1736
1740
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1737
1741
  <title>Figma Console MCP - The Most Comprehensive MCP Server for Figma</title>
1738
1742
  <link rel="icon" type="image/svg+xml" href="https://docs.figma-console-mcp.southleft.com/favicon.svg">
1739
- <meta name="description" content="Turn your Figma design system into a living API. 94+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
1743
+ <meta name="description" content="Turn your Figma design system into a living API. 100+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
1740
1744
 
1741
1745
  <!-- Open Graph -->
1742
1746
  <meta property="og:type" content="website">
1743
1747
  <meta property="og:url" content="https://figma-console-mcp.southleft.com">
1744
1748
  <meta property="og:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
1745
- <meta property="og:description" content="The most comprehensive MCP server for Figma. 94+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1749
+ <meta property="og:description" content="The most comprehensive MCP server for Figma. 100+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1746
1750
  <meta property="og:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
1747
1751
  <meta property="og:image:width" content="1200">
1748
1752
  <meta property="og:image:height" content="630">
@@ -1750,7 +1754,7 @@ export default {
1750
1754
  <!-- Twitter -->
1751
1755
  <meta name="twitter:card" content="summary_large_image">
1752
1756
  <meta name="twitter:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
1753
- <meta name="twitter:description" content="The most comprehensive MCP server for Figma. 94+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1757
+ <meta name="twitter:description" content="The most comprehensive MCP server for Figma. 100+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1754
1758
  <meta name="twitter:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
1755
1759
 
1756
1760
  <meta name="theme-color" content="#0D9488">
@@ -2637,7 +2641,7 @@ export default {
2637
2641
  <div class="grid-cell showcase-cell rule-left">
2638
2642
  <div class="showcase-label">What AI Can Access</div>
2639
2643
  <div class="showcase-stat">
2640
- <span class="number">94+</span>
2644
+ <span class="number">100+</span>
2641
2645
  <span class="label">MCP tools for Figma</span>
2642
2646
  </div>
2643
2647
  <div class="capability-list">
@@ -6,17 +6,7 @@
6
6
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
7
  import type { FigmaAPI } from "./figma-api.js";
8
8
  import type { CompanyDocsContentEntry } from "./types/design-code.js";
9
- /** Convert Figma RGBA (0-1 floats) to hex string */
10
- export declare function figmaRGBAToHex(color: {
11
- r: number;
12
- g: number;
13
- b: number;
14
- a?: number;
15
- }): string;
16
- /** Normalize a color string for comparison (uppercase hex without alpha if fully opaque) */
17
- export declare function normalizeColor(color: string): string;
18
- /** Compare numeric values with a tolerance */
19
- export declare function numericClose(a: number, b: number, tolerance?: number): boolean;
9
+ export { figmaRGBAToHex, normalizeColor, numericClose } from "./diff/property-compare.js";
20
10
  /** Calculate parity score from discrepancy counts */
21
11
  export declare function calculateParityScore(critical: number, major: number, minor: number, info: number): number;
22
12
  /** Split markdown by H2 headers for platforms that need chunking */
@@ -123,5 +113,4 @@ export declare function registerDesignCodeTools(server: McpServer, getFigmaAPI:
123
113
  }>, options?: {
124
114
  isRemoteMode?: boolean;
125
115
  }, getDesktopConnector?: () => Promise<any>): void;
126
- export {};
127
116
  //# sourceMappingURL=design-code-tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"design-code-tools.d.ts","sourceRoot":"","sources":["../../src/core/design-code-tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"design-code-tools.d.ts","sourceRoot":"","sources":["../../src/core/design-code-tools.ts"],"names":[],"mappings":"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"}
@@ -12,35 +12,9 @@ const enrichmentService = new EnrichmentService(logger);
12
12
  // ============================================================================
13
13
  // Shared Helpers
14
14
  // ============================================================================
15
- /** Convert Figma RGBA (0-1 floats) to hex string */
16
- export function figmaRGBAToHex(color) {
17
- const r = Math.round(color.r * 255);
18
- const g = Math.round(color.g * 255);
19
- const b = Math.round(color.b * 255);
20
- const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`.toUpperCase();
21
- if (color.a !== undefined && color.a < 1) {
22
- const a = Math.round(color.a * 255);
23
- return `${hex}${a.toString(16).padStart(2, "0")}`;
24
- }
25
- return hex;
26
- }
27
- /** Normalize a color string for comparison (uppercase hex without alpha if fully opaque) */
28
- export function normalizeColor(color) {
29
- let c = color.trim().toUpperCase();
30
- // Strip alpha if fully opaque (FF)
31
- if (c.length === 9 && c.endsWith("FF")) {
32
- c = c.slice(0, 7);
33
- }
34
- // Expand shorthand (#RGB -> #RRGGBB)
35
- if (/^#[0-9A-F]{3}$/.test(c)) {
36
- c = `#${c[1]}${c[1]}${c[2]}${c[2]}${c[3]}${c[3]}`;
37
- }
38
- return c;
39
- }
40
- /** Compare numeric values with a tolerance */
41
- export function numericClose(a, b, tolerance = 1) {
42
- return Math.abs(a - b) <= tolerance;
43
- }
15
+ // Re-exported from the shared diff module so existing imports continue to work.
16
+ export { figmaRGBAToHex, normalizeColor, numericClose } from "./diff/property-compare.js";
17
+ import { figmaRGBAToHex, normalizeColor, numericClose } from "./diff/property-compare.js";
44
18
  /** Calculate parity score from discrepancy counts */
45
19
  export function calculateParityScore(critical, major, minor, info) {
46
20
  return Math.max(0, 100 - (critical * 15 + major * 8 + minor * 3 + info * 1));
@@ -2180,7 +2154,11 @@ const codeSpecSchema = z.object({
2180
2154
  semanticElement: z.string().optional().describe("Semantic HTML element (e.g., 'button', 'a', 'input')"),
2181
2155
  supportsDisabled: z.boolean().optional().describe("Whether code supports disabled/aria-disabled state"),
2182
2156
  supportsError: z.boolean().optional().describe("Whether code supports aria-invalid/error state"),
2183
- renderedSize: z.tuple([z.number(), z.number()]).optional().describe("Rendered size [width, height] in px"),
2157
+ // NOTE: Use array-with-length, NOT z.tuple tuples emit JSON Schema `items: [...]`
2158
+ // (array of schemas), which Gemini's stricter Function Calling validator rejects with
2159
+ // "is not of type 'object', 'boolean'". See issue #64. A constrained array emits
2160
+ // `items: { type: 'number' }` which all major MCP clients accept.
2161
+ renderedSize: z.array(z.number()).min(2).max(2).optional().describe("Rendered size [width, height] in px"),
2184
2162
  }).optional().describe("Accessibility properties from code. Tip: use figma_scan_code_accessibility with mapToCodeSpec:true to auto-generate this from component HTML."),
2185
2163
  metadata: z.object({
2186
2164
  name: z.string().optional(),
@@ -2350,16 +2328,22 @@ export function registerDesignCodeTools(server, getFigmaAPI, getCurrentUrl, vari
2350
2328
  logger.warn("Enrichment failed, proceeding without token data");
2351
2329
  }
2352
2330
  }
2331
+ // Cast to the structural CodeSpec interface. The Zod schema infers
2332
+ // `accessibility.renderedSize` as `number[]` (post-#64 fix uses
2333
+ // `z.array(z.number()).min(2).max(2)` for Gemini compat), but at runtime
2334
+ // the validator guarantees exactly two numbers, matching CodeSpec's
2335
+ // `[number, number]`. TypeScript can't bridge the inference gap.
2336
+ const codeSpecTyped = codeSpec;
2353
2337
  // Run all comparators (use nodeForVisual for design properties, nodeForAPI for component API)
2354
2338
  const discrepancies = [];
2355
- compareVisual(nodeForVisual, codeSpec, discrepancies);
2356
- compareSpacing(nodeForVisual, codeSpec, discrepancies);
2357
- compareTypography(nodeForVisual, codeSpec, discrepancies);
2358
- compareTokens(enrichedData, codeSpec, discrepancies);
2359
- compareComponentAPI(nodeForAPI, codeSpec, discrepancies);
2360
- compareAccessibility(node, codeSpec, discrepancies);
2361
- compareNaming(node, codeSpec, discrepancies);
2362
- compareMetadata(node, componentMeta, codeSpec, discrepancies);
2339
+ compareVisual(nodeForVisual, codeSpecTyped, discrepancies);
2340
+ compareSpacing(nodeForVisual, codeSpecTyped, discrepancies);
2341
+ compareTypography(nodeForVisual, codeSpecTyped, discrepancies);
2342
+ compareTokens(enrichedData, codeSpecTyped, discrepancies);
2343
+ compareComponentAPI(nodeForAPI, codeSpecTyped, discrepancies);
2344
+ compareAccessibility(node, codeSpecTyped, discrepancies);
2345
+ compareNaming(node, codeSpecTyped, discrepancies);
2346
+ compareMetadata(node, componentMeta, codeSpecTyped, discrepancies);
2363
2347
  // Sort by severity
2364
2348
  const severityOrder = {
2365
2349
  critical: 0,
@@ -2410,7 +2394,7 @@ export function registerDesignCodeTools(server, getFigmaAPI, getCurrentUrl, vari
2410
2394
  : [],
2411
2395
  tokenCoverage: enrichedData?.token_coverage,
2412
2396
  },
2413
- codeData: codeSpec,
2397
+ codeData: codeSpecTyped,
2414
2398
  };
2415
2399
  return {
2416
2400
  content: [{ type: "text", text: JSON.stringify(result) }],