@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.
- package/README.md +74 -17
- package/dist/cloudflare/index.js +3 -3
- package/dist/core/annotation-tools.d.ts +14 -0
- package/dist/core/annotation-tools.d.ts.map +1 -0
- package/dist/core/annotation-tools.js +231 -0
- package/dist/core/annotation-tools.js.map +1 -0
- package/dist/core/deep-component-tools.d.ts +14 -0
- package/dist/core/deep-component-tools.d.ts.map +1 -0
- package/dist/core/deep-component-tools.js +129 -0
- package/dist/core/deep-component-tools.js.map +1 -0
- package/dist/core/design-code-tools.d.ts.map +1 -1
- package/dist/core/design-code-tools.js +65 -7
- package/dist/core/design-code-tools.js.map +1 -1
- package/dist/core/enrichment/enrichment-service.d.ts.map +1 -1
- package/dist/core/enrichment/enrichment-service.js +108 -12
- package/dist/core/enrichment/enrichment-service.js.map +1 -1
- package/dist/core/figjam-tools.d.ts +8 -0
- package/dist/core/figjam-tools.d.ts.map +1 -0
- package/dist/core/figjam-tools.js +486 -0
- package/dist/core/figjam-tools.js.map +1 -0
- package/dist/core/figma-api.d.ts +1 -1
- package/dist/core/figma-api.d.ts.map +1 -1
- package/dist/core/figma-api.js +2 -2
- package/dist/core/figma-api.js.map +1 -1
- package/dist/core/figma-connector.d.ts +102 -0
- package/dist/core/figma-connector.d.ts.map +1 -1
- package/dist/core/figma-desktop-connector.d.ts +43 -0
- package/dist/core/figma-desktop-connector.d.ts.map +1 -1
- package/dist/core/figma-desktop-connector.js +108 -0
- package/dist/core/figma-desktop-connector.js.map +1 -1
- package/dist/core/figma-tools.d.ts.map +1 -1
- package/dist/core/figma-tools.js +434 -49
- package/dist/core/figma-tools.js.map +1 -1
- package/dist/core/port-discovery.d.ts +21 -0
- package/dist/core/port-discovery.d.ts.map +1 -1
- package/dist/core/port-discovery.js +88 -0
- package/dist/core/port-discovery.js.map +1 -1
- package/dist/core/slides-tools.d.ts +8 -0
- package/dist/core/slides-tools.d.ts.map +1 -0
- package/dist/core/slides-tools.js +608 -0
- package/dist/core/slides-tools.js.map +1 -0
- package/dist/core/types/design-code.d.ts +1 -0
- package/dist/core/types/design-code.d.ts.map +1 -1
- package/dist/core/websocket-connector.d.ts +102 -0
- package/dist/core/websocket-connector.d.ts.map +1 -1
- package/dist/core/websocket-connector.js +93 -0
- package/dist/core/websocket-connector.js.map +1 -1
- package/dist/core/websocket-server.d.ts +6 -0
- package/dist/core/websocket-server.d.ts.map +1 -1
- package/dist/core/websocket-server.js +11 -0
- package/dist/core/websocket-server.js.map +1 -1
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +79 -11
- package/dist/local.js.map +1 -1
- package/figma-desktop-bridge/code.js +1945 -44
- package/figma-desktop-bridge/icon.png +0 -0
- package/figma-desktop-bridge/manifest.json +1 -1
- package/figma-desktop-bridge/ui-full.html +80 -0
- package/figma-desktop-bridge/ui.html +226 -0
- 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.
|
|
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
|
|
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
|
|
3207
|
-
3. Use figma_instantiate_component with the
|
|
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:
|
|
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
|
|
3396
|
-
example:
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
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",
|