@mp3wizard/figma-console-mcp 1.19.2 → 1.20.2

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 CHANGED
@@ -54,9 +54,9 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
54
54
  | Real-time monitoring (console, selection) | ✅ | ❌ | ❌ |
55
55
  | Desktop Bridge plugin | ✅ | ✅ | ❌ |
56
56
  | Requires Node.js | Yes | **No** | No |
57
- | **Total tools available** | **89+** | **43** | **22** |
57
+ | **Total tools available** | **92+** | **43** | **22** |
58
58
 
59
- > **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 89+ tools with real-time monitoring.
59
+ > **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 92+ tools with real-time monitoring.
60
60
 
61
61
  ---
62
62
 
@@ -64,7 +64,7 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
64
64
 
65
65
  **Best for:** Designers who want full AI-assisted design capabilities.
66
66
 
67
- **What you get:** All 89+ tools including design creation, variable management, and component instantiation.
67
+ **What you get:** All 92+ tools including design creation, variable management, and component instantiation.
68
68
 
69
69
  #### Prerequisites
70
70
 
@@ -126,7 +126,7 @@ If you're not sure where to put the JSON configuration above, here's where each
126
126
  #### Step 3: Connect to Figma Desktop
127
127
 
128
128
  **Desktop Bridge Plugin:**
129
- 1. Open Figma Desktop normally (no special flags needed)
129
+ 1. Open Figma Desktop normally (no special flags needed) and open a file
130
130
  2. Go to **Plugins → Development → Import plugin from manifest...**
131
131
  3. Select `~/.figma-console-mcp/plugin/manifest.json` (stable path, auto-created by the MCP server)
132
132
  4. Run the plugin in your Figma file — the bootloader finds the MCP server and loads the latest UI automatically
@@ -159,7 +159,7 @@ Create a simple frame with a blue background
159
159
 
160
160
  **Best for:** Developers who want to modify source code or contribute to the project.
161
161
 
162
- **What you get:** Same 89+ tools as NPX, plus full source code access.
162
+ **What you get:** Same 92+ tools as NPX, plus full source code access.
163
163
 
164
164
  #### Quick Setup
165
165
 
@@ -248,7 +248,7 @@ Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide
248
248
 
249
249
  **Best for:** Using Claude.ai, v0, Replit, or Lovable to create and modify Figma designs — no Node.js required.
250
250
 
251
- **What you get:** 79 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.
251
+ **What you get:** 82 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.
252
252
 
253
253
  #### Prerequisites
254
254
 
@@ -305,7 +305,7 @@ AI Client → Cloud MCP Server → Durable Object Relay → Desktop Bridge Plugi
305
305
  | Feature | NPX (Recommended) | Cloud Mode | Local Git | Remote SSE |
306
306
  |---------|-------------------|------------|-----------|------------|
307
307
  | **Setup time** | ~10 minutes | ~5 minutes | ~15 minutes | ~2 minutes |
308
- | **Total tools** | **89+** | **43** | **89+** | **22** (read-only) |
308
+ | **Total tools** | **92+** | **43** | **92+** | **22** (read-only) |
309
309
  | **Design creation** | ✅ | ✅ | ✅ | ❌ |
310
310
  | **Variable management** | ✅ | ✅ | ✅ | ❌ |
311
311
  | **Component instantiation** | ✅ | ✅ | ✅ | ❌ |
@@ -320,7 +320,7 @@ AI Client → Cloud MCP Server → Durable Object Relay → Desktop Bridge Plugi
320
320
  | **Automatic updates** | ✅ (`@latest`) | ✅ | Manual (`git pull`) | ✅ |
321
321
  | **Source code access** | ❌ | ❌ | ✅ | ❌ |
322
322
 
323
- > **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 89+ tools.
323
+ > **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 92+ tools.
324
324
 
325
325
  **📖 [Complete Feature Comparison](docs/mode-comparison.md)**
326
326
 
@@ -456,8 +456,10 @@ When you first use design system tools:
456
456
  - `figma_reorder_slides` - Reorder slides via new 2D grid layout
