@mp3wizard/figma-console-mcp 1.15.4 → 1.19.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.
Files changed (60) hide show
  1. package/README.md +74 -17
  2. package/dist/cloudflare/index.js +3 -3
  3. package/dist/core/annotation-tools.d.ts +14 -0
  4. package/dist/core/annotation-tools.d.ts.map +1 -0
  5. package/dist/core/annotation-tools.js +231 -0
  6. package/dist/core/annotation-tools.js.map +1 -0
  7. package/dist/core/deep-component-tools.d.ts +14 -0
  8. package/dist/core/deep-component-tools.d.ts.map +1 -0
  9. package/dist/core/deep-component-tools.js +129 -0
  10. package/dist/core/deep-component-tools.js.map +1 -0
  11. package/dist/core/design-code-tools.d.ts.map +1 -1
  12. package/dist/core/design-code-tools.js +65 -7
  13. package/dist/core/design-code-tools.js.map +1 -1
  14. package/dist/core/enrichment/enrichment-service.d.ts.map +1 -1
  15. package/dist/core/enrichment/enrichment-service.js +108 -12
  16. package/dist/core/enrichment/enrichment-service.js.map +1 -1
  17. package/dist/core/figjam-tools.d.ts +8 -0
  18. package/dist/core/figjam-tools.d.ts.map +1 -0
  19. package/dist/core/figjam-tools.js +486 -0
  20. package/dist/core/figjam-tools.js.map +1 -0
  21. package/dist/core/figma-api.d.ts +1 -1
  22. package/dist/core/figma-api.d.ts.map +1 -1
  23. package/dist/core/figma-api.js +2 -2
  24. package/dist/core/figma-api.js.map +1 -1
  25. package/dist/core/figma-connector.d.ts +102 -0
  26. package/dist/core/figma-connector.d.ts.map +1 -1
  27. package/dist/core/figma-desktop-connector.d.ts +43 -0
  28. package/dist/core/figma-desktop-connector.d.ts.map +1 -1
  29. package/dist/core/figma-desktop-connector.js +108 -0
  30. package/dist/core/figma-desktop-connector.js.map +1 -1
  31. package/dist/core/figma-tools.d.ts.map +1 -1
  32. package/dist/core/figma-tools.js +434 -49
  33. package/dist/core/figma-tools.js.map +1 -1
  34. package/dist/core/port-discovery.d.ts +21 -0
  35. package/dist/core/port-discovery.d.ts.map +1 -1
  36. package/dist/core/port-discovery.js +88 -0
  37. package/dist/core/port-discovery.js.map +1 -1
  38. package/dist/core/slides-tools.d.ts +8 -0
  39. package/dist/core/slides-tools.d.ts.map +1 -0
  40. package/dist/core/slides-tools.js +608 -0
  41. package/dist/core/slides-tools.js.map +1 -0
  42. package/dist/core/types/design-code.d.ts +1 -0
  43. package/dist/core/types/design-code.d.ts.map +1 -1
  44. package/dist/core/websocket-connector.d.ts +102 -0
  45. package/dist/core/websocket-connector.d.ts.map +1 -1
  46. package/dist/core/websocket-connector.js +93 -0
  47. package/dist/core/websocket-connector.js.map +1 -1
  48. package/dist/core/websocket-server.d.ts +6 -0
  49. package/dist/core/websocket-server.d.ts.map +1 -1
  50. package/dist/core/websocket-server.js +11 -0
  51. package/dist/core/websocket-server.js.map +1 -1
  52. package/dist/local.d.ts.map +1 -1
  53. package/dist/local.js +79 -11
  54. package/dist/local.js.map +1 -1
  55. package/figma-desktop-bridge/code.js +1945 -44
  56. package/figma-desktop-bridge/icon.png +0 -0
  57. package/figma-desktop-bridge/manifest.json +1 -1
  58. package/figma-desktop-bridge/ui-full.html +80 -0
  59. package/figma-desktop-bridge/ui.html +226 -0
  60. package/package.json +2 -2
package/dist/local.js CHANGED
@@ -27,13 +27,17 @@ import { FigmaAPI, extractFileKey, extractFigmaUrlInfo, formatVariables, } from
27
27
  import { registerFigmaAPITools } from "./core/figma-tools.js";
28
28
  import { registerDesignCodeTools } from "./core/design-code-tools.js";
29
29
  import { registerCommentTools } from "./core/comment-tools.js";
30
+ import { registerAnnotationTools } from "./core/annotation-tools.js";
31
+ import { registerDeepComponentTools } from "./core/deep-component-tools.js";
30
32
  import { registerDesignSystemTools } from "./core/design-system-tools.js";
31
33
  import { FigmaDesktopConnector } from "./core/figma-desktop-connector.js";
32
34
  import { FigmaWebSocketServer } from "./core/websocket-server.js";
