figma-console-mcp 1.13.1 β 1.15.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 +37 -28
- package/dist/cloudflare/core/cloud-websocket-connector.js +21 -0
- package/dist/cloudflare/core/figma-desktop-connector.js +32 -0
- package/dist/cloudflare/core/websocket-connector.js +21 -0
- package/dist/cloudflare/core/write-tools.js +85 -0
- package/dist/cloudflare/index.js +58 -25
- package/dist/core/cdp-pipe-manager.d.ts +96 -0
- package/dist/core/cdp-pipe-manager.d.ts.map +1 -0
- package/dist/core/cdp-pipe-manager.js +429 -0
- package/dist/core/cdp-pipe-manager.js.map +1 -0
- package/dist/core/port-discovery.d.ts +11 -0
- package/dist/core/port-discovery.d.ts.map +1 -1
- package/dist/core/port-discovery.js +71 -0
- package/dist/core/port-discovery.js.map +1 -1
- package/dist/core/websocket-server.d.ts +10 -1
- package/dist/core/websocket-server.d.ts.map +1 -1
- package/dist/core/websocket-server.js +150 -20
- package/dist/core/websocket-server.js.map +1 -1
- package/dist/core/write-tools.d.ts.map +1 -1
- package/dist/core/write-tools.js +10 -2
- package/dist/core/write-tools.js.map +1 -1
- package/dist/local.d.ts +3 -1
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +511 -26
- package/dist/local.js.map +1 -1
- package/figma-desktop-bridge/code.js +60 -2
- package/figma-desktop-bridge/ui-full.html +1236 -0
- package/figma-desktop-bridge/ui.html +173 -1156
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
> **Your design system as an API.** Model Context Protocol server that bridges design and developmentβgiving AI assistants complete access to Figma for **extraction**, **creation**, and **debugging**.
|
|
10
10
|
|
|
11
|
-
> **π
|
|
11
|
+
> **π Import Once, Update Never β Plugin Bootloader Architecture:** The Desktop Bridge plugin now dynamically loads its UI from the MCP server on every launch. Import the manifest once from `~/.figma-console-mcp/plugin/manifest.json` and you're done forever β server updates, new tools, and bug fixes are delivered automatically. Plus: orphaned process cleanup, cross-file library components, and built-in housekeeping. [See changelog β](CHANGELOG.md)
|
|
12
12
|
|
|
13
13
|
## What is this?
|
|
14
14
|
|
|
@@ -49,9 +49,9 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
|
|
|
49
49
|
| Real-time monitoring (console, selection) | β
| β | β |
|
|
50
50
|
| Desktop Bridge plugin | β
| β
| β |
|
|
51
51
|
| Requires Node.js | Yes | **No** | No |
|
|
52
|
-
| **Total tools available** | **
|
|
52
|
+
| **Total tools available** | **63+** | **43** | **22** |
|
|
53
53
|
|
|
54
|
-
> **Bottom line:** Remote SSE is **read-only** with ~38% of the tools. **Cloud Mode** unlocks write access from web AI clients without Node.js. NPX/Local Git gives the full
|
|
54
|
+
> **Bottom line:** Remote SSE is **read-only** with ~38% of the tools. **Cloud Mode** unlocks write access from web AI clients without Node.js. NPX/Local Git gives the full 63+ tools with real-time monitoring.
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
@@ -59,7 +59,7 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
|
|
|
59
59
|
|
|
60
60
|
**Best for:** Designers who want full AI-assisted design capabilities.
|
|
61
61
|
|
|
62
|
-
**What you get:** All
|
|
62
|
+
**What you get:** All 63+ tools including design creation, variable management, and component instantiation.
|
|
63
63
|
|
|
64
64
|
#### Prerequisites
|
|
65
65
|
|
|
@@ -122,11 +122,10 @@ If you're not sure where to put the JSON configuration above, here's where each
|
|
|
122
122
|
**Desktop Bridge Plugin:**
|
|
123
123
|
1. Open Figma Desktop normally (no special flags needed)
|
|
124
124
|
2. Go to **Plugins β Development β Import plugin from manifest...**
|
|
125
|
-
3. Select
|
|
126
|
-
|
|
127
|
-
4. Run the plugin in your Figma file β it auto-connects via WebSocket (scans ports 9223β9232)
|
|
125
|
+
3. Select `~/.figma-console-mcp/plugin/manifest.json` (stable path, auto-created by the MCP server)
|
|
126
|
+
4. Run the plugin in your Figma file β the bootloader finds the MCP server and loads the latest UI automatically
|
|
128
127
|
|
|
129
|
-
> One-time setup. The plugin
|
|
128
|
+
> One-time setup. The plugin uses a bootloader that dynamically loads fresh code from the MCP server β no need to re-import when the server updates.
|
|
130
129
|
|
|
131
130
|
#### Step 4: Restart Your MCP Client
|
|
132
131
|
|
|
@@ -152,7 +151,7 @@ Create a simple frame with a blue background
|
|
|
152
151
|
|
|
153
152
|
**Best for:** Developers who want to modify source code or contribute to the project.
|
|
154
153
|
|
|
155
|
-
**What you get:** Same
|
|
154
|
+
**What you get:** Same 63+ tools as NPX, plus full source code access.
|
|
156
155
|
|
|
157
156
|
#### Quick Setup
|
|
158
157
|
|
|
@@ -193,7 +192,7 @@ Then follow [NPX Steps 3-5](#step-3-connect-to-figma-desktop) above.
|
|
|
193
192
|
|
|
194
193
|
**Best for:** Quickly evaluating the tool or read-only design data extraction.
|
|
195
194
|
|
|
196
|
-
**What you get:**
|
|
195
|
+
**What you get:** 9 read-only tools β view data, take screenshots, read logs, design-code parity. **Cannot create or modify designs.**
|
|
197
196
|
|
|
198
197
|
#### Claude Desktop (UI Method)
|
|
199
198
|
|
|
@@ -241,24 +240,34 @@ Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide
|
|
|
241
240
|
|
|
242
241
|
**Best for:** Using Claude.ai, v0, Replit, or Lovable to create and modify Figma designs β no Node.js required.
|
|
243
242
|
|
|
244
|
-
**What you get:**
|
|
243
|
+
**What you get:** 52 tools including full write access β design creation, variable management, component instantiation, and all REST API tools. Only real-time monitoring (console logs, selection tracking, document changes) requires Local Mode.
|
|
245
244
|
|
|
246
245
|
#### Prerequisites
|
|
247
246
|
|
|
247
|
+
- [ ] **Figma Personal Access Token** β [Create one here](https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens) (starts with `figd_`)
|
|
248
248
|
- [ ] **Figma Desktop** with the Desktop Bridge plugin installed (see [Desktop Bridge setup](#step-3-connect-to-figma-desktop))
|
|
249
|
-
- [ ] **A web AI client**
|
|
249
|
+
- [ ] **A web AI client** that supports MCP (Claude.ai, Lovable, v0, Replit, etc.)
|
|
250
|
+
|
|
251
|
+
#### Step 1: Add the MCP Connector
|
|
252
|
+
|
|
253
|
+
Add this endpoint to your AI platform's MCP settings:
|
|
250
254
|
|
|
251
|
-
|
|
255
|
+
**URL:** `https://figma-console-mcp.southleft.com/mcp`
|
|
256
|
+
**Auth:** Your Figma PAT as Bearer token
|
|
252
257
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
258
|
+
In **Claude.ai**: Settings β Connectors β Add Custom Connector β paste the URL above.
|
|
259
|
+
In **Lovable/v0/Replit**: Look for "Add MCP Server" or "Integrations" in settings β paste the URL and add your token.
|
|
260
|
+
|
|
261
|
+
#### Step 2: Pair the Plugin
|
|
262
|
+
|
|
263
|
+
1. **Open the Desktop Bridge plugin** in Figma Desktop (Plugins β Development β Figma Desktop Bridge)
|
|
264
|
+
2. **Tell your AI assistant:**
|
|
256
265
|
```
|
|
257
266
|
Connect to my Figma plugin
|
|
258
267
|
```
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
268
|
+
3. **The AI gives you a 6-character pairing code** (expires in 5 minutes)
|
|
269
|
+
4. **In the plugin:** Toggle "Cloud Mode" β enter the code β click Connect
|
|
270
|
+
5. **You're paired!** Full write access is now available
|
|
262
271
|
|
|
263
272
|
#### What You Can Do
|
|
264
273
|
|
|
@@ -288,7 +297,7 @@ AI Client β Cloud MCP Server β Durable Object Relay β Desktop Bridge Plugi
|
|
|
288
297
|
| Feature | NPX (Recommended) | Cloud Mode | Local Git | Remote SSE |
|
|
289
298
|
|---------|-------------------|------------|-----------|------------|
|
|
290
299
|
| **Setup time** | ~10 minutes | ~5 minutes | ~15 minutes | ~2 minutes |
|
|
291
|
-
| **Total tools** | **
|
|
300
|
+
| **Total tools** | **63+** | **43** | **63+** | **22** (read-only) |
|
|
292
301
|
| **Design creation** | β
| β
| β
| β |
|
|
293
302
|
| **Variable management** | β
| β
| β
| β |
|
|
294
303
|
| **Component instantiation** | β
| β
| β
| β |
|
|
@@ -302,7 +311,7 @@ AI Client β Cloud MCP Server β Durable Object Relay β Desktop Bridge Plugi
|
|
|
302
311
|
| **Automatic updates** | β
(`@latest`) | β
| Manual (`git pull`) | β
|
|
|
303
312
|
| **Source code access** | β | β | β
| β |
|
|
304
313
|
|
|
305
|
-
> **Key insight:** Remote SSE is read-only. Cloud Mode adds write access for web AI clients without Node.js. NPX/Local Git give the full
|
|
314
|
+
> **Key insight:** Remote SSE is read-only. Cloud Mode adds write access for web AI clients without Node.js. NPX/Local Git give the full 63+ tools.
|
|
306
315
|
|
|
307
316
|
**π [Complete Feature Comparison](docs/mode-comparison.md)**
|
|
308
317
|
|
|
@@ -587,7 +596,7 @@ The **Figma Desktop Bridge** plugin is the recommended way to connect Figma to t
|
|
|
587
596
|
- The MCP server communicates via **WebSocket** through the Desktop Bridge plugin
|
|
588
597
|
- The server tries port 9223 first, then automatically falls back through ports 9224β9232 if needed
|
|
589
598
|
- The plugin scans all ports in the range and connects to every active server it finds
|
|
590
|
-
- All
|
|
599
|
+
- All 63+ tools work through the WebSocket transport
|
|
591
600
|
|
|
592
601
|
**Multiple files:** The WebSocket server supports multiple simultaneous plugin connections β one per open Figma file. Each connection is tracked by file key with independent state (selection, document changes, console logs).
|
|
593
602
|
|
|
@@ -627,11 +636,11 @@ When two processes tried to start the MCP server (e.g., Claude Desktop's Chat ta
|
|
|
627
636
|
|
|
628
637
|
### Do I Need to Do Anything?
|
|
629
638
|
|
|
630
|
-
**
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
639
|
+
**Nothing.** Multi-instance support is fully automatic:
|
|
640
|
+
- Each MCP server claims the next available port in the range
|
|
641
|
+
- The bootloader plugin scans all ports and connects to every active server
|
|
642
|
+
- Orphaned processes from closed tabs are automatically cleaned up on startup
|
|
643
|
+
- No re-importing, no manual port management
|
|
635
644
|
|
|
636
645
|
---
|
|
637
646
|
|
|
@@ -723,7 +732,7 @@ The architecture supports adding new apps with minimal boilerplate β each app
|
|
|
723
732
|
|
|
724
733
|
## π€οΈ Roadmap
|
|
725
734
|
|
|
726
|
-
**Current Status:** v1.12.0 (Stable) - Production-ready with Cloud Write Relay, Design System Kit, WebSocket-only connectivity, smart multi-file tracking,
|
|
735
|
+
**Current Status:** v1.12.0 (Stable) - Production-ready with Cloud Write Relay, Design System Kit, WebSocket-only connectivity, smart multi-file tracking, 63+ tools, Comments API, and MCP Apps
|
|
727
736
|
|
|
728
737
|
**Recent Releases:**
|
|
729
738
|
- [x] **v1.12.0** - Cloud Write Relay: web AI clients (Claude.ai, v0, Replit, Lovable) can create and modify Figma designs via cloud relay pairing β no Node.js required
|
|
@@ -223,6 +223,27 @@ export class CloudWebSocketConnector {
|
|
|
223
223
|
return this.sendCommand('SET_INSTANCE_PROPERTIES', { nodeId, properties });
|
|
224
224
|
}
|
|
225
225
|
// ============================================================================
|
|
226
|
+
// Image fill
|
|
227
|
+
// ============================================================================
|
|
228
|
+
async setImageFill(nodeIds, imageData, scaleMode = 'FILL') {
|
|
229
|
+
return this.sendCommand('SET_IMAGE_FILL', { nodeIds, imageData, scaleMode }, 60000);
|
|
230
|
+
}
|
|
231
|
+
// ============================================================================
|
|
232
|
+
// Design lint
|
|
233
|
+
// ============================================================================
|
|
234
|
+
async lintDesign(nodeId, rules, maxDepth, maxFindings) {
|
|
235
|
+
const params = {};
|
|
236
|
+
if (nodeId)
|
|
237
|
+
params.nodeId = nodeId;
|
|
238
|
+
if (rules)
|
|
239
|
+
params.rules = rules;
|
|
240
|
+
if (maxDepth !== undefined)
|
|
241
|
+
params.maxDepth = maxDepth;
|
|
242
|
+
if (maxFindings !== undefined)
|
|
243
|
+
params.maxFindings = maxFindings;
|
|
244
|
+
return this.sendCommand('LINT_DESIGN', params, 120000);
|
|
245
|
+
}
|
|
246
|
+
// ============================================================================
|
|
226
247
|
// Cache management (no-op for cloud relay)
|
|
227
248
|
// ============================================================================
|
|
228
249
|
clearFrameCache() {
|
|
@@ -1148,5 +1148,37 @@ export class FigmaDesktopConnector {
|
|
|
1148
1148
|
throw error;
|
|
1149
1149
|
}
|
|
1150
1150
|
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Set image fill on one or more nodes (decodes base64 in browser bridge, sends bytes to plugin)
|
|
1153
|
+
*/
|
|
1154
|
+
async setImageFill(nodeIds, imageData, scaleMode = 'FILL') {
|
|
1155
|
+
logger.info({ nodeIds, scaleMode, dataLength: imageData.length }, 'Setting image fill via plugin UI');
|
|
1156
|
+
const frame = await this.findPluginUIFrame();
|
|
1157
|
+
try {
|
|
1158
|
+
const result = await frame.evaluate(`window.setImageFill(${JSON.stringify(nodeIds)}, ${JSON.stringify(imageData)}, ${JSON.stringify(scaleMode)})`);
|
|
1159
|
+
logger.info({ success: result.success, imageHash: result.imageHash }, 'Image fill set');
|
|
1160
|
+
return result;
|
|
1161
|
+
}
|
|
1162
|
+
catch (error) {
|
|
1163
|
+
logger.error({ error, nodeIds }, 'Set image fill failed');
|
|
1164
|
+
throw error;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Lint design for accessibility and quality issues via plugin UI
|
|
1169
|
+
*/
|
|
1170
|
+
async lintDesign(nodeId, rules, maxDepth, maxFindings) {
|
|
1171
|
+
logger.info({ nodeId, rules }, 'Linting design via plugin UI');
|
|
1172
|
+
const frame = await this.findPluginUIFrame();
|
|
1173
|
+
try {
|
|
1174
|
+
const result = await frame.evaluate(`window.lintDesign(${JSON.stringify(nodeId || null)}, ${JSON.stringify(rules || null)}, ${JSON.stringify(maxDepth ?? null)}, ${JSON.stringify(maxFindings ?? null)})`);
|
|
1175
|
+
logger.info({ success: result.success }, 'Design lint complete');
|
|
1176
|
+
return result;
|
|
1177
|
+
}
|
|
1178
|
+
catch (error) {
|
|
1179
|
+
logger.error({ error, nodeId }, 'Design lint failed');
|
|
1180
|
+
throw error;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1151
1183
|
}
|
|
1152
1184
|
FigmaDesktopConnector.DEBUG = process.env.DEBUG === '1' || process.env.DEBUG === 'true';
|
|
@@ -227,6 +227,27 @@ export class WebSocketConnector {
|
|
|
227
227
|
return this.wsServer.sendCommand('SET_INSTANCE_PROPERTIES', { nodeId, properties });
|
|
228
228
|
}
|
|
229
229
|
// ============================================================================
|
|
230
|
+
// Image fill
|
|
231
|
+
// ============================================================================
|
|
232
|
+
async setImageFill(nodeIds, imageData, scaleMode = 'FILL') {
|
|
233
|
+
return this.wsServer.sendCommand('SET_IMAGE_FILL', { nodeIds, imageData, scaleMode }, 60000);
|
|
234
|
+
}
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// Design lint
|
|
237
|
+
// ============================================================================
|
|
238
|
+
async lintDesign(nodeId, rules, maxDepth, maxFindings) {
|
|
239
|
+
const params = {};
|
|
240
|
+
if (nodeId)
|
|
241
|
+
params.nodeId = nodeId;
|
|
242
|
+
if (rules)
|
|
243
|
+
params.rules = rules;
|
|
244
|
+
if (maxDepth !== undefined)
|
|
245
|
+
params.maxDepth = maxDepth;
|
|
246
|
+
if (maxFindings !== undefined)
|
|
247
|
+
params.maxFindings = maxFindings;
|
|
248
|
+
return this.wsServer.sendCommand('LINT_DESIGN', params, 120000);
|
|
249
|
+
}
|
|
250
|
+
// ============================================================================
|
|
230
251
|
// Cache management (no-op for WebSocket β no frame cache)
|
|
231
252
|
// ============================================================================
|
|
232
253
|
clearFrameCache() {
|
|
@@ -1227,6 +1227,49 @@ After instantiating components, use figma_take_screenshot to verify the result l
|
|
|
1227
1227
|
};
|
|
1228
1228
|
}
|
|
1229
1229
|
});
|
|
1230
|
+
// Tool: Set Image Fill on nodes
|
|
1231
|
+
server.tool("figma_set_image_fill", "Set an image fill on one or more Figma nodes. The imageData parameter accepts a base64-encoded " +
|
|
1232
|
+
"image string (JPEG/PNG). The image is decoded in the browser bridge and passed " +
|
|
1233
|
+
"as raw bytes to the Figma plugin. Requires Desktop Bridge plugin.", {
|
|
1234
|
+
nodeIds: z.array(z.string()).describe("Array of node IDs to apply the image fill to"),
|
|
1235
|
+
imageData: z.string().describe("Base64-encoded image data (JPEG/PNG)"),
|
|
1236
|
+
scaleMode: z.enum(["FILL", "FIT", "CROP", "TILE"]).optional().describe("How the image fills the node (default: FILL)"),
|
|
1237
|
+
}, async ({ nodeIds, imageData, scaleMode }) => {
|
|
1238
|
+
try {
|
|
1239
|
+
const connector = await getDesktopConnector();
|
|
1240
|
+
const result = await connector.setImageFill(nodeIds, imageData, scaleMode || "FILL");
|
|
1241
|
+
if (!result.success) {
|
|
1242
|
+
throw new Error(result.error || "Failed to set image fill");
|
|
1243
|
+
}
|
|
1244
|
+
return {
|
|
1245
|
+
content: [
|
|
1246
|
+
{
|
|
1247
|
+
type: "text",
|
|
1248
|
+
text: JSON.stringify({
|
|
1249
|
+
success: true,
|
|
1250
|
+
message: `Image fill applied to ${result.updatedCount || 0} node(s)`,
|
|
1251
|
+
imageHash: result.imageHash,
|
|
1252
|
+
nodes: result.nodes,
|
|
1253
|
+
}),
|
|
1254
|
+
},
|
|
1255
|
+
],
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
catch (error) {
|
|
1259
|
+
logger.error({ error }, "Failed to set image fill");
|
|
1260
|
+
return {
|
|
1261
|
+
content: [
|
|
1262
|
+
{
|
|
1263
|
+
type: "text",
|
|
1264
|
+
text: JSON.stringify({
|
|
1265
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1266
|
+
}),
|
|
1267
|
+
},
|
|
1268
|
+
],
|
|
1269
|
+
isError: true,
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1230
1273
|
// Tool: Set Node Strokes
|
|
1231
1274
|
server.tool("figma_set_strokes", "Set the stroke (border) on a node. Accepts hex color strings and optional stroke weight.", {
|
|
1232
1275
|
nodeId: z.string().describe("The node ID to modify"),
|
|
@@ -2003,4 +2046,46 @@ return {
|
|
|
2003
2046
|
};
|
|
2004
2047
|
}
|
|
2005
2048
|
});
|
|
2049
|
+
// Tool: Lint Design for accessibility and quality issues
|
|
2050
|
+
server.tool("figma_lint_design", "Run accessibility (WCAG) and design quality checks on the current page or a specific node tree. " +
|
|
2051
|
+
"Checks color contrast ratios, text sizing, touch targets, hardcoded values, detached components, " +
|
|
2052
|
+
"naming conventions, and layout quality. Returns categorized findings with severity levels. " +
|
|
2053
|
+
"Use natural language like 'check my design for accessibility issues' or 'lint this page'. " +
|
|
2054
|
+
"Requires Desktop Bridge plugin.", {
|
|
2055
|
+
nodeId: z.string().optional().describe("Node ID to lint (defaults to current page)"),
|
|
2056
|
+
rules: z.array(z.string()).optional().describe("Rule filter: ['all'] (default), ['wcag'], ['design-system'], ['layout'], or specific rule IDs like ['wcag-contrast', 'detached-component']"),
|
|
2057
|
+
maxDepth: z.number().optional().describe("Maximum tree depth to traverse (default: 10)"),
|
|
2058
|
+
maxFindings: z.number().optional().describe("Maximum findings before stopping (default: 100)"),
|
|
2059
|
+
}, async ({ nodeId, rules, maxDepth, maxFindings }) => {
|
|
2060
|
+
try {
|
|
2061
|
+
const connector = await getDesktopConnector();
|
|
2062
|
+
const result = await connector.lintDesign(nodeId, rules || ['all'], maxDepth || 10, maxFindings || 100);
|
|
2063
|
+
if (!result.success) {
|
|
2064
|
+
throw new Error(result.error || "Lint failed");
|
|
2065
|
+
}
|
|
2066
|
+
return {
|
|
2067
|
+
content: [
|
|
2068
|
+
{
|
|
2069
|
+
type: "text",
|
|
2070
|
+
text: JSON.stringify(result.data || result, null, 2),
|
|
2071
|
+
},
|
|
2072
|
+
],
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
catch (error) {
|
|
2076
|
+
logger.error({ error }, "Failed to lint design");
|
|
2077
|
+
return {
|
|
2078
|
+
content: [
|
|
2079
|
+
{
|
|
2080
|
+
type: "text",
|
|
2081
|
+
text: JSON.stringify({
|
|
2082
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2083
|
+
hint: "Make sure the Desktop Bridge plugin is running in your Figma file.",
|
|
2084
|
+
}),
|
|
2085
|
+
},
|
|
2086
|
+
],
|
|
2087
|
+
isError: true,
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2006
2091
|
}
|
package/dist/cloudflare/index.js
CHANGED
|
@@ -38,7 +38,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
|
|
|
38
38
|
super(...arguments);
|
|
39
39
|
this.server = new McpServer({
|
|
40
40
|
name: "Figma Console MCP",
|
|
41
|
-
version: "1.
|
|
41
|
+
version: "1.13.0",
|
|
42
42
|
});
|
|
43
43
|
this.browserManager = null;
|
|
44
44
|
this.consoleMonitor = null;
|
|
@@ -950,7 +950,7 @@ export default {
|
|
|
950
950
|
});
|
|
951
951
|
const statelessServer = new McpServer({
|
|
952
952
|
name: "Figma Console MCP",
|
|
953
|
-
version: "1.
|
|
953
|
+
version: "1.13.0",
|
|
954
954
|
});
|
|
955
955
|
// ================================================================
|
|
956
956
|
// Cloud Write Relay β Pairing Tool (stateless /mcp path)
|
|
@@ -1005,19 +1005,40 @@ export default {
|
|
|
1005
1005
|
await connector.initialize();
|
|
1006
1006
|
return connector;
|
|
1007
1007
|
};
|
|
1008
|
+
// Build a getCurrentUrl that resolves from the relay DO's file info
|
|
1009
|
+
const getCloudFileUrl = () => {
|
|
1010
|
+
// This is synchronous β we cache the file URL after first relay status check
|
|
1011
|
+
return cloudFileUrlCache;
|
|
1012
|
+
};
|
|
1013
|
+
let cloudFileUrlCache = null;
|
|
1014
|
+
// Pre-fetch file info from relay if paired
|
|
1015
|
+
try {
|
|
1016
|
+
const relayDoId = await env.OAUTH_TOKENS.get(`relay:${bearerToken}`);
|
|
1017
|
+
if (relayDoId) {
|
|
1018
|
+
const doId = env.PLUGIN_RELAY.idFromString(relayDoId);
|
|
1019
|
+
const stub = env.PLUGIN_RELAY.get(doId);
|
|
1020
|
+
const statusRes = await stub.fetch('https://relay/relay/status');
|
|
1021
|
+
const status = await statusRes.json();
|
|
1022
|
+
if (status.connected && status.fileInfo?.fileKey) {
|
|
1023
|
+
cloudFileUrlCache = `https://www.figma.com/design/${status.fileInfo.fileKey}`;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
catch {
|
|
1028
|
+
// No relay session or not paired β cloudFileUrlCache stays null
|
|
1029
|
+
}
|
|
1008
1030
|
// Register all write/manipulation tools via shared function
|
|
1009
1031
|
registerWriteTools(statelessServer, getCloudDesktopConnector);
|
|
1010
1032
|
// Register REST API tools with the authenticated Figma API
|
|
1011
|
-
registerFigmaAPITools(statelessServer, async () => statelessApi, () => null, // No
|
|
1012
|
-
() => null, // No console monitor
|
|
1033
|
+
registerFigmaAPITools(statelessServer, async () => statelessApi, getCloudFileUrl, () => null, // No console monitor
|
|
1013
1034
|
() => null, // No browser manager
|
|
1014
1035
|
undefined, // No ensureInitialized
|
|
1015
1036
|
new Map(), // Fresh variables cache per request
|
|
1016
1037
|
{ isRemoteMode: true }, getCloudDesktopConnector);
|
|
1017
|
-
registerDesignCodeTools(statelessServer, async () => statelessApi,
|
|
1038
|
+
registerDesignCodeTools(statelessServer, async () => statelessApi, getCloudFileUrl, new Map(), // Fresh variables cache per request
|
|
1018
1039
|
{ isRemoteMode: true }, getCloudDesktopConnector);
|
|
1019
|
-
registerCommentTools(statelessServer, async () => statelessApi,
|
|
1020
|
-
registerDesignSystemTools(statelessServer, async () => statelessApi,
|
|
1040
|
+
registerCommentTools(statelessServer, async () => statelessApi, getCloudFileUrl);
|
|
1041
|
+
registerDesignSystemTools(statelessServer, async () => statelessApi, getCloudFileUrl, new Map(), // Fresh variables cache per request
|
|
1021
1042
|
{ isRemoteMode: true });
|
|
1022
1043
|
await statelessServer.connect(transport);
|
|
1023
1044
|
const response = await transport.handleRequest(request);
|
|
@@ -1038,7 +1059,7 @@ export default {
|
|
|
1038
1059
|
const metadata = {
|
|
1039
1060
|
resource: url.origin,
|
|
1040
1061
|
authorization_servers: [`${url.origin}/`],
|
|
1041
|
-
scopes_supported: ["file_content:read", "
|
|
1062
|
+
scopes_supported: ["file_content:read", "file_variables:read", "library_content:read"],
|
|
1042
1063
|
bearer_methods_supported: ["header"],
|
|
1043
1064
|
resource_signing_alg_values_supported: ["RS256"]
|
|
1044
1065
|
};
|
|
@@ -1057,7 +1078,7 @@ export default {
|
|
|
1057
1078
|
authorization_endpoint: `${url.origin}/authorize`,
|
|
1058
1079
|
token_endpoint: `${url.origin}/token`,
|
|
1059
1080
|
registration_endpoint: `${url.origin}/oauth/register`,
|
|
1060
|
-
scopes_supported: ["file_content:read", "
|
|
1081
|
+
scopes_supported: ["file_content:read", "file_variables:read", "library_content:read"],
|
|
1061
1082
|
response_types_supported: ["code"],
|
|
1062
1083
|
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
1063
1084
|
token_endpoint_auth_methods_supported: ["client_secret_basic", "client_secret_post"],
|
|
@@ -1120,7 +1141,7 @@ export default {
|
|
|
1120
1141
|
const figmaAuthUrl = new URL("https://www.figma.com/oauth");
|
|
1121
1142
|
figmaAuthUrl.searchParams.set("client_id", env.FIGMA_OAUTH_CLIENT_ID);
|
|
1122
1143
|
figmaAuthUrl.searchParams.set("redirect_uri", `${oauthOrigin}/oauth/callback`);
|
|
1123
|
-
figmaAuthUrl.searchParams.set("scope", "file_content:read,
|
|
1144
|
+
figmaAuthUrl.searchParams.set("scope", "file_content:read,file_variables:read,library_content:read");
|
|
1124
1145
|
figmaAuthUrl.searchParams.set("state", stateToken);
|
|
1125
1146
|
figmaAuthUrl.searchParams.set("response_type", "code");
|
|
1126
1147
|
return Response.redirect(figmaAuthUrl.toString(), 302);
|
|
@@ -1161,7 +1182,7 @@ export default {
|
|
|
1161
1182
|
token_type: "Bearer",
|
|
1162
1183
|
expires_in: Math.max(0, Math.floor((tokenData.expiresAt - Date.now()) / 1000)),
|
|
1163
1184
|
refresh_token: tokenData.refreshToken,
|
|
1164
|
-
scope: "file_content:read
|
|
1185
|
+
scope: "file_content:read file_variables:read library_content:read"
|
|
1165
1186
|
}), {
|
|
1166
1187
|
headers: {
|
|
1167
1188
|
"Content-Type": "application/json",
|
|
@@ -1238,7 +1259,7 @@ export default {
|
|
|
1238
1259
|
token_type: "Bearer",
|
|
1239
1260
|
expires_in: tokenData.expires_in,
|
|
1240
1261
|
refresh_token: tokenData.refresh_token || refreshToken,
|
|
1241
|
-
scope: "file_content:read
|
|
1262
|
+
scope: "file_content:read file_variables:read library_content:read"
|
|
1242
1263
|
}), {
|
|
1243
1264
|
headers: {
|
|
1244
1265
|
"Content-Type": "application/json",
|
|
@@ -1310,7 +1331,7 @@ export default {
|
|
|
1310
1331
|
const figmaAuthUrl = new URL("https://www.figma.com/oauth");
|
|
1311
1332
|
figmaAuthUrl.searchParams.set("client_id", env.FIGMA_OAUTH_CLIENT_ID);
|
|
1312
1333
|
figmaAuthUrl.searchParams.set("redirect_uri", redirectUri);
|
|
1313
|
-
figmaAuthUrl.searchParams.set("scope", "file_content:read,
|
|
1334
|
+
figmaAuthUrl.searchParams.set("scope", "file_content:read,file_variables:read,library_content:read");
|
|
1314
1335
|
figmaAuthUrl.searchParams.set("state", stateToken);
|
|
1315
1336
|
figmaAuthUrl.searchParams.set("response_type", "code");
|
|
1316
1337
|
return Response.redirect(figmaAuthUrl.toString(), 302);
|
|
@@ -1558,7 +1579,7 @@ export default {
|
|
|
1558
1579
|
return new Response(JSON.stringify({
|
|
1559
1580
|
status: "healthy",
|
|
1560
1581
|
service: "Figma Console MCP",
|
|
1561
|
-
version: "1.
|
|
1582
|
+
version: "1.13.0",
|
|
1562
1583
|
endpoints: {
|
|
1563
1584
|
mcp: ["/sse", "/mcp"],
|
|
1564
1585
|
oauth_mcp_spec: ["/.well-known/oauth-authorization-server", "/authorize", "/token", "/oauth/register"],
|
|
@@ -1604,13 +1625,13 @@ export default {
|
|
|
1604
1625
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1605
1626
|
<title>Figma Console MCP - The Most Comprehensive MCP Server for Figma</title>
|
|
1606
1627
|
<link rel="icon" type="image/svg+xml" href="https://docs.figma-console-mcp.southleft.com/favicon.svg">
|
|
1607
|
-
<meta name="description" content="Turn your Figma design system into a living API.
|
|
1628
|
+
<meta name="description" content="Turn your Figma design system into a living API. 59+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
|
|
1608
1629
|
|
|
1609
1630
|
<!-- Open Graph -->
|
|
1610
1631
|
<meta property="og:type" content="website">
|
|
1611
1632
|
<meta property="og:url" content="https://figma-console-mcp.southleft.com">
|
|
1612
1633
|
<meta property="og:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
|
|
1613
|
-
<meta property="og:description" content="The most comprehensive MCP server for Figma.
|
|
1634
|
+
<meta property="og:description" content="The most comprehensive MCP server for Figma. 59+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
|
|
1614
1635
|
<meta property="og:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
|
|
1615
1636
|
<meta property="og:image:width" content="1200">
|
|
1616
1637
|
<meta property="og:image:height" content="630">
|
|
@@ -1618,7 +1639,7 @@ export default {
|
|
|
1618
1639
|
<!-- Twitter -->
|
|
1619
1640
|
<meta name="twitter:card" content="summary_large_image">
|
|
1620
1641
|
<meta name="twitter:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
|
|
1621
|
-
<meta name="twitter:description" content="The most comprehensive MCP server for Figma.
|
|
1642
|
+
<meta name="twitter:description" content="The most comprehensive MCP server for Figma. 59+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
|
|
1622
1643
|
<meta name="twitter:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
|
|
1623
1644
|
|
|
1624
1645
|
<meta name="theme-color" content="#0D9488">
|
|
@@ -2502,7 +2523,7 @@ export default {
|
|
|
2502
2523
|
<div class="grid-cell showcase-cell rule-left">
|
|
2503
2524
|
<div class="showcase-label">What AI Can Access</div>
|
|
2504
2525
|
<div class="showcase-stat">
|
|
2505
|
-
<span class="number">
|
|
2526
|
+
<span class="number">59+</span>
|
|
2506
2527
|
<span class="label">MCP tools for Figma</span>
|
|
2507
2528
|
</div>
|
|
2508
2529
|
<div class="capability-list">
|
|
@@ -2519,8 +2540,16 @@ export default {
|
|
|
2519
2540
|
<span>Programmatic design creation</span>
|
|
2520
2541
|
</div>
|
|
2521
2542
|
<div class="capability-item">
|
|
2522
|
-
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><
|
|
2523
|
-
<span>
|
|
2543
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
2544
|
+
<span>WCAG accessibility linting</span>
|
|
2545
|
+
</div>
|
|
2546
|
+
<div class="capability-item">
|
|
2547
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"/></svg>
|
|
2548
|
+
<span>Cloud relay for web AI clients</span>
|
|
2549
|
+
</div>
|
|
2550
|
+
<div class="capability-item">
|
|
2551
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
|
|
2552
|
+
<span>Visual debugging and screenshots</span>
|
|
2524
2553
|
</div>
|
|
2525
2554
|
</div>
|
|
2526
2555
|
</div>
|
|
@@ -2568,8 +2597,8 @@ export default {
|
|
|
2568
2597
|
<div class="capability-icon">
|
|
2569
2598
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
|
|
2570
2599
|
</div>
|
|
2571
|
-
<h3>
|
|
2572
|
-
<p>Capture
|
|
2600
|
+
<h3>Visual Debugging</h3>
|
|
2601
|
+
<p>Capture screenshots, inspect node properties, and track selection changes. Let AI analyze your designs and suggest improvements.</p>
|
|
2573
2602
|
</div>
|
|
2574
2603
|
</div>
|
|
2575
2604
|
|
|
@@ -2599,7 +2628,11 @@ export default {
|
|
|
2599
2628
|
</div>
|
|
2600
2629
|
<div class="prompt-item">
|
|
2601
2630
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
|
2602
|
-
<span>"
|
|
2631
|
+
<span>"Check my design for accessibility issues"</span>
|
|
2632
|
+
</div>
|
|
2633
|
+
<div class="prompt-item">
|
|
2634
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
|
2635
|
+
<span>"Connect to my Figma plugin and create a card component"</span>
|
|
2603
2636
|
</div>
|
|
2604
2637
|
</div>
|
|
2605
2638
|
</div>
|
|
@@ -2623,7 +2656,7 @@ export default {
|
|
|
2623
2656
|
</li>
|
|
2624
2657
|
<li>
|
|
2625
2658
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M5 13l4 4L19 7"/></svg>
|
|
2626
|
-
<span>
|
|
2659
|
+
<span>Lint designs for accessibility and quality issues</span>
|
|
2627
2660
|
</li>
|
|
2628
2661
|
</ul>
|
|
2629
2662
|
</div>
|
|
@@ -2684,7 +2717,7 @@ export default {
|
|
|
2684
2717
|
<div class="grid-cell getting-started-cell">
|
|
2685
2718
|
<div class="getting-started-content">
|
|
2686
2719
|
<h3>Ready to get started?</h3>
|
|
2687
|
-
<p>
|
|
2720
|
+
<p>Three ways to connect: local mode for full capabilities, cloud mode for web AI clients like Claude.ai and v0, or remote mode for quick read-only access. Our docs will guide you through the right path.</p>
|
|
2688
2721
|
</div>
|
|
2689
2722
|
<div class="getting-started-actions">
|
|
2690
2723
|
<a href="https://docs.figma-console-mcp.southleft.com/setup" class="btn btn-primary">
|