457
457
  - `figma_set_slide_transition` - Set transition effects (22 styles, 8 curves)
458
458
  - `figma_skip_slide` - Toggle whether a slide is skipped in presentation mode
459
- - `figma_add_text_to_slide` - Add text to a specific slide
459
+ - `figma_add_text_to_slide` - Add text to a slide with custom fonts, colors, alignment, and wrapping
460
460
  - `figma_add_shape_to_slide` - Add rectangle or ellipse shapes with color
461
+ - `figma_set_slide_background` - Set a slide's background color (creates or updates)
462
+ - `figma_get_text_styles` - Get all local text styles with IDs, fonts, and sizes
461
463
  - `figma_set_slides_view_mode` - Toggle grid vs. single-slide view
462
464
  - `figma_focus_slide` - Navigate to a specific slide
463
465
 
@@ -652,7 +654,7 @@ The **Figma Desktop Bridge** plugin is the recommended way to connect Figma to t
652
654
  - The MCP server communicates via **WebSocket** through the Desktop Bridge plugin
653
655
  - The server tries port 9223 first, then automatically falls back through ports 9224–9232 if needed
654
656
  - The plugin scans all ports in the range and connects to every active server it finds
655
- - All 89+ tools work through the WebSocket transport
657
+ - All 92+ tools work through the WebSocket transport
656
658
 
657
659
  **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).
658
660
 
@@ -789,7 +791,7 @@ The architecture supports adding new apps with minimal boilerplate — each app
789
791
 
790
792
  ## 🛤️ Roadmap
791
793
 
792
- **Current Status:** v1.17.0 (Stable) - Production-ready with FigJam + Slides support, Cloud Write Relay, Design System Kit, WebSocket-only connectivity, smart multi-file tracking, 89+ tools, Comments API, and MCP Apps
794
+ **Current Status:** v1.17.0 (Stable) - Production-ready with FigJam + Slides support, Cloud Write Relay, Design System Kit, WebSocket-only connectivity, smart multi-file tracking, 92+ tools, Comments API, and MCP Apps
793
795
 
794
796
  **Recent Releases:**
