figma-console-mcp 1.43.0 → 1.44.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 +22 -2
- package/dist/core/http-transport.d.ts +47 -0
- package/dist/core/http-transport.d.ts.map +1 -0
- package/dist/core/http-transport.js +233 -0
- package/dist/core/http-transport.js.map +1 -0
- package/dist/core/port-discovery.d.ts +4 -0
- package/dist/core/port-discovery.d.ts.map +1 -1
- package/dist/core/port-discovery.js +13 -0
- package/dist/core/port-discovery.js.map +1 -1
- package/dist/core/tokens-tools.js +1 -1
- package/dist/local.d.ts +13 -0
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +102 -42
- package/dist/local.js.map +1 -1
- package/package.json +1 -1
package/dist/local.d.ts
CHANGED
|
@@ -29,6 +29,9 @@ declare class LocalFigmaConsoleMCP {
|
|
|
29
29
|
private wsReaperStop;
|
|
30
30
|
/** Heartbeat timer that refreshes port file to prove this server is active */
|
|
31
31
|
private wsHeartbeatTimer;
|
|
32
|
+
/** Optional Streamable HTTP endpoint (FIGMA_MCP_HTTP_PORT), shared by local clients. */
|
|
33
|
+
private httpEndpoint;
|
|
34
|
+
private httpStartupError;
|
|
32
35
|
private config;
|
|
33
36
|
private variablesCache;
|
|
34
37
|
private auditDataCache;
|
|
@@ -40,6 +43,11 @@ declare class LocalFigmaConsoleMCP {
|
|
|
40
43
|
*/
|
|
41
44
|
private invalidateVariablesCache;
|
|
42
45
|
constructor();
|
|
46
|
+
/**
|
|
47
|
+
* A configured McpServer with no tools yet. stdio uses one for the whole
|
|
48
|
+
* run; the optional HTTP endpoint builds one per request (see http-transport.ts).
|
|
49
|
+
*/
|
|
50
|
+
private createMcpServer;
|
|
43
51
|
/**
|
|
44
52
|
* Get or create Figma API client
|
|
45
53
|
*/
|
|
@@ -87,6 +95,11 @@ declare class LocalFigmaConsoleMCP {
|
|
|
87
95
|
* Start the MCP server
|
|
88
96
|
*/
|
|
89
97
|
start(): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Serve MCP over Streamable HTTP as well, when FIGMA_MCP_HTTP_PORT is set.
|
|
100
|
+
* A failure here (bad value, port taken) is reported but never stops stdio.
|
|
101
|
+
*/
|
|
102
|
+
private startHttpEndpoint;
|
|
90
103
|
/**
|
|
91
104
|
* Cleanup and shutdown
|
|
92
105
|
*/
|
package/dist/local.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../src/local.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../src/local.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG;AAsGH;;;GAGG;AACH,cAAM,oBAAoB;IACzB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,cAAc,CAA+C;IACrE,uGAAuG;IACvG,OAAO,CAAC,YAAY,CAAuB;IAC3C,6DAA6D;IAC7D,OAAO,CAAC,eAAe,CAA2B;IAClD,2EAA2E;IAC3E,OAAO,CAAC,YAAY,CAA6B;IACjD,8EAA8E;IAC9E,OAAO,CAAC,gBAAgB,CAA+C;IACvE,wFAAwF;IACxF,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,MAAM,CAAe;IAI7B,OAAO,CAAC,cAAc,CAMR;IAOd,OAAO,CAAC,cAAc,CAMR;IACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAiB;IAE3D;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;;IAWhC;;;OAGG;IACH,OAAO,CAAC,eAAe;IAgFvB;;OAEG;YACW,WAAW;IAoBzB;;;OAGG;YACW,mBAAmB;IAyBjC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAmBvB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAkD5B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;YACW,iBAAiB;IAiB/B,wDAAwD;IACxD,OAAO,CAAC,gBAAgB,CAAuB;IAE/C;;;OAGG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACH,OAAO,CAAC,aAAa;IAozGrB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsP5B;;;OAGG;YACW,iBAAiB;IAgC/B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAmC/B;AAyGD,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
package/dist/local.js
CHANGED
|
@@ -40,7 +40,8 @@ import { wrapServerForIdentity } from "./core/identity.js";
|
|
|
40
40
|
import { PACKAGE_ROOT } from "./core/resolve-package-root.js";
|
|
41
41
|
import { FigmaWebSocketServer, getBundledPluginVersion, getServerVersion } from "./core/websocket-server.js";
|
|
42
42
|
import { WebSocketConnector } from "./core/websocket-connector.js";
|
|
43
|
-
import { DEFAULT_WS_PORT, getPortRange, advertisePort, unadvertisePort, registerPortCleanup, startPeriodicReaper, discoverActiveInstances, cleanupStalePortFiles, cleanupOrphanedProcesses, evictOldestInstance, refreshPortAdvertisement, noteToolCall, HEARTBEAT_INTERVAL_MS, } from "./core/port-discovery.js";
|
|
43
|
+
import { DEFAULT_WS_PORT, getPortRange, advertisePort, unadvertisePort, registerPortCleanup, startPeriodicReaper, discoverActiveInstances, cleanupStalePortFiles, cleanupOrphanedProcesses, evictOldestInstance, refreshPortAdvertisement, noteToolCall, setAdvertisedMcpHttpUrl, HEARTBEAT_INTERVAL_MS, } from "./core/port-discovery.js";
|
|
44
|
+
import { parseHttpPort, startLocalHttpTransport } from "./core/http-transport.js";
|
|
44
45
|
import { onStdinClosed } from "./core/stdin-lifecycle.js";
|
|
45
46
|
import { registerTokenBrowserApp } from "./apps/token-browser/server.js";
|
|
46
47
|
import { registerDesignSystemAuditTool, registerDesignSystemDashboardApp, } from "./apps/design-system-dashboard/server.js";
|
|
@@ -111,6 +112,9 @@ class LocalFigmaConsoleMCP {
|
|
|
111
112
|
this.wsReaperStop = null;
|
|
112
113
|
/** Heartbeat timer that refreshes port file to prove this server is active */
|
|
113
114
|
this.wsHeartbeatTimer = null;
|
|
115
|
+
/** Optional Streamable HTTP endpoint (FIGMA_MCP_HTTP_PORT), shared by local clients. */
|
|
116
|
+
this.httpEndpoint = null;
|
|
117
|
+
this.httpStartupError = null;
|
|
114
118
|
this.config = getConfig();
|
|
115
119
|
// In-memory cache for variables data to avoid MCP token limits
|
|
116
120
|
// Maps fileKey -> {data, timestamp}
|
|
@@ -123,7 +127,14 @@ class LocalFigmaConsoleMCP {
|
|
|
123
127
|
this.auditDataCache = new Map();
|
|
124
128
|
/** Stable plugin directory path (set during startup) */
|
|
125
129
|
this.stablePluginPath = null;
|
|
126
|
-
this.server =
|
|
130
|
+
this.server = this.createMcpServer();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A configured McpServer with no tools yet. stdio uses one for the whole
|
|
134
|
+
* run; the optional HTTP endpoint builds one per request (see http-transport.ts).
|
|
135
|
+
*/
|
|
136
|
+
createMcpServer() {
|
|
137
|
+
const server = new McpServer({
|
|
127
138
|
name: "Figma Console MCP (Local)",
|
|
128
139
|
// Was hard-coded "0.1.0" — every client saw that regardless of the release
|
|
129
140
|
version: getServerVersion(),
|
|
@@ -194,7 +205,8 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
194
205
|
// Stamp every tool response (and every thrown error) with our MCP identity
|
|
195
206
|
// so LLMs can attribute output unambiguously when multiple Figma-related
|
|
196
207
|
// MCPs are connected. Idempotent for already-tagged responses.
|
|
197
|
-
wrapServerForIdentity(
|
|
208
|
+
wrapServerForIdentity(server, { onToolCall: noteToolCall });
|
|
209
|
+
return server;
|
|
198
210
|
}
|
|
199
211
|
/**
|
|
200
212
|
* Get or create Figma API client
|
|
@@ -366,9 +378,9 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
366
378
|
/**
|
|
367
379
|
* Register all MCP tools
|
|
368
380
|
*/
|
|
369
|
-
registerTools() {
|
|
381
|
+
registerTools(server) {
|
|
370
382
|
// Tool 1: Get Console Logs
|
|
371
|
-
|
|
383
|
+
server.tool("figma_get_console_logs", "Retrieve console logs captured by the Desktop Bridge plugin: console output from code run through it (figma_execute and the other write tools) and from the bridge itself. It does NOT capture another plugin's console — Figma runs one plugin at a time in a file, each in its own sandbox, so the bridge cannot see a plugin you are developing. To debug your own plugin's logic, run that code through figma_execute and read the logs here, or use Figma's own developer console for the plugin.", {
|
|
372
384
|
count: z
|
|
373
385
|
.number()
|
|
374
386
|
.optional()
|
|
@@ -461,7 +473,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
461
473
|
// Bridge-first: the plugin's exportAsync works on any Figma plan with no REST
|
|
462
474
|
// token, and reflects the current runtime state (no cloud-sync lag).
|
|
463
475
|
// Note: For screenshots of specific components, use figma_get_component_image instead
|
|
464
|
-
|
|
476
|
+
server.tool("figma_take_screenshot", `Export an image of the current Figma page or specific node. Uses the Desktop Bridge plugin (exportAsync) when connected — works on any plan, no REST token needed, reflects current runtime state. Falls back to the Figma REST API when the bridge is unavailable or for PDF format. Use for visual validation after design changes — check alignment, spacing, proportions. Pass nodeId to target specific elements. For components, prefer figma_get_component_image.`, {
|
|
465
477
|
nodeId: z
|
|
466
478
|
.string()
|
|
467
479
|
.optional()
|
|
@@ -646,7 +658,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
646
658
|
}
|
|
647
659
|
});
|
|
648
660
|
// Tool 3: Watch Console (Real-time streaming)
|
|
649
|
-
|
|
661
|
+
server.tool("figma_watch_console", "Stream console logs captured by the Desktop Bridge plugin in real-time for a specified duration (max 5 minutes). Covers code run through the bridge (figma_execute and the write tools) — not other plugins, which run in their own sandboxes. Returns all logs captured during the watch period with summary statistics. NOT for retrieving past logs (use figma_get_console_logs). Best for: watching long-running figma_execute scripts, debugging race conditions, monitoring async operations.", {
|
|
650
662
|
duration: z
|
|
651
663
|
.number()
|
|
652
664
|
.optional()
|
|
@@ -696,7 +708,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
696
708
|
};
|
|
697
709
|
});
|
|
698
710
|
// Tool 4: Reload Plugin
|
|
699
|
-
|
|
711
|
+
server.tool("figma_reload_plugin", "Reload the current Figma page/plugin to test code changes. Optionally clears console logs before reload. Use when user says: 'reload plugin', 'refresh page', 'restart plugin', 'test my changes'. Returns reload confirmation and current URL. Best for rapid iteration during plugin development.", {
|
|
700
712
|
clearConsole: z
|
|
701
713
|
.boolean()
|
|
702
714
|
.optional()
|
|
@@ -764,7 +776,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
764
776
|
}
|
|
765
777
|
});
|
|
766
778
|
// Tool 5: Clear Console
|
|
767
|
-
|
|
779
|
+
server.tool("figma_clear_console", "Clear the console log buffer. Safely clears the buffer without disrupting the connection. Returns number of logs cleared.", {}, async () => {
|
|
768
780
|
try {
|
|
769
781
|
let clearedCount = 0;
|
|
770
782
|
let transport = "websocket";
|
|
@@ -816,7 +828,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
816
828
|
}
|
|
817
829
|
});
|
|
818
830
|
// Tool 6: Navigate / switch active file
|
|
819
|
-
|
|
831
|
+
server.tool("figma_navigate", "Switch the active Figma file target among files that already have the Desktop Bridge plugin running. Local mode is WebSocket-only — this tool does NOT launch a browser or open files. If the requested URL is already the active file, it confirms the connection. If another connected plugin matches the URL, it switches the active target so subsequent tool calls hit that file. If no connected plugin matches, returns guidance for the user to open the Desktop Bridge plugin in the target file. Use figma_list_open_files to see all connected files.\n\nPass lock: true to PIN this file as the target — new connections, reconnects, and the user's own selection/page changes in other files will no longer move the target. Use this for parallel work (agent edits one file while the user works in another) so commands can't silently route to the wrong file. Switching to another file (or lock: false) releases the pin; it also releases automatically if the pinned file disconnects.", {
|
|
820
832
|
url: z
|
|
821
833
|
.string()
|
|
822
834
|
.url()
|
|
@@ -953,7 +965,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
953
965
|
}
|
|
954
966
|
});
|
|
955
967
|
// Tool 7: Get Status (with setup validation)
|
|
956
|
-
|
|
968
|
+
server.tool("figma_get_status", "Check connection status to Figma Desktop. Reports transport status and connection health via the Desktop Bridge plugin (WebSocket transport). Use probe:true for an active roundtrip verification that the plugin is actually responding.", {
|
|
957
969
|
probe: z.boolean().optional().describe("When true, sends a live roundtrip command to the plugin to verify the connection is actually responsive (not just TCP-open). Returns probeResult with success/latency. Recommended for health checks."),
|
|
958
970
|
}, async ({ probe }) => {
|
|
959
971
|
try {
|
|
@@ -1049,6 +1061,11 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
1049
1061
|
monitorWorkerCount: 0,
|
|
1050
1062
|
transport: {
|
|
1051
1063
|
active: activeTransport,
|
|
1064
|
+
mcpHttp: this.httpEndpoint
|
|
1065
|
+
? { url: this.httpEndpoint.url, sessions: this.httpEndpoint.sessionCount(), auth: process.env.FIGMA_MCP_HTTP_TOKEN ? "bearer" : "none" }
|
|
1066
|
+
: this.httpStartupError
|
|
1067
|
+
? { error: this.httpStartupError }
|
|
1068
|
+
: undefined,
|
|
1052
1069
|
websocket: {
|
|
1053
1070
|
available: wsConnected,
|
|
1054
1071
|
serverRunning: this.wsServer?.isStarted() ?? false,
|
|
@@ -1075,6 +1092,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
1075
1092
|
port: i.port,
|
|
1076
1093
|
pid: i.pid,
|
|
1077
1094
|
startedAt: i.startedAt,
|
|
1095
|
+
mcpHttpUrl: i.mcpHttpUrl,
|
|
1078
1096
|
}));
|
|
1079
1097
|
}
|
|
1080
1098
|
catch {
|
|
@@ -1180,7 +1198,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
1180
1198
|
// CONNECTION MANAGEMENT TOOLS
|
|
1181
1199
|
// ============================================================================
|
|
1182
1200
|
// Tool: Force reconnect to Figma Desktop
|
|
1183
|
-
|
|
1201
|
+
server.tool("figma_reconnect", "Force a complete reconnection to Figma Desktop. Use when connection seems stale or after switching files.", {}, async () => {
|
|
1184
1202
|
try {
|
|
1185
1203
|
// figma_reconnect is informational in WebSocket-only mode — the
|
|
1186
1204
|
// plugin handles its own reconnect logic. A TCP-open socket is not
|
|
@@ -1263,7 +1281,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
1263
1281
|
// REAL-TIME AWARENESS TOOLS (WebSocket-only)
|
|
1264
1282
|
// ============================================================================
|
|
1265
1283
|
// Tool: Get current user selection in Figma
|
|
1266
|
-
|
|
1284
|
+
server.tool("figma_get_selection", "Get the currently selected nodes in Figma. Returns node IDs, names, types, and dimensions. WebSocket-only — requires Desktop Bridge plugin. Use this to understand what the user is pointing at instead of asking them to describe it.", {
|
|
1267
1285
|
verbose: z
|
|
1268
1286
|
.boolean()
|
|
1269
1287
|
.optional()
|
|
@@ -1356,7 +1374,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
1356
1374
|
}
|
|
1357
1375
|
});
|
|
1358
1376
|
// Tool: Get recent design changes
|
|
1359
|
-
|
|
1377
|
+
server.tool("figma_get_design_changes", "Get recent document changes detected in Figma. Returns buffered change events including which nodes changed, whether styles were modified, and change counts. WebSocket-only — events are captured via Desktop Bridge plugin. Use this to understand what changed since you last checked.", {
|
|
1360
1378
|
since: z
|
|
1361
1379
|
.number()
|
|
1362
1380
|
.optional()
|
|
@@ -1433,7 +1451,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
1433
1451
|
}
|
|
1434
1452
|
});
|
|
1435
1453
|
// Tool: List all open files connected via WebSocket
|
|
1436
|
-
|
|
1454
|
+
server.tool("figma_list_open_files", "List all Figma files currently connected via the Desktop Bridge plugin. Shows which files have the plugin open and which one is the active target for tool calls. Use figma_navigate to switch between files. WebSocket multi-client mode — each file with the Desktop Bridge plugin maintains its own connection.", {}, async () => {
|
|
1437
1455
|
try {
|
|
1438
1456
|
if (!this.wsServer?.isClientConnected()) {
|
|
1439
1457
|
return {
|
|
@@ -1651,7 +1669,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
1651
1669
|
// (Previously interleaved with write tools in local.ts; restored after the
|
|
1652
1670
|
// Phase-2 write-tools dedupe excised them along with the surrounding writes.)
|
|
1653
1671
|
// ============================================================================
|
|
1654
|
-
|
|
1672
|
+
server.tool("figma_get_design_system_summary", "Get a compact overview of the design system. Returns categories, component counts, and token collection names WITHOUT full details. Use this first to understand what's available, then use figma_search_components to find specific components. This tool is optimized for minimal token usage.", {
|
|
1655
1673
|
forceRefresh: z
|
|
1656
1674
|
.boolean()
|
|
1657
1675
|
.optional()
|
|
@@ -1855,7 +1873,7 @@ If Design Systems Assistant MCP is not available, install it from: https://githu
|
|
|
1855
1873
|
}
|
|
1856
1874
|
});
|
|
1857
1875
|
// Tool 2: Search Components (~3000 tokens response max, paginated)
|
|
1858
|
-
|
|
1876
|
+
server.tool("figma_search_components", `Search for components by name, category, or description. Returns paginated results with component keys for instantiation. Automatically loads the design system cache if needed.
|
|
1859
1877
|
|
|
1860
1878
|
**NEW: Cross-file library search!** Pass a libraryFileKey or libraryFileUrl to search for components in a published shared library (different file). This uses the REST API and requires FIGMA_ACCESS_TOKEN.
|
|
1861
1879
|
|
|
@@ -2096,7 +2114,7 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
2096
2114
|
}
|
|
2097
2115
|
});
|
|
2098
2116
|
// Tool 3: Get Component Details (~500 tokens per component)
|
|
2099
|
-
|
|
2117
|
+
server.tool("figma_get_component_details", "Get full details for a specific component including all variants, properties, and keys needed for instantiation. Use the component key or name from figma_search_components.", {
|
|
2100
2118
|
componentKey: z
|
|
2101
2119
|
.string()
|
|
2102
2120
|
.optional()
|
|
@@ -2206,7 +2224,7 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
2206
2224
|
}
|
|
2207
2225
|
});
|
|
2208
2226
|
// Tool 4: Get Token Values (~2000 tokens response max)
|
|
2209
|
-
|
|
2227
|
+
server.tool("figma_get_token_values", "Get actual values for design tokens (colors, spacing, etc). Use after figma_get_design_system_summary to get specific token values for implementation.", {
|
|
2210
2228
|
type: z
|
|
2211
2229
|
.enum(["colors", "spacing", "all"])
|
|
2212
2230
|
.optional()
|
|
@@ -2299,7 +2317,7 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
2299
2317
|
}
|
|
2300
2318
|
});
|
|
2301
2319
|
// Tool 5: Instantiate Component
|
|
2302
|
-
|
|
2320
|
+
server.tool("figma_get_library_components", `Discover published components from a shared/team library file.
|
|
2303
2321
|
|
|
2304
2322
|
**USE THIS when you need to use components from a published design system library** (a different file than the one currently open). This bridges the gap between library discovery and instantiation.
|
|
2305
2323
|
|
|
@@ -2569,18 +2587,18 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
2569
2587
|
// Register all write/manipulation tools (figma_execute, variable CRUD, node mutations,
|
|
2570
2588
|
// design-token setup, accessibility audits, etc.). Sourced from src/core/write-tools.ts
|
|
2571
2589
|
// so local mode and cloud mode share the same 30 implementations — no risk of drift.
|
|
2572
|
-
registerWriteTools(
|
|
2590
|
+
registerWriteTools(server, () => this.getDesktopConnector());
|
|
2573
2591
|
// Register cross-file tools (figma_execute_across_files) — run the same
|
|
2574
2592
|
// code against every (or a chosen subset of) currently connected files
|
|
2575
2593
|
// concurrently. Local mode only, needs direct wsServer access for
|
|
2576
2594
|
// getConnectedFiles(), so it isn't part of the connector abstraction.
|
|
2577
|
-
registerMultiFileTools(
|
|
2595
|
+
registerMultiFileTools(server, () => this.wsServer, () => this.getDesktopConnector());
|
|
2578
2596
|
// Register token sync tools — figma_export_tokens and figma_import_tokens.
|
|
2579
2597
|
// Replace Style Dictionary and Tokens Studio's export pipeline for the
|
|
2580
2598
|
// popular styling methods (DTCG canonical — legacy + 2025.10 dialects —
|
|
2581
2599
|
// plus CSS/Tailwind/SCSS/TS/JSON/Style Dictionary/Tokens Studio, all
|
|
2582
2600
|
// derived from a single internal token model).
|
|
2583
|
-
registerTokensTools(
|
|
2601
|
+
registerTokensTools(server, () => this.getDesktopConnector(), {
|
|
2584
2602
|
// Lets figma_export_tokens report WHICH connected file it read from —
|
|
2585
2603
|
// the bridge reads the active file, which may not be the intended one.
|
|
2586
2604
|
resolveFileName: (fileKey) => this.wsServer
|
|
@@ -2593,18 +2611,18 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
2593
2611
|
// these read/write the local filesystem, which Cloudflare Workers
|
|
2594
2612
|
// cannot (registerMultiFileTools precedent — never registered in
|
|
2595
2613
|
// src/index.ts, so no Cloud Mode silent no-op is possible).
|
|
2596
|
-
registerDesignSystemExtractionTools(
|
|
2614
|
+
registerDesignSystemExtractionTools(server);
|
|
2597
2615
|
// Register Figma API tools (Tools 8-11)
|
|
2598
|
-
registerFigmaAPITools(
|
|
2616
|
+
registerFigmaAPITools(server, () => this.getFigmaAPI(), () => this.getCurrentFileUrl(), this.variablesCache, // Pass cache for efficient variable queries
|
|
2599
2617
|
undefined, // options (use default)
|
|
2600
2618
|
() => this.getDesktopConnector());
|
|
2601
2619
|
// Register Design-Code Parity & Documentation tools
|
|
2602
|
-
registerDesignCodeTools(
|
|
2620
|
+
registerDesignCodeTools(server, () => this.getFigmaAPI(), () => this.getCurrentFileUrl(), this.variablesCache, undefined, // options
|
|
2603
2621
|
() => this.getDesktopConnector());
|
|
2604
2622
|
// Register Comment tools
|
|
2605
|
-
registerCommentTools(
|
|
2623
|
+
registerCommentTools(server, () => this.getFigmaAPI(), () => this.getCurrentFileUrl());
|
|
2606
2624
|
// Register Version History tools
|
|
2607
|
-
registerVersionTools(
|
|
2625
|
+
registerVersionTools(server, () => this.getFigmaAPI(), () => this.getCurrentFileUrl(), undefined, // options
|
|
2608
2626
|
() => {
|
|
2609
2627
|
// Selection fallback for blame/diff/changelog tools
|
|
2610
2628
|
const sel = this.wsServer?.getCurrentSelection();
|
|
@@ -2619,20 +2637,20 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
2619
2637
|
return this.wsServer.getMetadataChanges(opts);
|
|
2620
2638
|
});
|
|
2621
2639
|
// Register Design System Kit tool
|
|
2622
|
-
registerDesignSystemTools(
|
|
2640
|
+
registerDesignSystemTools(server, () => this.getFigmaAPI(), () => this.getCurrentFileUrl(), this.variablesCache, undefined, // options (use default)
|
|
2623
2641
|
() => this.getDesktopConnector());
|
|
2624
2642
|
// Register Library Tools (key-based component inspection across shared libraries)
|
|
2625
|
-
registerLibraryTools(
|
|
2643
|
+
registerLibraryTools(server, () => this.getFigmaAPI());
|
|
2626
2644
|
// Register Library Variable Tools (Plugin-API based — list + import variables
|
|
2627
2645
|
// from subscribed team libraries; works on every Figma plan, no Enterprise needed)
|
|
2628
|
-
registerLibraryVariableTools(
|
|
2646
|
+
registerLibraryVariableTools(server, () => this.getDesktopConnector());
|
|
2629
2647
|
// Register code-side accessibility scanning (axe-core + JSDOM)
|
|
2630
|
-
registerAccessibilityTools(
|
|
2648
|
+
registerAccessibilityTools(server);
|
|
2631
2649
|
// Register figma_diagnose — designer-readable health check + cross-MCP disambiguator.
|
|
2632
2650
|
// This is the first tool to point a confused user at: it self-identifies the server,
|
|
2633
2651
|
// reports plugin/token state in plain language, and explicitly disclaims any
|
|
2634
2652
|
// token/OAuth error that may have been emitted by a different Figma-related MCP.
|
|
2635
|
-
registerDiagnoseTool(
|
|
2653
|
+
registerDiagnoseTool(server, {
|
|
2636
2654
|
mode: "local",
|
|
2637
2655
|
getServerVersion: () => {
|
|
2638
2656
|
try {
|
|
@@ -2666,17 +2684,17 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
2666
2684
|
},
|
|
2667
2685
|
});
|
|
2668
2686
|
// Register Annotation tools (read/write design annotations via Desktop Bridge)
|
|
2669
|
-
registerAnnotationTools(
|
|
2687
|
+
registerAnnotationTools(server, () => this.getDesktopConnector());
|
|
2670
2688
|
// Register Deep Component tools (full Plugin API tree extraction for code generation)
|
|
2671
|
-
registerDeepComponentTools(
|
|
2689
|
+
registerDeepComponentTools(server, () => this.getDesktopConnector());
|
|
2672
2690
|
// Register FigJam-specific tools (sticky notes, connectors, tables, etc.)
|
|
2673
|
-
registerFigJamTools(
|
|
2691
|
+
registerFigJamTools(server, () => this.getDesktopConnector());
|
|
2674
2692
|
// Register Figma Slides tools (slide management, transitions, content)
|
|
2675
|
-
registerSlidesTools(
|
|
2676
|
-
registerSlotTools(
|
|
2693
|
+
registerSlidesTools(server, () => this.getDesktopConnector());
|
|
2694
|
+
registerSlotTools(server, () => this.getDesktopConnector());
|
|
2677
2695
|
// MCP Apps - gated behind ENABLE_MCP_APPS env var
|
|
2678
2696
|
if (process.env.ENABLE_MCP_APPS === "true") {
|
|
2679
|
-
registerTokenBrowserApp(
|
|
2697
|
+
registerTokenBrowserApp(server, async (fileUrl) => {
|
|
2680
2698
|
const url = fileUrl || this.getCurrentFileUrl();
|
|
2681
2699
|
if (!url) {
|
|
2682
2700
|
throw new Error("No Figma file URL available. Either pass a fileUrl, call figma_navigate, or ensure the Desktop Bridge plugin is connected.");
|
|
@@ -3135,10 +3153,10 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
3135
3153
|
};
|
|
3136
3154
|
// Always available — any MCP client can run the audit, no MCP Apps
|
|
3137
3155
|
// support required.
|
|
3138
|
-
registerDesignSystemAuditTool(
|
|
3156
|
+
registerDesignSystemAuditTool(server, fetchDesignSystemAuditData, () => this.getCurrentFileUrl());
|
|
3139
3157
|
// Visual dashboard app (MCP-Apps-capable hosts only).
|
|
3140
3158
|
if (process.env.ENABLE_MCP_APPS === "true") {
|
|
3141
|
-
registerDesignSystemDashboardApp(
|
|
3159
|
+
registerDesignSystemDashboardApp(server, fetchDesignSystemAuditData,
|
|
3142
3160
|
// Pass getCurrentUrl so dashboard can track which file was audited
|
|
3143
3161
|
() => this.getCurrentFileUrl());
|
|
3144
3162
|
}
|
|
@@ -3322,12 +3340,13 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
3322
3340
|
logger.info("Checking Figma Desktop accessibility...");
|
|
3323
3341
|
await this.checkFigmaDesktop();
|
|
3324
3342
|
// Register all tools
|
|
3325
|
-
this.registerTools();
|
|
3343
|
+
this.registerTools(this.server);
|
|
3326
3344
|
// Create stdio transport
|
|
3327
3345
|
const transport = new StdioServerTransport();
|
|
3328
3346
|
// Connect server to transport
|
|
3329
3347
|
await this.server.connect(transport);
|
|
3330
3348
|
logger.info("MCP server started successfully on stdio transport");
|
|
3349
|
+
await this.startHttpEndpoint();
|
|
3331
3350
|
// 🆕 AUTO-CONNECT: Start monitoring immediately if Figma Desktop is available
|
|
3332
3351
|
// This enables "get latest logs" workflow without requiring manual setup
|
|
3333
3352
|
// In WS-only mode, no auto-connect is needed — the Desktop Bridge plugin
|
|
@@ -3342,12 +3361,53 @@ Without libraryFileKey/libraryFileUrl, searches the currently open file (local c
|
|
|
3342
3361
|
process.exit(1);
|
|
3343
3362
|
}
|
|
3344
3363
|
}
|
|
3364
|
+
/**
|
|
3365
|
+
* Serve MCP over Streamable HTTP as well, when FIGMA_MCP_HTTP_PORT is set.
|
|
3366
|
+
* A failure here (bad value, port taken) is reported but never stops stdio.
|
|
3367
|
+
*/
|
|
3368
|
+
async startHttpEndpoint() {
|
|
3369
|
+
let port;
|
|
3370
|
+
try {
|
|
3371
|
+
port = parseHttpPort(process.env.FIGMA_MCP_HTTP_PORT);
|
|
3372
|
+
}
|
|
3373
|
+
catch (error) {
|
|
3374
|
+
this.httpStartupError = error instanceof Error ? error.message : String(error);
|
|
3375
|
+
logger.warn(this.httpStartupError);
|
|
3376
|
+
return;
|
|
3377
|
+
}
|
|
3378
|
+
if (port === null)
|
|
3379
|
+
return;
|
|
3380
|
+
try {
|
|
3381
|
+
this.httpEndpoint = await startLocalHttpTransport({
|
|
3382
|
+
port,
|
|
3383
|
+
token: process.env.FIGMA_MCP_HTTP_TOKEN || undefined,
|
|
3384
|
+
createServer: () => {
|
|
3385
|
+
const server = this.createMcpServer();
|
|
3386
|
+
this.registerTools(server);
|
|
3387
|
+
return server;
|
|
3388
|
+
},
|
|
3389
|
+
});
|
|
3390
|
+
setAdvertisedMcpHttpUrl(this.httpEndpoint.url);
|
|
3391
|
+
console.error(`[figma-console-mcp] MCP over HTTP: ${this.httpEndpoint.url}`);
|
|
3392
|
+
}
|
|
3393
|
+
catch (error) {
|
|
3394
|
+
const code = error?.code;
|
|
3395
|
+
this.httpStartupError = code === "EADDRINUSE"
|
|
3396
|
+
? `Port ${port} is already in use, so the HTTP endpoint is off (stdio still works). Another figma-console-mcp may already serve it: check its port file, or pick another FIGMA_MCP_HTTP_PORT.`
|
|
3397
|
+
: `HTTP endpoint failed to start on port ${port}: ${error instanceof Error ? error.message : String(error)}`;
|
|
3398
|
+
logger.warn({ port, code }, this.httpStartupError);
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3345
3401
|
/**
|
|
3346
3402
|
* Cleanup and shutdown
|
|
3347
3403
|
*/
|
|
3348
3404
|
async shutdown() {
|
|
3349
3405
|
logger.info("Shutting down MCP server...");
|
|
3350
3406
|
try {
|
|
3407
|
+
if (this.httpEndpoint) {
|
|
3408
|
+
await this.httpEndpoint.close();
|
|
3409
|
+
this.httpEndpoint = null;
|
|
3410
|
+
}
|
|
3351
3411
|
// Stop heartbeat timer
|
|
3352
3412
|
if (this.wsHeartbeatTimer) {
|
|
3353
3413
|
clearInterval(this.wsHeartbeatTimer);
|