figma-console-mcp 1.7.0 → 1.9.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 +81 -52
- package/dist/cloudflare/core/comment-tools.js +292 -0
- package/dist/cloudflare/core/design-code-tools.js +2 -2
- package/dist/cloudflare/core/figma-api.js +34 -0
- package/dist/cloudflare/core/figma-connector.js +8 -0
- package/dist/cloudflare/core/figma-desktop-connector.js +69 -2
- package/dist/cloudflare/core/figma-tools.js +118 -98
- package/dist/cloudflare/core/websocket-connector.js +235 -0
- package/dist/cloudflare/core/websocket-server.js +603 -0
- package/dist/cloudflare/index.js +9 -6
- package/dist/core/comment-tools.d.ts +11 -0
- package/dist/core/comment-tools.d.ts.map +1 -0
- package/dist/core/comment-tools.js +293 -0
- package/dist/core/comment-tools.js.map +1 -0
- package/dist/core/design-code-tools.js +2 -2
- package/dist/core/design-code-tools.js.map +1 -1
- package/dist/core/figma-api.d.ts +23 -0
- package/dist/core/figma-api.d.ts.map +1 -1
- package/dist/core/figma-api.js +34 -0
- package/dist/core/figma-api.js.map +1 -1
- package/dist/core/figma-connector.d.ts +47 -0
- package/dist/core/figma-connector.d.ts.map +1 -0
- package/dist/core/figma-connector.js +9 -0
- package/dist/core/figma-connector.js.map +1 -0
- package/dist/core/figma-desktop-connector.d.ts +14 -1
- package/dist/core/figma-desktop-connector.d.ts.map +1 -1
- package/dist/core/figma-desktop-connector.js +69 -2
- package/dist/core/figma-desktop-connector.js.map +1 -1
- package/dist/core/figma-tools.d.ts +1 -1
- package/dist/core/figma-tools.d.ts.map +1 -1
- package/dist/core/figma-tools.js +118 -98
- package/dist/core/figma-tools.js.map +1 -1
- package/dist/core/websocket-connector.d.ts +53 -0
- package/dist/core/websocket-connector.d.ts.map +1 -0
- package/dist/core/websocket-connector.js +236 -0
- package/dist/core/websocket-connector.js.map +1 -0
- package/dist/core/websocket-server.d.ts +183 -0
- package/dist/core/websocket-server.d.ts.map +1 -0
- package/dist/core/websocket-server.js +604 -0
- package/dist/core/websocket-server.js.map +1 -0
- package/dist/local.d.ts +11 -3
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +847 -276
- package/dist/local.js.map +1 -1
- package/figma-desktop-bridge/README.md +30 -16
- package/figma-desktop-bridge/code.js +195 -3
- package/figma-desktop-bridge/manifest.json +4 -1
- package/figma-desktop-bridge/ui.html +229 -0
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -42,9 +42,9 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
|
|
|
42
42
|
| **Edit existing designs** | ✅ | ❌ |
|
|
43
43
|
| **Manage design tokens/variables** | ✅ | ❌ |
|
|
44
44
|
| Desktop Bridge plugin | ✅ | ❌ |
|
|
45
|
-
| **Total tools available** | **
|
|
45
|
+
| **Total tools available** | **56+** | **16** |
|
|
46
46
|
|
|
47
|
-
> **Bottom line:** Remote SSE is **read-only
|
|
47
|
+
> **Bottom line:** Remote SSE is **read-only** with ~34% of the tools. If you want AI to actually design in Figma, use NPX Setup.
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
@@ -52,13 +52,13 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
|
|
|
52
52
|
|
|
53
53
|
**Best for:** Designers who want full AI-assisted design capabilities.
|
|
54
54
|
|
|
55
|
-
**What you get:** All
|
|
55
|
+
**What you get:** All 56+ tools including design creation, variable management, and component instantiation.
|
|
56
56
|
|
|
57
57
|
#### Prerequisites
|
|
58
58
|
|
|
59
59
|
- [ ] **Node.js 18+** — Check with `node --version` ([Download](https://nodejs.org))
|
|
60
60
|
- [ ] **Figma Desktop** installed (not just the web app)
|
|
61
|
-
- [ ] **
|
|
61
|
+
- [ ] **An MCP client** (Claude Code, Cursor, Windsurf, Claude Desktop, etc.)
|
|
62
62
|
|
|
63
63
|
#### Step 1: Get Your Figma Token
|
|
64
64
|
|
|
@@ -67,9 +67,16 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
|
|
|
67
67
|
3. Enter description: `Figma Console MCP`
|
|
68
68
|
4. **Copy the token** — you won't see it again! (starts with `figd_`)
|
|
69
69
|
|
|
70
|
-
#### Step 2: Configure
|
|
70
|
+
#### Step 2: Configure Your MCP Client
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
**Claude Code (CLI):**
|
|
73
|
+
```bash
|
|
74
|
+
claude mcp add figma-console -s user -e FIGMA_ACCESS_TOKEN=figd_YOUR_TOKEN_HERE -- npx -y figma-console-mcp@latest
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Cursor / Windsurf / Claude Desktop:**
|
|
78
|
+
|
|
79
|
+
Add to your MCP config file:
|
|
73
80
|
|
|
74
81
|
```json
|
|
75
82
|
{
|
|
@@ -85,32 +92,34 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
|
|
|
85
92
|
}
|
|
86
93
|
```
|
|
87
94
|
|
|
88
|
-
#### Step 3:
|
|
95
|
+
#### Step 3: Connect to Figma Desktop
|
|
89
96
|
|
|
90
|
-
**
|
|
97
|
+
**Option A — Desktop Bridge Plugin (Recommended):**
|
|
98
|
+
1. Open Figma Desktop normally (no special flags needed)
|
|
99
|
+
2. Go to **Plugins → Development → Import plugin from manifest...**
|
|
100
|
+
3. Select `figma-desktop-bridge/manifest.json` from the figma-console-mcp directory
|
|
101
|
+
4. Run the plugin in your Figma file — it auto-connects via WebSocket
|
|
91
102
|
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
open -a "Figma" --args --remote-debugging-port=9222
|
|
95
|
-
```
|
|
103
|
+
> One-time setup. No need to restart Figma with special flags.
|
|
96
104
|
|
|
97
|
-
**
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
**Option B — CDP Debug Mode (Alternative):**
|
|
106
|
+
|
|
107
|
+
Quit Figma completely, then restart with:
|
|
108
|
+
- **macOS:** `open -a "Figma" --args --remote-debugging-port=9222`
|
|
109
|
+
- **Windows:** `cmd /c "%LOCALAPPDATA%\Figma\Figma.exe" --remote-debugging-port=9222`
|
|
101
110
|
|
|
102
111
|
Verify at [http://localhost:9222](http://localhost:9222) — you should see inspectable Figma pages.
|
|
103
112
|
|
|
104
|
-
#### Step 4: Restart
|
|
113
|
+
#### Step 4: Restart Your MCP Client
|
|
105
114
|
|
|
106
|
-
|
|
115
|
+
Restart your MCP client to load the new configuration.
|
|
107
116
|
|
|
108
117
|
#### Step 5: Test It!
|
|
109
118
|
|
|
110
119
|
```
|
|
111
120
|
Check Figma status
|
|
112
121
|
```
|
|
113
|
-
→ Should show
|
|
122
|
+
→ Should show connection status with active transport (WebSocket or CDP)
|
|
114
123
|
|
|
115
124
|
```
|
|
116
125
|
Create a simple frame with a blue background
|
|
@@ -125,7 +134,7 @@ Create a simple frame with a blue background
|
|
|
125
134
|
|
|
126
135
|
**Best for:** Developers who want to modify source code or contribute to the project.
|
|
127
136
|
|
|
128
|
-
**What you get:** Same
|
|
137
|
+
**What you get:** Same 56+ tools as NPX, plus full source code access.
|
|
129
138
|
|
|
130
139
|
#### Quick Setup
|
|
131
140
|
|
|
@@ -137,7 +146,7 @@ npm install
|
|
|
137
146
|
npm run build:local
|
|
138
147
|
```
|
|
139
148
|
|
|
140
|
-
#### Configure
|
|
149
|
+
#### Configure Your MCP Client
|
|
141
150
|
|
|
142
151
|
```json
|
|
143
152
|
{
|
|
@@ -153,7 +162,7 @@ npm run build:local
|
|
|
153
162
|
}
|
|
154
163
|
```
|
|
155
164
|
|
|
156
|
-
Then follow [NPX Steps 3-5](#step-3-
|
|
165
|
+
Then follow [NPX Steps 3-5](#step-3-connect-to-figma-desktop) above.
|
|
157
166
|
|
|
158
167
|
**📖 [Complete Setup Guide](docs/setup.md)**
|
|
159
168
|
|
|
@@ -163,7 +172,7 @@ Then follow [NPX Steps 3-5](#step-3-start-figma-with-debug-mode) above.
|
|
|
163
172
|
|
|
164
173
|
**Best for:** Quickly evaluating the tool or read-only design data extraction.
|
|
165
174
|
|
|
166
|
-
**What you get:**
|
|
175
|
+
**What you get:** 21 read-only tools — view data, take screenshots, read logs, design-code parity. **Cannot create or modify designs.**
|
|
167
176
|
|
|
168
177
|
#### Claude Desktop (UI Method)
|
|
169
178
|
|
|
@@ -212,7 +221,7 @@ Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide
|
|
|
212
221
|
| Feature | NPX (Recommended) | Local Git | Remote SSE |
|
|
213
222
|
|---------|-------------------|-----------|------------|
|
|
214
223
|
| **Setup time** | ~10 minutes | ~15 minutes | ~2 minutes |
|
|
215
|
-
| **Total tools** | **
|
|
224
|
+
| **Total tools** | **56+** | **56+** | **21** (read-only) |
|
|
216
225
|
| **Design creation** | ✅ | ✅ | ❌ |
|
|
217
226
|
| **Variable management** | ✅ | ✅ | ❌ |
|
|
218
227
|
| **Component instantiation** | ✅ | ✅ | ❌ |
|
|
@@ -224,7 +233,7 @@ Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide
|
|
|
224
233
|
| **Automatic updates** | ✅ (`@latest`) | Manual (`git pull`) | ✅ |
|
|
225
234
|
| **Source code access** | ❌ | ✅ | ❌ |
|
|
226
235
|
|
|
227
|
-
> **Key insight:** Remote SSE is read-only with ~
|
|
236
|
+
> **Key insight:** Remote SSE is read-only with ~34% of the tools. Use NPX for full capabilities.
|
|
228
237
|
|
|
229
238
|
**📖 [Complete Feature Comparison](docs/mode-comparison.md)**
|
|
230
239
|
|
|
@@ -385,7 +394,7 @@ Navigate to this file and capture what's on screen
|
|
|
385
394
|
|
|
386
395
|
## 🎨 AI-Assisted Design Creation
|
|
387
396
|
|
|
388
|
-
> **⚠️ Local Mode Only:** This feature requires the Desktop Bridge plugin and only works with
|
|
397
|
+
> **⚠️ Local Mode Only:** This feature requires the Desktop Bridge plugin and only works with Local Mode installation (NPX or Local Git). Remote Mode is read-only and cannot create or modify designs.
|
|
389
398
|
|
|
390
399
|
One of the most powerful capabilities of this MCP server is the ability to **design complete UI components and pages directly in Figma through natural language conversation** with any MCP-compatible AI assistant like Claude Desktop or Claude Code.
|
|
391
400
|
|
|
@@ -458,31 +467,51 @@ This ensures designs aren't just technically correct—they *look* right.
|
|
|
458
467
|
|
|
459
468
|
---
|
|
460
469
|
|
|
461
|
-
## 🎨 Desktop Bridge Plugin (
|
|
470
|
+
## 🎨 Desktop Bridge Plugin (Recommended Connection)
|
|
462
471
|
|
|
463
|
-
The **Figma Desktop Bridge** plugin
|
|
472
|
+
The **Figma Desktop Bridge** plugin is the recommended way to connect Figma to the MCP server. It communicates via WebSocket — no special Figma launch flags needed, and it persists across Figma restarts.
|
|
464
473
|
|
|
465
|
-
###
|
|
466
|
-
- ✅ Variables without Enterprise API
|
|
467
|
-
- ✅ Reliable component descriptions (bypasses API bugs)
|
|
468
|
-
- ✅ Multi-mode support (Light/Dark/Brand variants)
|
|
474
|
+
### Setup
|
|
469
475
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
476
|
+
1. Open Figma Desktop (normal launch — no debug flags needed)
|
|
477
|
+
2. Go to **Plugins → Development → Import plugin from manifest...**
|
|
478
|
+
3. Select `figma-desktop-bridge/manifest.json` from the figma-console-mcp directory
|
|
479
|
+
4. Run the plugin in your Figma file — it auto-connects to `ws://localhost:9223`
|
|
480
|
+
5. Ask your AI: "Check Figma status" to verify the connection
|
|
474
481
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
**Setup:**
|
|
478
|
-
1. Install Local Mode MCP
|
|
479
|
-
2. Download plugin from [Releases](https://github.com/southleft/figma-console-mcp/releases/latest)
|
|
480
|
-
3. Import plugin: Figma Desktop → Plugins → Development → Import plugin from manifest
|
|
481
|
-
4. Run plugin in your Figma file
|
|
482
|
-
5. Ask Claude: "Create a button component" or "Show me the design variables"
|
|
482
|
+
> **One-time import.** Once imported, the plugin stays in your Development plugins list. Just run it whenever you want to use the MCP.
|
|
483
483
|
|
|
484
484
|
**📖 [Desktop Bridge Documentation](figma-desktop-bridge/README.md)**
|
|
485
485
|
|
|
486
|
+
### Capabilities
|
|
487
|
+
|
|
488
|
+
**Read Operations:**
|
|
489
|
+
- Variables without Enterprise API
|
|
490
|
+
- Reliable component descriptions (bypasses API bugs)
|
|
491
|
+
- Multi-mode support (Light/Dark/Brand variants)
|
|
492
|
+
- Real-time selection tracking and document change monitoring
|
|
493
|
+
|
|
494
|
+
**Write Operations:**
|
|
495
|
+
- **Design Creation** - Create frames, shapes, text, components via `figma_execute`
|
|
496
|
+
- **Variable Management** - Full CRUD operations on variables and collections
|
|
497
|
+
- **Mode Management** - Add and rename modes for multi-theme support
|
|
498
|
+
|
|
499
|
+
### How the Transport Works
|
|
500
|
+
|
|
501
|
+
- The MCP server tries **WebSocket first** (port 9223, instant check) via the Desktop Bridge plugin
|
|
502
|
+
- If no WebSocket client is connected, it falls back to **CDP** (port 9222) if available
|
|
503
|
+
- The transport is selected automatically per-command — no configuration needed
|
|
504
|
+
- All 56+ tools work identically through either transport
|
|
505
|
+
|
|
506
|
+
**CDP as fallback:** If you also launch Figma with `--remote-debugging-port=9222`, CDP serves as a fallback transport. CDP captures all page-level console logs while WebSocket captures plugin-context logs. `figma_navigate` requires CDP for browser-level navigation; in WebSocket mode it returns the connected file info with guidance instead.
|
|
507
|
+
|
|
508
|
+
**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).
|
|
509
|
+
|
|
510
|
+
**Environment variables:**
|
|
511
|
+
- `FIGMA_WS_PORT` — Override the server-side WebSocket port (default: 9223). Note: the plugin UI and manifest are hard-coded to port 9223. Using a custom port also requires updating `wsPort` in `ui.html` and `allowedDomains` in `manifest.json`.
|
|
512
|
+
|
|
513
|
+
**Plugin Limitation:** Only works in Local Mode (NPX or Local Git). Remote SSE mode cannot access it.
|
|
514
|
+
|
|
486
515
|
---
|
|
487
516
|
|
|
488
517
|
## 🧩 MCP Apps (Experimental)
|
|
@@ -581,18 +610,18 @@ The architecture supports adding new apps with minimal boilerplate — each app
|
|
|
581
610
|
|
|
582
611
|
## 🛤️ Roadmap
|
|
583
612
|
|
|
584
|
-
**Current Status:** v1.
|
|
613
|
+
**Current Status:** v1.9.0 (Stable) - Production-ready with WebSocket Bridge, 56+ tools, Comments API, and MCP Apps
|
|
585
614
|
|
|
586
|
-
**
|
|
587
|
-
- [
|
|
615
|
+
**Recent Releases:**
|
|
616
|
+
- [x] **v1.8.0** - WebSocket Bridge transport (CDP-free connectivity), real-time selection/document tracking, `figma_get_selection` + `figma_get_design_changes` tools
|
|
617
|
+
- [x] **v1.7.0** - MCP Apps (Token Browser, Design System Dashboard), batch variable operations, design-code parity tools
|
|
618
|
+
- [x] **v1.5.0** - Node manipulation tools, component property management, component set arrangement
|
|
619
|
+
- [x] **v1.3.0** - Design creation via `figma_execute`, variable CRUD operations
|
|
620
|
+
|
|
621
|
+
**Coming Next:**
|
|
588
622
|
- [ ] **Component template library** - Common UI pattern generation
|
|
589
|
-
- [ ] **Batch variant operations** - Create multiple variants efficiently
|
|
590
623
|
- [ ] **Visual regression testing** - Screenshot diff capabilities
|
|
591
|
-
|
|
592
|
-
**Future:**
|
|
593
|
-
- [ ] **Multi-user debugging** - Collaborative debugging sessions
|
|
594
624
|
- [ ] **Design linting** - Automated compliance and accessibility checks
|
|
595
|
-
- [ ] **VS Code extension** - Simplified setup and integration
|
|
596
625
|
- [ ] **AI enhancements** - Intelligent component suggestions and auto-layout optimization
|
|
597
626
|
|
|
598
627
|
**📖 [Full Roadmap](docs/ROADMAP.md)**
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Comments MCP Tools
|
|
3
|
+
* Tools for getting, posting, and deleting comments on Figma files via REST API.
|
|
4
|
+
* Works in both local and Cloudflare Workers modes — no Plugin API dependency.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { extractFileKey } from "./figma-api.js";
|
|
8
|
+
import { createChildLogger } from "./logger.js";
|
|
9
|
+
const logger = createChildLogger({ component: "comment-tools" });
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Tool Registration
|
|
12
|
+
// ============================================================================
|
|
13
|
+
export function registerCommentTools(server, getFigmaAPI, getCurrentUrl, options) {
|
|
14
|
+
// -----------------------------------------------------------------------
|
|
15
|
+
// Tool: figma_get_comments
|
|
16
|
+
// -----------------------------------------------------------------------
|
|
17
|
+
server.tool("figma_get_comments", "Get comments on a Figma file. Returns comment threads with author, message, timestamps, and pinned node locations. Use include_resolved to also see resolved comments.", {
|
|
18
|
+
fileUrl: z
|
|
19
|
+
.string()
|
|
20
|
+
.url()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Figma file URL. Uses current URL if omitted."),
|
|
23
|
+
as_md: z
|
|
24
|
+
.boolean()
|
|
25
|
+
.optional()
|
|
26
|
+
.default(false)
|
|
27
|
+
.describe("Return comment message bodies as markdown. Default: false"),
|
|
28
|
+
include_resolved: z
|
|
29
|
+
.boolean()
|
|
30
|
+
.optional()
|
|
31
|
+
.default(false)
|
|
32
|
+
.describe("Include resolved (completed) comment threads. Default: false (only active comments)"),
|
|
33
|
+
}, async ({ fileUrl, as_md = false, include_resolved = false }) => {
|
|
34
|
+
try {
|
|
35
|
+
const url = fileUrl || getCurrentUrl();
|
|
36
|
+
if (!url) {
|
|
37
|
+
return {
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: "text",
|
|
41
|
+
text: JSON.stringify({
|
|
42
|
+
error: "no_file_url",
|
|
43
|
+
message: "No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).",
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
isError: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const fileKey = extractFileKey(url);
|
|
51
|
+
if (!fileKey) {
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: JSON.stringify({
|
|
57
|
+
error: "invalid_url",
|
|
58
|
+
message: `Invalid Figma URL: ${url}`,
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
logger.info({ fileKey, as_md, include_resolved }, "Fetching comments");
|
|
66
|
+
const api = await getFigmaAPI();
|
|
67
|
+
const response = await api.getComments(fileKey, { as_md });
|
|
68
|
+
const allComments = response.comments || [];
|
|
69
|
+
// Filter out resolved comments unless explicitly requested
|
|
70
|
+
const comments = include_resolved
|
|
71
|
+
? allComments
|
|
72
|
+
: allComments.filter((c) => !c.resolved_at);
|
|
73
|
+
const result = {
|
|
74
|
+
comments,
|
|
75
|
+
summary: {
|
|
76
|
+
total: allComments.length,
|
|
77
|
+
active: allComments.filter((c) => !c.resolved_at).length,
|
|
78
|
+
resolved: allComments.filter((c) => c.resolved_at).length,
|
|
79
|
+
returned: comments.length,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
text: JSON.stringify(result),
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
93
|
+
logger.error({ error }, "Failed to get comments");
|
|
94
|
+
return {
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: JSON.stringify({
|
|
99
|
+
error: "get_comments_failed",
|
|
100
|
+
message: `Cannot get comments. ${message}`,
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
isError: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
// -----------------------------------------------------------------------
|
|
109
|
+
// Tool: figma_post_comment
|
|
110
|
+
// -----------------------------------------------------------------------
|
|
111
|
+
server.tool("figma_post_comment", "Post a comment on a Figma file, optionally pinned to a specific design node. Use after figma_check_design_parity to notify designers of drift when code is the canonical source. Supports replies to existing comment threads.", {
|
|
112
|
+
fileUrl: z
|
|
113
|
+
.string()
|
|
114
|
+
.url()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe("Figma file URL. Uses current URL if omitted."),
|
|
117
|
+
message: z
|
|
118
|
+
.string()
|
|
119
|
+
.describe("The comment message text. Supports basic formatting."),
|
|
120
|
+
node_id: z
|
|
121
|
+
.string()
|
|
122
|
+
.optional()
|
|
123
|
+
.describe("Node ID to pin the comment to (e.g., '695:313'). Comment appears on that element in Figma."),
|
|
124
|
+
x: z
|
|
125
|
+
.number()
|
|
126
|
+
.optional()
|
|
127
|
+
.describe("X coordinate for comment placement (absolute canvas position). Used with node_id."),
|
|
128
|
+
y: z
|
|
129
|
+
.number()
|
|
130
|
+
.optional()
|
|
131
|
+
.describe("Y coordinate for comment placement (absolute canvas position). Used with node_id."),
|
|
132
|
+
reply_to_comment_id: z
|
|
133
|
+
.string()
|
|
134
|
+
.optional()
|
|
135
|
+
.describe("ID of an existing comment to reply to. Creates a threaded reply instead of a new top-level comment."),
|
|
136
|
+
}, async ({ fileUrl, message, node_id, x, y, reply_to_comment_id }) => {
|
|
137
|
+
try {
|
|
138
|
+
const url = fileUrl || getCurrentUrl();
|
|
139
|
+
if (!url) {
|
|
140
|
+
return {
|
|
141
|
+
content: [
|
|
142
|
+
{
|
|
143
|
+
type: "text",
|
|
144
|
+
text: JSON.stringify({
|
|
145
|
+
error: "no_file_url",
|
|
146
|
+
message: "No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).",
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
isError: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const fileKey = extractFileKey(url);
|
|
154
|
+
if (!fileKey) {
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: "text",
|
|
159
|
+
text: JSON.stringify({
|
|
160
|
+
error: "invalid_url",
|
|
161
|
+
message: `Invalid Figma URL: ${url}`,
|
|
162
|
+
}),
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
isError: true,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
logger.info({ fileKey, node_id, reply_to_comment_id }, "Posting comment");
|
|
169
|
+
const api = await getFigmaAPI();
|
|
170
|
+
// Build client_meta for pinning to a node/position
|
|
171
|
+
// Figma API requires node_offset when node_id is present — default to (0,0) if not specified
|
|
172
|
+
let clientMeta;
|
|
173
|
+
if (node_id) {
|
|
174
|
+
clientMeta = {
|
|
175
|
+
node_id,
|
|
176
|
+
node_offset: { x: x ?? 0, y: y ?? 0 },
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const result = await api.postComment(fileKey, message, clientMeta, reply_to_comment_id);
|
|
180
|
+
return {
|
|
181
|
+
content: [
|
|
182
|
+
{
|
|
183
|
+
type: "text",
|
|
184
|
+
text: JSON.stringify({
|
|
185
|
+
success: true,
|
|
186
|
+
comment: {
|
|
187
|
+
id: result.id,
|
|
188
|
+
message: result.message,
|
|
189
|
+
created_at: result.created_at,
|
|
190
|
+
user: result.user,
|
|
191
|
+
client_meta: result.client_meta,
|
|
192
|
+
order_id: result.order_id,
|
|
193
|
+
},
|
|
194
|
+
}),
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
const message_text = error instanceof Error ? error.message : String(error);
|
|
201
|
+
logger.error({ error }, "Failed to post comment");
|
|
202
|
+
return {
|
|
203
|
+
content: [
|
|
204
|
+
{
|
|
205
|
+
type: "text",
|
|
206
|
+
text: JSON.stringify({
|
|
207
|
+
error: "post_comment_failed",
|
|
208
|
+
message: `Cannot post comment. ${message_text}`,
|
|
209
|
+
}),
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
isError: true,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
// -----------------------------------------------------------------------
|
|
217
|
+
// Tool: figma_delete_comment
|
|
218
|
+
// -----------------------------------------------------------------------
|
|
219
|
+
server.tool("figma_delete_comment", "Delete a comment from a Figma file by its comment ID. Use figma_get_comments to find comment IDs first.", {
|
|
220
|
+
fileUrl: z
|
|
221
|
+
.string()
|
|
222
|
+
.url()
|
|
223
|
+
.optional()
|
|
224
|
+
.describe("Figma file URL. Uses current URL if omitted."),
|
|
225
|
+
comment_id: z
|
|
226
|
+
.string()
|
|
227
|
+
.describe("The ID of the comment to delete. Get IDs from figma_get_comments."),
|
|
228
|
+
}, async ({ fileUrl, comment_id }) => {
|
|
229
|
+
try {
|
|
230
|
+
const url = fileUrl || getCurrentUrl();
|
|
231
|
+
if (!url) {
|
|
232
|
+
return {
|
|
233
|
+
content: [
|
|
234
|
+
{
|
|
235
|
+
type: "text",
|
|
236
|
+
text: JSON.stringify({
|
|
237
|
+
error: "no_file_url",
|
|
238
|
+
message: "No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).",
|
|
239
|
+
}),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
isError: true,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const fileKey = extractFileKey(url);
|
|
246
|
+
if (!fileKey) {
|
|
247
|
+
return {
|
|
248
|
+
content: [
|
|
249
|
+
{
|
|
250
|
+
type: "text",
|
|
251
|
+
text: JSON.stringify({
|
|
252
|
+
error: "invalid_url",
|
|
253
|
+
message: `Invalid Figma URL: ${url}`,
|
|
254
|
+
}),
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
isError: true,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
logger.info({ fileKey, comment_id }, "Deleting comment");
|
|
261
|
+
const api = await getFigmaAPI();
|
|
262
|
+
await api.deleteComment(fileKey, comment_id);
|
|
263
|
+
return {
|
|
264
|
+
content: [
|
|
265
|
+
{
|
|
266
|
+
type: "text",
|
|
267
|
+
text: JSON.stringify({
|
|
268
|
+
success: true,
|
|
269
|
+
deleted_comment_id: comment_id,
|
|
270
|
+
}),
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
277
|
+
logger.error({ error }, "Failed to delete comment");
|
|
278
|
+
return {
|
|
279
|
+
content: [
|
|
280
|
+
{
|
|
281
|
+
type: "text",
|
|
282
|
+
text: JSON.stringify({
|
|
283
|
+
error: "delete_comment_failed",
|
|
284
|
+
message: `Cannot delete comment. ${message}`,
|
|
285
|
+
}),
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
isError: true,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
@@ -1287,7 +1287,7 @@ export function registerDesignCodeTools(server, getFigmaAPI, getCurrentUrl, vari
|
|
|
1287
1287
|
try {
|
|
1288
1288
|
const url = fileUrl || getCurrentUrl();
|
|
1289
1289
|
if (!url) {
|
|
1290
|
-
throw new Error("No Figma file URL
|
|
1290
|
+
throw new Error("No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).");
|
|
1291
1291
|
}
|
|
1292
1292
|
const fileKey = extractFileKey(url);
|
|
1293
1293
|
if (!fileKey) {
|
|
@@ -1474,7 +1474,7 @@ export function registerDesignCodeTools(server, getFigmaAPI, getCurrentUrl, vari
|
|
|
1474
1474
|
try {
|
|
1475
1475
|
const url = fileUrl || getCurrentUrl();
|
|
1476
1476
|
if (!url) {
|
|
1477
|
-
throw new Error("No Figma file URL
|
|
1477
|
+
throw new Error("No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).");
|
|
1478
1478
|
}
|
|
1479
1479
|
const fileKey = extractFileKey(url);
|
|
1480
1480
|
if (!fileKey) {
|
|
@@ -280,6 +280,40 @@ export class FigmaAPI {
|
|
|
280
280
|
logger.info({ fileKey, ids, options }, 'Rendering images');
|
|
281
281
|
return this.request(endpoint);
|
|
282
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* GET /v1/files/:file_key/comments
|
|
285
|
+
* Get comments on a file
|
|
286
|
+
*/
|
|
287
|
+
async getComments(fileKey, options) {
|
|
288
|
+
const params = new URLSearchParams();
|
|
289
|
+
if (options?.as_md)
|
|
290
|
+
params.set('as_md', 'true');
|
|
291
|
+
const query = params.toString() ? `?${params.toString()}` : '';
|
|
292
|
+
return this.request(`/files/${fileKey}/comments${query}`);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* POST /v1/files/:file_key/comments
|
|
296
|
+
* Post a comment on a file
|
|
297
|
+
*/
|
|
298
|
+
async postComment(fileKey, message, clientMeta, commentId) {
|
|
299
|
+
return this.request(`/files/${fileKey}/comments`, {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
body: JSON.stringify({
|
|
302
|
+
message,
|
|
303
|
+
...(clientMeta && { client_meta: clientMeta }),
|
|
304
|
+
...(commentId && { comment_id: commentId }),
|
|
305
|
+
}),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* DELETE /v1/files/:file_key/comments/:comment_id
|
|
310
|
+
* Delete a comment on a file
|
|
311
|
+
*/
|
|
312
|
+
async deleteComment(fileKey, commentId) {
|
|
313
|
+
return this.request(`/files/${fileKey}/comments/${commentId}`, {
|
|
314
|
+
method: 'DELETE',
|
|
315
|
+
});
|
|
316
|
+
}
|
|
283
317
|
/**
|
|
284
318
|
* Helper: Get all design tokens (variables) with formatted output
|
|
285
319
|
* Both local and published can fail gracefully (e.g., 403 without Enterprise plan)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Connector Interface
|
|
3
|
+
*
|
|
4
|
+
* Transport abstraction that both CDP (FigmaDesktopConnector) and WebSocket
|
|
5
|
+
* (WebSocketConnector) implement. Allows getDesktopConnector() to return
|
|
6
|
+
* whichever transport is available — WebSocket preferred, CDP fallback.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|