795
797
  - [x] **v1.17.0** - Figma Slides Support: 15 new tools for managing presentations — slides, transitions, content, reordering, and navigation. Inspired by Toni Haidamous (PR #11).
@@ -276,6 +276,9 @@ export class CloudWebSocketConnector {
276
276
  async createShapeWithText(params) {
277
277
  return this.sendCommand('CREATE_SHAPE_WITH_TEXT', params);
278
278
  }
279
+ async createSection(params) {
280
+ return this.sendCommand('CREATE_SECTION', params);
281
+ }
279
282
  async createTable(params) {
280
283
  return this.sendCommand('CREATE_TABLE', params, 30000);
281
284
  }
@@ -336,6 +339,12 @@ export class CloudWebSocketConnector {
336
339
  async addShapeToSlide(params) {
337
340
  return this.sendCommand('ADD_SHAPE_TO_SLIDE', params, 5000);
338
341
  }
342
+ async setSlideBackground(params) {
343
+ return this.sendCommand('SET_SLIDE_BACKGROUND', params, 5000);
344
+ }
345
+ async getTextStyles() {
346
+ return this.sendCommand('GET_TEXT_STYLES', {}, 5000);
347
+ }
339
348
  // ============================================================================
340
349
  // Cache management (no-op for cloud relay)
341
350
  // ============================================================================
@@ -138,7 +138,9 @@ export function registerFigJamTools(server, getDesktopConnector) {
138
138
  // ============================================================================
139
139
  server.tool("figjam_create_connector", `Connect two nodes with a connector line in FigJam. Use to create flowcharts, diagrams, and relationship maps.
140
140
 
141
- Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from creation results.`, {
141
+ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from creation results.
142
+
143
+ **Magnet positions:** AUTO (default), TOP, BOTTOM, LEFT, RIGHT — controls where the connector attaches to each node.`, {
142
144
  startNodeId: z.string().describe("Node ID of the start element"),
143
145
  endNodeId: z.string().describe("Node ID of the end element"),
144
146
  label: z
@@ -146,13 +148,25 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
146
148
  .max(MAX_TEXT_LENGTH)
147
149
  .optional()
148
150
  .describe("Optional text label on the connector"),
149
- }, async ({ startNodeId, endNodeId, label }) => {
151
+ startMagnet: z
152
+ .enum(["AUTO", "TOP", "BOTTOM", "LEFT", "RIGHT"])
153
+ .optional()
154
+ .default("AUTO")
155
+ .describe("Magnet position on the start node"),
156
+ endMagnet: z
157
+ .enum(["AUTO", "TOP", "BOTTOM", "LEFT", "RIGHT"])
158
+ .optional()
159
+ .default("AUTO")
160
+ .describe("Magnet position on the end node"),
161
+ }, async ({ startNodeId, endNodeId, label, startMagnet, endMagnet }) => {
150
162
  try {
151
163
  const connector = await getDesktopConnector();
152
164
  const result = await connector.createConnector({
153
165
  startNodeId,
154
166
  endNodeId,
155
167
  label,
168
+ startMagnet,
169
+ endMagnet,
156
170
  });
157
171
  return {
158
172
  content: [{ type: "text", text: JSON.stringify(result) }],
@@ -177,7 +191,7 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
177
191
  // ============================================================================
178
192
  // SHAPE WITH TEXT TOOL
179
193
  // ============================================================================
180
- server.tool("figjam_create_shape_with_text", `Create a labeled shape on a FigJam board. Use for flowchart nodes, process diagrams, and visual organization.
194
+ server.tool("figjam_create_shape_with_text", `Create a labeled shape on a FigJam board with optional size, colors, and font control. Use for flowchart nodes, process diagrams, and visual organization.
181
195
 
182
196
  **Shape types:** ROUNDED_RECTANGLE (default), DIAMOND, ELLIPSE, TRIANGLE_UP, TRIANGLE_DOWN, PARALLELOGRAM_RIGHT, PARALLELOGRAM_LEFT, ENG_DATABASE, ENG_QUEUE, ENG_FILE, ENG_FOLDER`, {
183
197
  text: z
@@ -191,7 +205,13 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
191
205
  .describe("Shape type"),
192
206
  x: z.number().optional().describe("X position on canvas"),
193
207
  y: z.number().optional().describe("Y position on canvas"),
194
- }, async ({ text, shapeType, x, y }) => {
208
+ width: z.number().min(1).max(10000).optional().describe("Width in pixels"),
209
+ height: z.number().min(1).max(10000).optional().describe("Height in pixels"),
210
+ fillColor: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("Fill color as hex (e.g., '#E1F5EE')"),
211
+ strokeColor: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("Stroke/border color as hex"),
212
+ fontSize: z.number().min(1).max(200).optional().describe("Text font size in pixels"),
213
+ strokeDashPattern: z.string().optional().describe("Dash pattern as comma-separated numbers (e.g., '10,5' for dashed)"),
214
+ }, async ({ text, shapeType, x, y, width, height, fillColor, strokeColor, fontSize, strokeDashPattern }) => {
195
215
  try {
196
216
  const connector = await getDesktopConnector();
197
217
  const result = await connector.createShapeWithText({
@@ -199,6 +219,12 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
199
219
  shapeType,
200
220
  x,
201
221
  y,
222
+ width,
223
+ height,
224
+ fillColor,
225
+ strokeColor,
226
+ fontSize,
227
+ strokeDashPattern,
202
228
  });
203
229
  return {
204
230
  content: [{ type: "text", text: JSON.stringify(result) }],
@@ -221,6 +247,42 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
221
247
  }
222
248
  });
223
249
  // ============================================================================
250
+ // SECTION TOOL
251
+ // ============================================================================
252
+ server.tool("figjam_create_section", `Create a section on a FigJam board. Sections are containers that can hold other elements. Use for grouping related content.\n\n**Note:** After creating a section, place elements inside it by setting their x/y coordinates within the section's bounds, then use figma_execute to call section.appendChild(node) to parent them.`, {
253
+ name: z.string().max(500).optional().describe("Section name/title"),
254
+ x: z.number().optional().describe("X position on canvas"),
255
+ y: z.number().optional().describe("Y position on canvas"),
256
+ width: z.number().min(1).max(20000).optional().default(1000).describe("Section width in pixels"),
257
+ height: z.number().min(1).max(20000).optional().default(800).describe("Section height in pixels"),
258
+ fillColor: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("Fill color as hex"),
259
+ }, async ({ name, x, y, width, height, fillColor }) => {
260
+ try {
261
+ const connector = await getDesktopConnector();
262
+ const result = await connector.createSection({
263
+ name, x, y, width, height, fillColor,
264
+ });
265
+ return {
266
+ content: [{ type: "text", text: JSON.stringify(result) }],
267
+ };
268
+ }
269
+ catch (error) {
270
+ logger.error({ error }, "figjam_create_section failed");
271
+ return {
272
+ content: [
273
+ {
274
+ type: "text",
275
+ text: JSON.stringify({
276
+ error: error instanceof Error ? error.message : String(error),
277
+ hint: "This tool only works in FigJam files.",
278
+ }),
279
+ },
280
+ ],
281
+ isError: true,
282
+ };
283
+ }
284
+ });
285
+ // ============================================================================
224
286
  // TABLE TOOL
225
287
  // ============================================================================
226
288
  server.tool("figjam_create_table", `Create a table on a FigJam board with optional cell data. Use for structured data display, comparison matrices, and organized information.
@@ -1267,6 +1267,7 @@ export class FigmaDesktopConnector {
1267
1267
  async createStickies() { throw new Error('FigJam operations require WebSocket transport'); }
1268
1268
  async createConnector() { throw new Error('FigJam operations require WebSocket transport'); }
1269
1269
  async createShapeWithText() { throw new Error('FigJam operations require WebSocket transport'); }
1270
+ async createSection() { throw new Error('FigJam operations require WebSocket transport'); }
1270
1271
  async createTable() { throw new Error('FigJam operations require WebSocket transport'); }
1271
1272
  async createCodeBlock() { throw new Error('FigJam operations require WebSocket transport'); }
1272
1273
  async getBoardContents() { throw new Error('FigJam operations require WebSocket transport'); }
@@ -1287,5 +1288,7 @@ export class FigmaDesktopConnector {
1287
1288
  async skipSlide() { throw new Error('Slides operations require WebSocket transport'); }
1288
1289
  async addTextToSlide() { throw new Error('Slides operations require WebSocket transport'); }
1289
1290
  async addShapeToSlide() { throw new Error('Slides operations require WebSocket transport'); }
1291
+ async setSlideBackground() { throw new Error('Slides operations require WebSocket transport'); }
1292
+ async getTextStyles() { throw new Error('Text styles require WebSocket transport'); }
1290
1293
  }
1291
1294
  FigmaDesktopConnector.DEBUG = process.env.DEBUG === '1' || process.env.DEBUG === 'true';
@@ -184,6 +184,31 @@ export function registerSlidesTools(server, getDesktopConnector) {
184
184
  };
185
185
  }
186
186
  });
187
+ server.tool("figma_get_text_styles", `Get all local text styles in the current file. Returns style IDs, names, font info, and sizes. Use these IDs when setting textStyleId on text nodes via figma_execute.`, {}, async () => {
188
+ try {
189
+ const connector = await getDesktopConnector();
190
+ const result = await connector.getTextStyles();
191
+ return {
192
+ content: [
193
+ { type: "text", text: JSON.stringify(result) },
194
+ ],
195
+ };
196
+ }
197
+ catch (error) {
198
+ logger.error({ error }, "figma_get_text_styles failed");
199
+ return {
200
+ content: [
201
+ {
202
+ type: "text",
203
+ text: JSON.stringify({
204
+ error: error instanceof Error ? error.message : String(error),
205
+ }),
206
+ },
207
+ ],
208
+ isError: true,
209
+ };
210
+ }
211
+ });
187
212
  server.tool("figma_get_focused_slide", `Get the slide currently focused in single-slide view.`, {}, async () => {
188
213
  try {
189
214
  const connector = await getDesktopConnector();
@@ -430,7 +455,7 @@ export function registerSlidesTools(server, getDesktopConnector) {
430
455
  };
431
456
  }
432
457
  });
433
- server.tool("figma_add_text_to_slide", `Add a new text element to a specific slide. Uses Inter font by default.`, {
458
+ server.tool("figma_add_text_to_slide", `Add a new text element to a specific slide. Supports custom fonts, colors, alignment, and text formatting.`, {
434
459
  slideId: z
435
460
  .string()
436
461
  .max(50)
@@ -448,7 +473,46 @@ export function registerSlidesTools(server, getDesktopConnector) {
448
473
  .optional()
449
474
  .default(24)
450
475
  .describe("Font size in pixels"),
451
- }, async ({ slideId, text, x, y, fontSize }) => {
476
+ fontFamily: z
477
+ .string()
478
+ .optional()
479
+ .default("Inter")
480
+ .describe("Font family (e.g., 'Manrope', 'IBM Plex Sans')"),
481
+ fontStyle: z
482
+ .string()
483
+ .optional()
484
+ .default("Regular")
485
+ .describe("Font style/weight (e.g., 'Bold', 'SemiBold', 'Medium')"),
486
+ color: z
487
+ .string()
488
+ .regex(/^#[0-9a-fA-F]{6}$/)
489
+ .optional()
490
+ .describe("Text fill color as hex (e.g., '#FFFFFF')"),
491
+ textAlign: z
492
+ .enum(["LEFT", "CENTER", "RIGHT"])
493
+ .optional()
494
+ .describe("Horizontal text alignment"),
495
+ width: z
496
+ .number()
497
+ .min(1)
498
+ .max(MAX_DIMENSION)
499
+ .optional()
500
+ .describe("Text box width for wrapping. When set, enables text wrapping."),
501
+ lineHeight: z
502
+ .number()
503
+ .min(1)
504
+ .max(500)
505
+ .optional()
506
+ .describe("Line height in pixels"),
507
+ letterSpacing: z
508
+ .number()
509
+ .optional()
510
+ .describe("Letter spacing in pixels"),
511
+ textCase: z
512
+ .enum(["ORIGINAL", "UPPER", "LOWER", "TITLE"])
513
+ .optional()
514
+ .describe("Text case transformation"),
515
+ }, async ({ slideId, text, x, y, fontSize, fontFamily, fontStyle, color, textAlign, width, lineHeight, letterSpacing, textCase }) => {
452
516
  try {
453
517
  const connector = await getDesktopConnector();
454
518
  const result = await connector.addTextToSlide({
@@ -457,6 +521,14 @@ export function registerSlidesTools(server, getDesktopConnector) {
457
521
  x,
458
522
  y,
459
523
  fontSize,
524
+ fontFamily,
525
+ fontStyle,
526
+ color,
527
+ textAlign,
528
+ width,
529
+ lineHeight,
530
+ letterSpacing,
531
+ textCase,
460
532
  });
461
533
  return {
462
534
  content: [
@@ -540,6 +612,41 @@ export function registerSlidesTools(server, getDesktopConnector) {
540
612
  };
541
613
  }
542
614
  });
615
+ server.tool("figma_set_slide_background", `Set the background color of a slide. Creates or updates a full-slide background rectangle.`, {
616
+ slideId: z
617
+ .string()
618
+ .max(50)
619
+ .describe("The node ID of the slide"),
620
+ color: z
621
+ .string()
622
+ .regex(/^#[0-9a-fA-F]{6}$/, "Must be a 6-digit hex color like '#181818'")
623
+ .describe("Background color as hex (e.g., '#181818')"),
624
+ }, async ({ slideId, color }) => {
625
+ try {
626
+ const connector = await getDesktopConnector();
627
+ const result = await connector.setSlideBackground({ slideId, color });
628
+ return {
629
+ content: [
630
+ { type: "text", text: JSON.stringify(result) },
631
+ ],
632
+ };
633
+ }
634
+ catch (error) {
635
+ logger.error({ error }, "figma_set_slide_background failed");
636
+ return {
637
+ content: [
638
+ {
639
+ type: "text",
640
+ text: JSON.stringify({
641
+ error: error instanceof Error ? error.message : String(error),
642
+ hint: "This tool only works in Figma Slides files.",
643
+ }),
644
+ },
645
+ ],
646
+ isError: true,
647
+ };
648
+ }
649
+ });
543
650
  // ============================================================================
544
651
  // NAVIGATION TOOLS — Control slide view
545
652
  // ============================================================================
@@ -280,6 +280,9 @@ export class WebSocketConnector {
280
280
  async createShapeWithText(params) {
281
281
  return this.wsServer.sendCommand('CREATE_SHAPE_WITH_TEXT', params);
282
282
  }
283
+ async createSection(params) {
284
+ return this.wsServer.sendCommand('CREATE_SECTION', params);
285
+ }
283
286
  async createTable(params) {
284
287
  return this.wsServer.sendCommand('CREATE_TABLE', params, 30000);
285
288
  }
@@ -340,6 +343,12 @@ export class WebSocketConnector {
340
343
  async addShapeToSlide(params) {
341
344
  return this.wsServer.sendCommand('ADD_SHAPE_TO_SLIDE', params, 5000);
342
345
  }
346
+ async setSlideBackground(params) {
347
+ return this.wsServer.sendCommand('SET_SLIDE_BACKGROUND', params, 5000);
348
+ }
349
+ async getTextStyles() {
350
+ return this.wsServer.sendCommand('GET_TEXT_STYLES', {}, 5000);
351
+ }
343
352
  // ============================================================================
344
353
  // Cache management (no-op for WebSocket — no frame cache)
345
354
  // ============================================================================
@@ -68,7 +68,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
68
68
  super(...arguments);
69
69
  this.server = new McpServer({
70
70
  name: "Figma Console MCP",
71
- version: "1.19.1",
71
+ version: "1.20.1",
72
72
  });
73
73
  this.browserManager = null;
74
74
  this.consoleMonitor = null;
@@ -1046,7 +1046,7 @@ export default {
1046
1046
  });
1047
1047
  const statelessServer = new McpServer({
1048
1048
  name: "Figma Console MCP",
1049
- version: "1.19.1",
1049
+ version: "1.20.1",
1050
1050
  });
1051
1051
  // ================================================================
1052
1052
  // Cloud Write Relay — Pairing Tool (stateless /mcp path)
@@ -1683,7 +1683,7 @@ export default {
1683
1683
  return new Response(JSON.stringify({
1684
1684
  status: "healthy",
1685
1685
  service: "Figma Console MCP",
1686
- version: "1.19.1",
1686
+ version: "1.20.1",
1687
1687
  endpoints: {
1688
1688
  mcp: ["/sse", "/mcp"],
1689
1689
  oauth_mcp_spec: ["/.well-known/oauth-authorization-server", "/authorize", "/token", "/oauth/register"],
@@ -1729,13 +1729,13 @@ export default {
1729
1729
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1730
1730
  <title>Figma Console MCP - The Most Comprehensive MCP Server for Figma</title>
1731
1731
  <link rel="icon" type="image/svg+xml" href="https://docs.figma-console-mcp.southleft.com/favicon.svg">
1732
- <meta name="description" content="Turn your Figma design system into a living API. 89+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
1732
+ <meta name="description" content="Turn your Figma design system into a living API. 92+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
1733
1733
 
1734
1734
  <!-- Open Graph -->
1735
1735
  <meta property="og:type" content="website">
1736
1736
  <meta property="og:url" content="https://figma-console-mcp.southleft.com">
1737
1737
  <meta property="og:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
1738
- <meta property="og:description" content="The most comprehensive MCP server for Figma. 89+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1738
+ <meta property="og:description" content="The most comprehensive MCP server for Figma. 92+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1739
1739
  <meta property="og:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
1740
1740
  <meta property="og:image:width" content="1200">
1741
1741
  <meta property="og:image:height" content="630">
@@ -1743,7 +1743,7 @@ export default {
1743
1743
  <!-- Twitter -->
1744
1744
  <meta name="twitter:card" content="summary_large_image">
1745
1745
  <meta name="twitter:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
1746
- <meta name="twitter:description" content="The most comprehensive MCP server for Figma. 89+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1746
+ <meta name="twitter:description" content="The most comprehensive MCP server for Figma. 92+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
1747
1747
  <meta name="twitter:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
1748
1748
 
1749
1749
  <meta name="theme-color" content="#0D9488">
@@ -2630,7 +2630,7 @@ export default {
2630
2630
  <div class="grid-cell showcase-cell rule-left">
2631
2631
  <div class="showcase-label">What AI Can Access</div>
2632
2632
  <div class="showcase-stat">
2633
- <span class="number">87+</span>
2633
+ <span class="number">92+</span>
2634
2634
  <span class="label">MCP tools for Figma</span>
2635
2635
  </div>
2636
2636
  <div class="capability-list">
@@ -1 +1 @@
1
- {"version":3,"file":"figjam-tools.d.ts","sourceRoot":"","sources":["../../src/core/figjam-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4DzE;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,SAAS,EACjB,mBAAmB,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACrC,IAAI,CA2eN"}
1
+ {"version":3,"file":"figjam-tools.d.ts","sourceRoot":"","sources":["../../src/core/figjam-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4DzE;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,SAAS,EACjB,mBAAmB,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACrC,IAAI,CA+iBN"}
@@ -138,7 +138,9 @@ export function registerFigJamTools(server, getDesktopConnector) {
138
138
  // ============================================================================
139
139
  server.tool("figjam_create_connector", `Connect two nodes with a connector line in FigJam. Use to create flowcharts, diagrams, and relationship maps.
140
140
 
141
- Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from creation results.`, {
141
+ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from creation results.
142
+
143
+ **Magnet positions:** AUTO (default), TOP, BOTTOM, LEFT, RIGHT — controls where the connector attaches to each node.`, {
142
144
  startNodeId: z.string().describe("Node ID of the start element"),
143
145
  endNodeId: z.string().describe("Node ID of the end element"),
144
146
  label: z
@@ -146,13 +148,25 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
146
148
  .max(MAX_TEXT_LENGTH)
147
149
  .optional()
148
150
  .describe("Optional text label on the connector"),
149
- }, async ({ startNodeId, endNodeId, label }) => {
151
+ startMagnet: z
152
+ .enum(["AUTO", "TOP", "BOTTOM", "LEFT", "RIGHT"])
153
+ .optional()
154
+ .default("AUTO")
155
+ .describe("Magnet position on the start node"),
156
+ endMagnet: z
157
+ .enum(["AUTO", "TOP", "BOTTOM", "LEFT", "RIGHT"])
158
+ .optional()
159
+ .default("AUTO")
160
+ .describe("Magnet position on the end node"),
161
+ }, async ({ startNodeId, endNodeId, label, startMagnet, endMagnet }) => {
150
162
  try {
151
163
  const connector = await getDesktopConnector();
152
164
  const result = await connector.createConnector({
153
165
  startNodeId,
154
166
  endNodeId,
155
167
  label,
168
+ startMagnet,
169
+ endMagnet,
156
170
  });
157
171
  return {
158
172
  content: [{ type: "text", text: JSON.stringify(result) }],
@@ -177,7 +191,7 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
177
191
  // ============================================================================
178
192
  // SHAPE WITH TEXT TOOL
179
193
  // ============================================================================
180
- server.tool("figjam_create_shape_with_text", `Create a labeled shape on a FigJam board. Use for flowchart nodes, process diagrams, and visual organization.
194
+ server.tool("figjam_create_shape_with_text", `Create a labeled shape on a FigJam board with optional size, colors, and font control. Use for flowchart nodes, process diagrams, and visual organization.
181
195
 
182
196
  **Shape types:** ROUNDED_RECTANGLE (default), DIAMOND, ELLIPSE, TRIANGLE_UP, TRIANGLE_DOWN, PARALLELOGRAM_RIGHT, PARALLELOGRAM_LEFT, ENG_DATABASE, ENG_QUEUE, ENG_FILE, ENG_FOLDER`, {
183
197
  text: z
@@ -191,7 +205,13 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
191
205
  .describe("Shape type"),
192
206
  x: z.number().optional().describe("X position on canvas"),
193
207
  y: z.number().optional().describe("Y position on canvas"),
194
- }, async ({ text, shapeType, x, y }) => {
208
+ width: z.number().min(1).max(10000).optional().describe("Width in pixels"),
209
+ height: z.number().min(1).max(10000).optional().describe("Height in pixels"),
210
+ fillColor: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("Fill color as hex (e.g., '#E1F5EE')"),
211
+ strokeColor: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("Stroke/border color as hex"),
212
+ fontSize: z.number().min(1).max(200).optional().describe("Text font size in pixels"),
213
+ strokeDashPattern: z.string().optional().describe("Dash pattern as comma-separated numbers (e.g., '10,5' for dashed)"),
214
+ }, async ({ text, shapeType, x, y, width, height, fillColor, strokeColor, fontSize, strokeDashPattern }) => {
195
215
  try {
196
216
  const connector = await getDesktopConnector();
197
217
  const result = await connector.createShapeWithText({
@@ -199,6 +219,12 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
199
219
  shapeType,
200
220
  x,
201
221
  y,
222
+ width,
223
+ height,
224
+ fillColor,
225
+ strokeColor,
226
+ fontSize,
227
+ strokeDashPattern,
202
228
  });
203
229
  return {
204
230
  content: [{ type: "text", text: JSON.stringify(result) }],
@@ -221,6 +247,42 @@ Nodes must exist on the board (stickies, shapes, etc.). Use their node IDs from
221
247
  }
222
248
  });
223
249
  // ============================================================================
250
+ // SECTION TOOL
251
+ // ============================================================================
252
+ server.tool("figjam_create_section", `Create a section on a FigJam board. Sections are containers that can hold other elements. Use for grouping related content.\n\n**Note:** After creating a section, place elements inside it by setting their x/y coordinates within the section's bounds, then use figma_execute to call section.appendChild(node) to parent them.`, {
253
+ name: z.string().max(500).optional().describe("Section name/title"),
254
+ x: z.number().optional().describe("X position on canvas"),
255
+ y: z.number().optional().describe("Y position on canvas"),
256
+ width: z.number().min(1).max(20000).optional().default(1000).describe("Section width in pixels"),
257
+ height: z.number().min(1).max(20000).optional().default(800).describe("Section height in pixels"),
258
+ fillColor: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("Fill color as hex"),
259
+ }, async ({ name, x, y, width, height, fillColor }) => {
260
+ try {
261
+ const connector = await getDesktopConnector();
262
+ const result = await connector.createSection({
263
+ name, x, y, width, height, fillColor,
264
+ });
265
+ return {
266
+ content: [{ type: "text", text: JSON.stringify(result) }],
267
+ };
268
+ }
269
+ catch (error) {
270
+ logger.error({ error }, "figjam_create_section failed");
271
+ return {
272
+ content: [
273
+ {
274
+ type: "text",
275
+ text: JSON.stringify({
276
+ error: error instanceof Error ? error.message : String(error),
277
+ hint: "This tool only works in FigJam files.",
278
+ }),
279
+ },
280
+ ],
281
+ isError: true,
282
+ };
283
+ }
284
+ });
285
+ // ============================================================================
224
286
  // TABLE TOOL
225
287
  // ============================================================================
226
288
  server.tool("figjam_create_table", `Create a table on a FigJam board with optional cell data. Use for structured data display, comparison matrices, and organized information.