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.
Files changed (49) hide show
  1. package/README.md +81 -52
  2. package/dist/cloudflare/core/comment-tools.js +292 -0
  3. package/dist/cloudflare/core/design-code-tools.js +2 -2
  4. package/dist/cloudflare/core/figma-api.js +34 -0
  5. package/dist/cloudflare/core/figma-connector.js +8 -0
  6. package/dist/cloudflare/core/figma-desktop-connector.js +69 -2
  7. package/dist/cloudflare/core/figma-tools.js +118 -98
  8. package/dist/cloudflare/core/websocket-connector.js +235 -0
  9. package/dist/cloudflare/core/websocket-server.js +603 -0
  10. package/dist/cloudflare/index.js +9 -6
  11. package/dist/core/comment-tools.d.ts +11 -0
  12. package/dist/core/comment-tools.d.ts.map +1 -0
  13. package/dist/core/comment-tools.js +293 -0
  14. package/dist/core/comment-tools.js.map +1 -0
  15. package/dist/core/design-code-tools.js +2 -2
  16. package/dist/core/design-code-tools.js.map +1 -1
  17. package/dist/core/figma-api.d.ts +23 -0
  18. package/dist/core/figma-api.d.ts.map +1 -1
  19. package/dist/core/figma-api.js +34 -0
  20. package/dist/core/figma-api.js.map +1 -1
  21. package/dist/core/figma-connector.d.ts +47 -0
  22. package/dist/core/figma-connector.d.ts.map +1 -0
  23. package/dist/core/figma-connector.js +9 -0
  24. package/dist/core/figma-connector.js.map +1 -0
  25. package/dist/core/figma-desktop-connector.d.ts +14 -1
  26. package/dist/core/figma-desktop-connector.d.ts.map +1 -1
  27. package/dist/core/figma-desktop-connector.js +69 -2
  28. package/dist/core/figma-desktop-connector.js.map +1 -1
  29. package/dist/core/figma-tools.d.ts +1 -1
  30. package/dist/core/figma-tools.d.ts.map +1 -1
  31. package/dist/core/figma-tools.js +118 -98
  32. package/dist/core/figma-tools.js.map +1 -1
  33. package/dist/core/websocket-connector.d.ts +53 -0
  34. package/dist/core/websocket-connector.d.ts.map +1 -0
  35. package/dist/core/websocket-connector.js +236 -0
  36. package/dist/core/websocket-connector.js.map +1 -0
  37. package/dist/core/websocket-server.d.ts +183 -0
  38. package/dist/core/websocket-server.d.ts.map +1 -0
  39. package/dist/core/websocket-server.js +604 -0
  40. package/dist/core/websocket-server.js.map +1 -0
  41. package/dist/local.d.ts +11 -3
  42. package/dist/local.d.ts.map +1 -1
  43. package/dist/local.js +847 -276
  44. package/dist/local.js.map +1 -1
  45. package/figma-desktop-bridge/README.md +30 -16
  46. package/figma-desktop-bridge/code.js +195 -3
  47. package/figma-desktop-bridge/manifest.json +4 -1
  48. package/figma-desktop-bridge/ui.html +229 -0
  49. 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** | **72+** | **16** |
45
+ | **Total tools available** | **56+** | **16** |
46
46
 
47
- > **Bottom line:** Remote SSE is **read-only**. If you want AI to actually design in Figma, use NPX Setup.
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 72+ tools including design creation, variable management, and component instantiation.
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
- - [ ] **Claude Desktop** or another MCP client
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 Claude Desktop
70
+ #### Step 2: Configure Your MCP Client
71
71
 
72
- Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
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: Start Figma with Debug Mode
95
+ #### Step 3: Connect to Figma Desktop
89
96
 
90
- **Quit Figma completely**, then restart with debug flag:
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
- **macOS:**
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
- **Windows:**
98
- ```
99
- cmd /c "%LOCALAPPDATA%\Figma\Figma.exe" --remote-debugging-port=9222
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 Claude Desktop
113
+ #### Step 4: Restart Your MCP Client
105
114
 
106
- Quit and reopen Claude Desktop. Look for 🔌 icon showing "figma-console: connected".
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 "✅ Figma Desktop connected"
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 72+ tools as NPX, plus full source code access.
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 Claude Desktop
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-start-figma-with-debug-mode) above.
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:** 16 read-only tools — view data, take screenshots, read logs. **Cannot create or modify designs.**
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** | **72+** | **72+** | **16** (read-only) |
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 ~22% of the tools. Use NPX for full capabilities.
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 [Local Mode installation](#for-plugin-developers-local-mode). Remote Mode is read-only and cannot create or modify designs.
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 (Local Mode Only)
470
+ ## 🎨 Desktop Bridge Plugin (Recommended Connection)
462
471
 
463
- The **Figma Desktop Bridge** plugin enables powerful capabilities:
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
- ### Read Operations
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
- ### Write Operations
471
- - **Design Creation** - Create frames, shapes, text, components via `figma_execute`
472
- - **Variable Management** - Full CRUD operations on variables and collections
473
- - **Mode Management** - Add and rename modes for multi-theme support
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
- **⚠️ Plugin Limitation:** Only works in Local Mode. Remote mode cannot access it.
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.2.x (Stable) - Production-ready with comprehensive capabilities
613
+ **Current Status:** v1.9.0 (Stable) - Production-ready with WebSocket Bridge, 56+ tools, Comments API, and MCP Apps
585
614
 
586
- **Coming Soon:**
587
- - [ ] **Enhanced error messages** - Actionable suggestions for design operations
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 provided. Either pass fileUrl parameter or call figma_navigate first.");
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 provided. Either pass fileUrl parameter or call figma_navigate first.");
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 {};