33
35
  import { WebSocketConnector } from "./core/websocket-connector.js";
34
- import { DEFAULT_WS_PORT, getPortRange, advertisePort, unadvertisePort, registerPortCleanup, discoverActiveInstances, cleanupStalePortFiles, cleanupOrphanedProcesses, refreshPortAdvertisement, HEARTBEAT_INTERVAL_MS, } from "./core/port-discovery.js";
36
+ import { DEFAULT_WS_PORT, getPortRange, advertisePort, unadvertisePort, registerPortCleanup, discoverActiveInstances, cleanupStalePortFiles, cleanupOrphanedProcesses, evictOldestInstance, refreshPortAdvertisement, HEARTBEAT_INTERVAL_MS, } from "./core/port-discovery.js";
35
37
  import { registerTokenBrowserApp } from "./apps/token-browser/server.js";
36
38
  import { registerDesignSystemDashboardApp } from "./apps/design-system-dashboard/server.js";
39
+ import { registerFigJamTools } from "./core/figjam-tools.js";
40
+ import { registerSlidesTools } from "./core/slides-tools.js";
37
41
  const logger = createChildLogger({ component: "local-server" });
38
42
  /**
39
43
  * Copy plugin files to a stable directory (~/.figma-console-mcp/plugin/).
@@ -1052,6 +1056,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
1052
1056
  currentFileName: currentFileName ||
1053
1057
  "(unable to retrieve - Desktop Bridge may need to be opened)",
1054
1058
  currentFileKey: currentFileKey || undefined,
1059
+ editorType: this.wsServer?.getEditorType() || "figma",
1055
1060
  monitoredPageUrl: currentUrl,
1056
1061
  monitorWorkerCount: monitorStatus?.workerCount ?? 0,
1057
1062
  transport: {
@@ -1097,6 +1102,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
1097
1102
  fileName: f.fileName,
1098
1103
  fileKey: f.fileKey,
1099
1104
  currentPage: f.currentPage,
1105
+ editorType: f.editorType || 'figma',
1100
1106
  isActive: f.isActive,
1101
1107
  connectedAt: new Date(f.connectedAt).toISOString(),
1102
1108
  }));
@@ -1122,7 +1128,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
1122
1128
  : this.wsStartupError?.code === "EADDRINUSE"
1123
1129
  ? `❌ All WebSocket ports ${this.wsPreferredPort}-${this.wsPreferredPort + 9} are in use`
1124
1130
  : this.wsActualPort !== null && this.wsActualPort !== this.wsPreferredPort
1125
- ? `❌ WebSocket server running on port ${this.wsActualPort} (fallback) but no plugin connected. Re-import the Desktop Bridge plugin in Figma to enable multi-port scanning.`
1131
+ ? `❌ WebSocket server running on port ${this.wsActualPort} (fallback) but no plugin connected. Restart the Desktop Bridge plugin in Figma to reconnect.`
1126
1132
  : "❌ No connection to Figma Desktop",
1127
1133
  setupInstructions: !setupValid
1128
1134
  ? this.wsStartupError?.code === "EADDRINUSE"
@@ -1138,7 +1144,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
1138
1144
  ? this.wsStartupError?.code === "EADDRINUSE"
1139
1145
  ? `All WebSocket ports in range ${this.wsPreferredPort}-${this.wsPreferredPort + 9} are in use — most likely multiple Claude Desktop tabs or terminal sessions are running the Figma Console MCP server. Ask the user to close some sessions and restart.`
1140
1146
  : this.wsActualPort !== null && this.wsActualPort !== this.wsPreferredPort
1141
- ? `Server is running on fallback port ${this.wsActualPort} (port ${this.wsPreferredPort} was taken by another instance). The Desktop Bridge plugin is not connected — most likely because the plugin has old code that only scans port ${this.wsPreferredPort}. TELL THE USER: Re-import the Desktop Bridge plugin in Figma (Plugins → Development → Import plugin from manifest) to update it with multi-port scanning support. This is a one-time step.${this.getPluginPath() ? ' The manifest file is at: ' + this.getPluginPath() : ''}`
1147
+ ? `Server is running on fallback port ${this.wsActualPort} (port ${this.wsPreferredPort} was taken by another instance). The Desktop Bridge plugin is not connected. TELL THE USER: Close and reopen the Desktop Bridge plugin in Figma to reconnect. The plugin's bootloader will automatically scan all ports in the range.`
1142
1148
  : `No connection to Figma Desktop. Open the Desktop Bridge plugin in Figma to connect.${this.getPluginPath() ? ' Plugin manifest: ' + this.getPluginPath() : ''}`
1143
1149
  : activeTransport === "websocket"
1144
1150
  ? `Connected via WebSocket Bridge to "${currentFileName || "unknown file"}" on port ${this.wsActualPort}. All design tools and console monitoring tools are available. Console logs are captured from the plugin sandbox (code.js). IMPORTANT: Always verify the file name before destructive operations when multiple files have the plugin open.`
@@ -1316,6 +1322,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
1316
1322
  if ('opacity' in node) info.opacity = node.opacity;
1317
1323
  if ('cornerRadius' in node) info.cornerRadius = node.cornerRadius;
1318
1324
  if ('componentProperties' in node) info.componentProperties = node.componentProperties;
1325
+ if ('annotations' in node && node.annotations && node.annotations.length > 0) info.annotations = node.annotations;
1319
1326
  results.push(info);
1320
1327
  }
1321
1328
  return results;`, 10000);
@@ -3115,6 +3122,11 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
3115
3122
  **For local components:** Pass BOTH componentKey AND nodeId together. Most local/unpublished components require nodeId.
3116
3123
  **For library components:** Pass just the componentKey from figma_get_library_components or figma_search_components (with libraryFileKey). The component will be imported from the published library automatically.
3117
3124
 
3125
+ **CRITICAL: Use VARIANT keys, not COMPONENT_SET keys!**
3126
+ When importing from a published library, use the key of a specific variant (type: "COMPONENT"), NOT the parent component set key (type: "COMPONENT_SET"). Component set keys will fail with importComponentByKeyAsync. In figma_get_library_components results, look inside the "variants" array for individual variant keys.
3127
+
3128
+ **Font loading:** Library components may use fonts not loaded in the current file. If instantiation fails with a font error, load the required fonts first via figma_execute before retrying (e.g., \`await figma.loadFontAsync({ family: "Geist", style: "Regular" })\`).
3129
+
3118
3130
  **IMPORTANT: Always re-search before instantiating!**
3119
3131
  NodeIds are session-specific and may be stale from previous conversations. ALWAYS search for components at the start of each design session to get current, valid identifiers.
3120
3132
 
@@ -3203,8 +3215,12 @@ After instantiating components, use figma_take_screenshot to verify the result l
3203
3215
 
3204
3216
  **WORKFLOW:**
3205
3217
  1. Call this tool with the library file's URL or file key
3206
- 2. Browse the returned components (with keys, names, variants)
3207
- 3. Use figma_instantiate_component with the componentKey to place them in the current file
3218
+ 2. Browse the returned components — results include COMPONENT_SET (with variants array) and standalone COMPONENT types
3219
+ 3. Use figma_instantiate_component with a VARIANT key (from the variants array inside a COMPONENT_SET result, NOT the component set key itself)
3220
+
3221
+ **SEARCH NOTE:** The query filter matches both component names AND descriptions. If you get unexpected results (e.g., "Accordion" when searching "Button"), verify the result name matches what you need — it may have matched on a description mention.
3222
+
3223
+ **MULTI-FILE TIP:** If you need to find a specific component and REST API search returns too many results, you can switch to the library file via figma_navigate, use figma_execute to find the exact component and its variant key, then switch back.
3208
3224
 
3209
3225
  **NOTE:** Requires FIGMA_ACCESS_TOKEN to be set (uses the Figma REST API to read the library file).`, {
3210
3226
  libraryFileUrl: z
@@ -3274,13 +3290,19 @@ After instantiating components, use figma_take_screenshot to verify the result l
3274
3290
  // Use REST API to get published components from the library file
3275
3291
  const api = await this.getFigmaAPI();
3276
3292
  // Fetch both components and component sets in parallel
3293
+ // Surface errors instead of swallowing them — token/scope issues need to be visible
3294
+ const apiErrors = [];
3277
3295
  const [componentsResponse, componentSetsResponse] = await Promise.all([
3278
3296
  api.getComponents(fileKey).catch((err) => {
3297
+ const msg = err.message || String(err);
3279
3298
  logger.warn({ error: err }, "Failed to fetch components from library");
3299
+ apiErrors.push(`Components API: ${msg}`);
3280
3300
  return { meta: { components: [] } };
3281
3301
  }),
3282
3302
  api.getComponentSets(fileKey).catch((err) => {
3303
+ const msg = err.message || String(err);
3283
3304
  logger.warn({ error: err }, "Failed to fetch component sets from library");
3305
+ apiErrors.push(`Component Sets API: ${msg}`);
3284
3306
  return { meta: { component_sets: [] } };
3285
3307
  }),
3286
3308
  ]);
@@ -3376,9 +3398,13 @@ After instantiating components, use figma_take_screenshot to verify the result l
3376
3398
  {
3377
3399
  type: "text",
3378
3400
  text: JSON.stringify({
3379
- success: true,
3401
+ success: apiErrors.length === 0,
3380
3402
  libraryFileKey: fileKey,
3381
3403
  query: query || "(all)",
3404
+ ...(apiErrors.length > 0 && {
3405
+ apiErrors,
3406
+ hint: "REST API errors occurred. Check that FIGMA_ACCESS_TOKEN is valid and has file_content:read scope. If the token is correct, the library components may not be published to a team library.",
3407
+ }),
3382
3408
  summary: {
3383
3409
  totalComponentSets: componentSets.length,
3384
3410
  totalStandaloneComponents: standaloneComponents.length,
@@ -3392,11 +3418,17 @@ After instantiating components, use figma_take_screenshot to verify the result l
3392
3418
  hasMore,
3393
3419
  },
3394
3420
  usage: {
3395
- instantiate: `To use a component: call figma_instantiate_component with the componentKey from results above.`,
3396
- example: paginatedResults[0]
3397
- ? `figma_instantiate_component({ componentKey: "${paginatedResults[0].key}" })`
3398
- : undefined,
3399
- note: "Components will be imported from the published library into the current file automatically.",
3421
+ instantiate: `To use a component: call figma_instantiate_component with a VARIANT key (not the component set key). For COMPONENT_SET results, pick a variant from the "variants" array.`,
3422
+ example: (() => {
3423
+ const first = paginatedResults[0];
3424
+ if (!first)
3425
+ return undefined;
3426
+ if (first.type === "COMPONENT_SET" && first.variants?.length > 0) {
3427
+ return `figma_instantiate_component({ componentKey: "${first.variants[0].key}" }) — using first variant of "${first.name}"`;
3428
+ }
3429
+ return `figma_instantiate_component({ componentKey: "${first.key}" })`;
3430
+ })(),
3431
+ note: "IMPORTANT: Use variant keys (type COMPONENT), not component set keys (type COMPONENT_SET). Component set keys will fail. Also pre-load any custom fonts the component uses via figma_execute before instantiating.",
3400
3432
  },
3401
3433
  }),
3402
3434
  },
@@ -4657,6 +4689,14 @@ return {
4657
4689
  registerCommentTools(this.server, () => this.getFigmaAPI(), () => this.getCurrentFileUrl());
4658
4690
  // Register Design System Kit tool
4659
4691
  registerDesignSystemTools(this.server, () => this.getFigmaAPI(), () => this.getCurrentFileUrl(), this.variablesCache);
4692
+ // Register Annotation tools (read/write design annotations via Desktop Bridge)
4693
+ registerAnnotationTools(this.server, () => this.getDesktopConnector());
4694
+ // Register Deep Component tools (full Plugin API tree extraction for code generation)
4695
+ registerDeepComponentTools(this.server, () => this.getDesktopConnector());
4696
+ // Register FigJam-specific tools (sticky notes, connectors, tables, etc.)
4697
+ registerFigJamTools(this.server, () => this.getDesktopConnector());
4698
+ // Register Figma Slides tools (slide management, transitions, content)
4699
+ registerSlidesTools(this.server, () => this.getDesktopConnector());
4660
4700
  // MCP Apps - gated behind ENABLE_MCP_APPS env var
4661
4701
  if (process.env.ENABLE_MCP_APPS === "true") {
4662
4702
  registerTokenBrowserApp(this.server, async (fileUrl) => {
@@ -5025,6 +5065,34 @@ return {
5025
5065
  break;
5026
5066
  }
5027
5067
  }
5068
+ // Phase 3: If all ports exhausted, try evicting the oldest instance and retry ONCE
5069
+ if (!boundPort && evictOldestInstance(this.wsPreferredPort)) {
5070
+ for (const port of portsToTry) {
5071
+ try {
5072
+ this.wsServer = new FigmaWebSocketServer({ port, host: wsHost });
5073
+ await this.wsServer.start();
5074
+ const addr = this.wsServer.address();
5075
+ boundPort = addr?.port ?? port;
5076
+ this.wsActualPort = boundPort;
5077
+ logger.info({ wsPort: boundPort, eviction: true }, "WebSocket bridge server started after evicting stale instance");
5078
+ advertisePort(boundPort, wsHost);
5079
+ registerPortCleanup(boundPort);
5080
+ const heartbeatPort = boundPort;
5081
+ this.wsHeartbeatTimer = setInterval(() => refreshPortAdvertisement(heartbeatPort), HEARTBEAT_INTERVAL_MS);
5082
+ this.wsHeartbeatTimer.unref();
5083
+ break;
5084
+ }
5085
+ catch (wsError) {
5086
+ const errorCode = wsError instanceof Error ? wsError.code : undefined;
5087
+ if (errorCode === "EADDRINUSE") {
5088
+ this.wsServer = null;
5089
+ continue;
5090
+ }
5091
+ this.wsServer = null;
5092
+ break;
5093
+ }
5094
+ }
5095
+ }
5028
5096
  if (!boundPort) {
5029
5097
  this.wsStartupError = {
5030
5098
  code: "EADDRINUSE",