@atezer/figma-mcp-bridge 1.1.2 → 1.2.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/dist/local.js CHANGED
@@ -48,7 +48,7 @@ class LocalFigmaMCP {
48
48
  this.variablesCache = new Map();
49
49
  this.server = new McpServer({
50
50
  name: "F-MCP ATezer (Local)",
51
- version: "0.1.0",
51
+ version: "1.1.2",
52
52
  });
53
53
  }
54
54
  /**
@@ -263,17 +263,21 @@ class LocalFigmaMCP {
263
263
  */
264
264
  registerTools() {
265
265
  // Tool 1: Get Console Logs
266
- this.server.tool("figma_get_console_logs", "Retrieve console logs from Figma Desktop. FOR PLUGIN DEVELOPERS: This works immediately - no navigation needed! Just check logs, run your plugin in Figma Desktop, check logs again. All plugin logs ([Main], [Swapper], etc.) appear instantly.", {
267
- count: z.number().optional().default(100).describe("Number of recent logs to retrieve"),
268
- level: z
269
- .enum(["log", "info", "warn", "error", "debug", "all"])
270
- .optional()
271
- .default("all")
272
- .describe("Filter by log level"),
273
- since: z
274
- .number()
275
- .optional()
276
- .describe("Only logs after this timestamp (Unix ms)"),
266
+ this.server.registerTool("figma_get_console_logs", {
267
+ description: "Retrieve console logs from Figma Desktop. FOR PLUGIN DEVELOPERS: This works immediately - no navigation needed! Just check logs, run your plugin in Figma Desktop, check logs again. All plugin logs ([Main], [Swapper], etc.) appear instantly.",
268
+ inputSchema: {
269
+ count: z.number().optional().default(100).describe("Number of recent logs to retrieve"),
270
+ level: z
271
+ .enum(["log", "info", "warn", "error", "debug", "all"])
272
+ .optional()
273
+ .default("all")
274
+ .describe("Filter by log level"),
275
+ since: z
276
+ .number()
277
+ .optional()
278
+ .describe("Only logs after this timestamp (Unix ms)"),
279
+ },
280
+ annotations: { readOnlyHint: true },
277
281
  }, async ({ count, level, since }) => {
278
282
  try {
279
283
  await this.ensureInitialized();
@@ -355,7 +359,8 @@ class LocalFigmaMCP {
355
359
  });
356
360
  // Tool 2: Take Screenshot (using Figma REST API)
357
361
  // Note: For screenshots of specific components, use figma_get_component_image instead
358
- this.server.tool("figma_take_screenshot", `Export an image of the currently viewed Figma page or specific node using Figma's REST API. Returns an image URL (valid for 30 days). For specific components, use figma_get_component_image instead.
362
+ this.server.registerTool("figma_take_screenshot", {
363
+ description: `Export an image of the currently viewed Figma page or specific node using Figma's REST API. Returns an image URL (valid for 30 days). For specific components, use figma_get_component_image instead.
359
364
 
360
365
  **CRITICAL: Use this tool for visual validation after ANY design creation or modification.**
361
366
  This is an essential part of the visual validation workflow:
@@ -365,23 +370,26 @@ This is an essential part of the visual validation workflow:
365
370
  4. If issues are found, iterate with fixes and take another screenshot
366
371
  5. Continue until the design looks correct (max 3 iterations)
367
372
 
368
- Pass a nodeId to screenshot specific frames/elements, or omit to capture the current view.`, {
369
- nodeId: z
370
- .string()
371
- .optional()
372
- .describe("Optional node ID to screenshot. If not provided, uses the currently viewed page/frame from the browser URL."),
373
- scale: z
374
- .number()
375
- .min(0.01)
376
- .max(4)
377
- .optional()
378
- .default(2)
379
- .describe("Image scale factor (0.01-4, default: 2 for high quality)"),
380
- format: z
381
- .enum(["png", "jpg", "svg", "pdf"])
382
- .optional()
383
- .default("png")
384
- .describe("Image format (default: png)"),
373
+ Pass a nodeId to screenshot specific frames/elements, or omit to capture the current view.`,
374
+ inputSchema: {
375
+ nodeId: z
376
+ .string()
377
+ .optional()
378
+ .describe("Optional node ID to screenshot. If not provided, uses the currently viewed page/frame from the browser URL."),
379
+ scale: z
380
+ .number()
381
+ .min(0.01)
382
+ .max(4)
383
+ .optional()
384
+ .default(2)
385
+ .describe("Image scale factor (0.01-4, default: 2 for high quality)"),
386
+ format: z
387
+ .enum(["png", "jpg", "svg", "pdf"])
388
+ .optional()
389
+ .default("png")
390
+ .describe("Image format (default: png)"),
391
+ },
392
+ annotations: { readOnlyHint: true },
385
393
  }, async ({ nodeId, scale, format }) => {
386
394
  try {
387
395
  const api = await this.getFigmaAPI();
@@ -454,17 +462,21 @@ Pass a nodeId to screenshot specific frames/elements, or omit to capture the cur
454
462
  }
455
463
  });
456
464
  // Tool 3: Watch Console (Real-time streaming)
457
- this.server.tool("figma_watch_console", "Stream console logs in real-time for a specified duration (max 5 minutes). Use for monitoring plugin execution while user tests manually. Returns all logs captured during watch period with summary statistics. NOT for retrieving past logs (use figma_get_console_logs). Best for: watching plugin output during manual testing, debugging race conditions, monitoring async operations.", {
458
- duration: z
459
- .number()
460
- .optional()
461
- .default(30)
462
- .describe("How long to watch in seconds"),
463
- level: z
464
- .enum(["log", "info", "warn", "error", "debug", "all"])
465
- .optional()
466
- .default("all")
467
- .describe("Filter by log level"),
465
+ this.server.registerTool("figma_watch_console", {
466
+ description: "Stream console logs in real-time for a specified duration (max 5 minutes). Use for monitoring plugin execution while user tests manually. Returns all logs captured during watch period with summary statistics. NOT for retrieving past logs (use figma_get_console_logs). Best for: watching plugin output during manual testing, debugging race conditions, monitoring async operations.",
467
+ inputSchema: {
468
+ duration: z
469
+ .number()
470
+ .optional()
471
+ .default(30)
472
+ .describe("How long to watch in seconds"),
473
+ level: z
474
+ .enum(["log", "info", "warn", "error", "debug", "all"])
475
+ .optional()
476
+ .default("all")
477
+ .describe("Filter by log level"),
478
+ },
479
+ annotations: { readOnlyHint: true },
468
480
  }, async ({ duration, level }) => {
469
481
  if (!this.browserManager || !this.consoleMonitor) {
470
482
  throw new Error("Browser not connected. Ensure Figma Desktop is running with --remote-debugging-port=9222");
@@ -507,12 +519,16 @@ Pass a nodeId to screenshot specific frames/elements, or omit to capture the cur
507
519
  };
508
520
  });
509
521
  // Tool 4: Reload Plugin
510
- this.server.tool("figma_reload_plugin", "Reload the current Figma page/plugin to test code changes. Optionally clears console logs before reload. Use when user says: 'reload plugin', 'refresh page', 'restart plugin', 'test my changes'. Returns reload confirmation and current URL. Best for rapid iteration during plugin development.", {
511
- clearConsole: z
512
- .boolean()
513
- .optional()
514
- .default(true)
515
- .describe("Clear console logs before reload"),
522
+ this.server.registerTool("figma_reload_plugin", {
523
+ description: "Reload the current Figma page/plugin to test code changes. Optionally clears console logs before reload. Use when user says: 'reload plugin', 'refresh page', 'restart plugin', 'test my changes'. Returns reload confirmation and current URL. Best for rapid iteration during plugin development.",
524
+ inputSchema: {
525
+ clearConsole: z
526
+ .boolean()
527
+ .optional()
528
+ .default(true)
529
+ .describe("Clear console logs before reload"),
530
+ },
531
+ annotations: { destructiveHint: true },
516
532
  }, async ({ clearConsole: clearConsoleBefore }) => {
517
533
  try {
518
534
  await this.ensureInitialized();
@@ -574,7 +590,11 @@ Pass a nodeId to screenshot specific frames/elements, or omit to capture the cur
574
590
  }
575
591
  });
576
592
  // Tool 5: Clear Console
577
- this.server.tool("figma_clear_console", "Clear the console log buffer. ⚠️ WARNING: Disrupts monitoring connection - requires MCP reconnect afterward. AVOID using this - prefer filtering logs with figma_get_console_logs instead. Only use if user explicitly requests clearing logs. Returns number of logs cleared.", {}, async () => {
593
+ this.server.registerTool("figma_clear_console", {
594
+ description: "Clear the console log buffer. ⚠️ WARNING: Disrupts monitoring connection - requires MCP reconnect afterward. AVOID using this - prefer filtering logs with figma_get_console_logs instead. Only use if user explicitly requests clearing logs. Returns number of logs cleared.",
595
+ inputSchema: {},
596
+ annotations: { destructiveHint: true },
597
+ }, async () => {
578
598
  try {
579
599
  await this.ensureInitialized();
580
600
  if (!this.consoleMonitor) {
@@ -612,11 +632,15 @@ Pass a nodeId to screenshot specific frames/elements, or omit to capture the cur
612
632
  }
613
633
  });
614
634
  // Tool 6: Navigate to Figma
615
- this.server.tool("figma_navigate", "Navigate browser to a Figma URL and start console monitoring. ALWAYS use this first when starting a new debugging session or switching files. Initializes browser connection and begins capturing console logs. Use when user provides a Figma URL or says: 'open this file', 'debug this design', 'switch to'. Returns navigation status and current URL.", {
616
- url: z
617
- .string()
618
- .url()
619
- .describe("Figma URL to navigate to (e.g., https://www.figma.com/design/abc123)"),
635
+ this.server.registerTool("figma_navigate", {
636
+ description: "Navigate browser to a Figma URL and start console monitoring. ALWAYS use this first when starting a new debugging session or switching files. Initializes browser connection and begins capturing console logs. Use when user provides a Figma URL or says: 'open this file', 'debug this design', 'switch to'. Returns navigation status and current URL.",
637
+ inputSchema: {
638
+ url: z
639
+ .string()
640
+ .url()
641
+ .describe("Figma URL to navigate to (e.g., https://www.figma.com/design/abc123)"),
642
+ },
643
+ annotations: { destructiveHint: true },
620
644
  }, async ({ url }) => {
621
645
  try {
622
646
  await this.ensureInitialized();
@@ -665,7 +689,11 @@ Pass a nodeId to screenshot specific frames/elements, or omit to capture the cur
665
689
  }
666
690
  });
667
691
  // Tool 7: Get Status (with setup validation)
668
- this.server.tool("figma_get_status", "Check browser and monitoring status. Also validates if Figma Desktop is running with the required --remote-debugging-port=9222 flag. Automatically initializes connection if needed.", {}, async () => {
692
+ this.server.registerTool("figma_get_status", {
693
+ description: "Check browser and monitoring status. Also validates if Figma Desktop is running with the required --remote-debugging-port=9222 flag. Automatically initializes connection if needed.",
694
+ inputSchema: {},
695
+ annotations: { readOnlyHint: true },
696
+ }, async () => {
669
697
  try {
670
698
  // Ensure initialized (connects to Figma Desktop if not already connected)
671
699
  await this.ensureInitialized();
@@ -784,7 +812,11 @@ Pass a nodeId to screenshot specific frames/elements, or omit to capture the cur
784
812
  // CONNECTION MANAGEMENT TOOLS
785
813
  // ============================================================================
786
814
  // Tool: Force reconnect to Figma Desktop
787
- this.server.tool("figma_reconnect", "Force a complete reconnection to Figma Desktop. Use this when you get 'detached Frame' errors or when the connection seems stale. This will disconnect and reconnect to Figma, getting fresh page and frame references.", {}, async () => {
815
+ this.server.registerTool("figma_reconnect", {
816
+ description: "Force a complete reconnection to Figma Desktop. Use this when you get 'detached Frame' errors or when the connection seems stale. This will disconnect and reconnect to Figma, getting fresh page and frame references.",
817
+ inputSchema: {},
818
+ annotations: { destructiveHint: true },
819
+ }, async () => {
788
820
  try {
789
821
  if (!this.browserManager) {
790
822
  throw new Error("Browser manager not initialized. Run any tool first to initialize.");
@@ -851,7 +883,8 @@ Pass a nodeId to screenshot specific frames/elements, or omit to capture the cur
851
883
  // WRITE OPERATION TOOLS - Figma Design Manipulation
852
884
  // ============================================================================
853
885
  // Tool: Execute arbitrary code in Figma plugin context (Power Tool)
854
- this.server.tool("figma_execute", `Execute arbitrary JavaScript code in Figma's plugin context. This is a POWER TOOL that can run any Figma Plugin API code. Use for complex operations not covered by other tools. Requires the F-MCP ATezer Bridge plugin to be running in Figma. Returns the result of the code execution. CAUTION: Can modify your Figma document - use carefully.
886
+ this.server.registerTool("figma_execute", {
887
+ description: `Execute arbitrary JavaScript code in Figma's plugin context. This is a POWER TOOL that can run any Figma Plugin API code. Use for complex operations not covered by other tools. Requires the F-MCP ATezer Bridge plugin to be running in Figma. Returns the result of the code execution. CAUTION: Can modify your Figma document - use carefully.
855
888
 
856
889
  **IMPORTANT: COMPONENT INSTANCES vs DIRECT NODE EDITING**
857
890
  When working with component instances (node.type === 'INSTANCE'), you must use the correct approach:
@@ -885,10 +918,13 @@ Common issues to check:
885
918
  - Inconsistent padding (elements not visually balanced)
886
919
  - Text/inputs not filling available width
887
920
  - Component text not changing (use figma_set_instance_properties instead)
888
- - Duplicate pages created (check before creating new pages)`, {
889
- code: z.string().describe("JavaScript code to execute. Has access to the 'figma' global object. " +
890
- "Example: 'const rect = figma.createRectangle(); rect.resize(100, 100); return { id: rect.id };'"),
891
- timeout: z.number().optional().default(5000).describe("Execution timeout in milliseconds (default: 5000, max: 30000)"),
921
+ - Duplicate pages created (check before creating new pages)`,
922
+ inputSchema: {
923
+ code: z.string().describe("JavaScript code to execute. Has access to the 'figma' global object. " +
924
+ "Example: 'const rect = figma.createRectangle(); rect.resize(100, 100); return { id: rect.id };'"),
925
+ timeout: z.number().optional().default(5000).describe("Execution timeout in milliseconds (default: 5000, max: 30000)"),
926
+ },
927
+ annotations: { destructiveHint: true },
892
928
  }, async ({ code, timeout }) => {
893
929
  const maxRetries = 2;
894
930
  let lastError = null;
@@ -963,10 +999,14 @@ Common issues to check:
963
999
  };
964
1000
  });
965
1001
  // Tool: Update a variable's value
966
- this.server.tool("figma_update_variable", "Update a Figma variable's value in a specific mode. Use figma_get_variables first to get variable IDs and mode IDs. Supports COLOR (hex string like '#FF0000'), FLOAT (number), STRING (text), and BOOLEAN values. Requires the F-MCP ATezer Bridge plugin to be running.", {
967
- variableId: z.string().describe("The variable ID to update (e.g., 'VariableID:123:456'). Get this from figma_get_variables."),
968
- modeId: z.string().describe("The mode ID to update the value in (e.g., '1:0'). Get this from the variable's collection modes."),
969
- value: z.any().describe("The new value. For COLOR: hex string like '#FF0000'. For FLOAT: number. For STRING: text. For BOOLEAN: true/false."),
1002
+ this.server.registerTool("figma_update_variable", {
1003
+ description: "Update a Figma variable's value in a specific mode. Use figma_get_variables first to get variable IDs and mode IDs. Supports COLOR (hex string like '#FF0000'), FLOAT (number), STRING (text), and BOOLEAN values. Requires the F-MCP ATezer Bridge plugin to be running.",
1004
+ inputSchema: {
1005
+ variableId: z.string().describe("The variable ID to update (e.g., 'VariableID:123:456'). Get this from figma_get_variables."),
1006
+ modeId: z.string().describe("The mode ID to update the value in (e.g., '1:0'). Get this from the variable's collection modes."),
1007
+ value: z.any().describe("The new value. For COLOR: hex string like '#FF0000'. For FLOAT: number. For STRING: text. For BOOLEAN: true/false."),
1008
+ },
1009
+ annotations: { destructiveHint: true },
970
1010
  }, async ({ variableId, modeId, value }) => {
971
1011
  try {
972
1012
  const connector = await this.getDesktopConnector();
@@ -1003,12 +1043,16 @@ Common issues to check:
1003
1043
  }
1004
1044
  });
1005
1045
  // Tool: Create a new variable
1006
- this.server.tool("figma_create_variable", "Create a new Figma variable in an existing collection. Use figma_get_variables first to get collection IDs. Supports COLOR, FLOAT, STRING, and BOOLEAN types. Requires the F-MCP ATezer Bridge plugin to be running.", {
1007
- name: z.string().describe("Name for the new variable (e.g., 'primary-blue')"),
1008
- collectionId: z.string().describe("The collection ID to create the variable in (e.g., 'VariableCollectionId:123:456'). Get this from figma_get_variables."),
1009
- resolvedType: z.enum(["COLOR", "FLOAT", "STRING", "BOOLEAN"]).describe("The variable type: COLOR, FLOAT, STRING, or BOOLEAN"),
1010
- description: z.string().optional().describe("Optional description for the variable"),
1011
- valuesByMode: z.record(z.any()).optional().describe("Optional initial values by mode ID. Example: { '1:0': '#FF0000', '1:1': '#0000FF' }"),
1046
+ this.server.registerTool("figma_create_variable", {
1047
+ description: "Create a new Figma variable in an existing collection. Use figma_get_variables first to get collection IDs. Supports COLOR, FLOAT, STRING, and BOOLEAN types. Requires the F-MCP ATezer Bridge plugin to be running.",
1048
+ inputSchema: {
1049
+ name: z.string().describe("Name for the new variable (e.g., 'primary-blue')"),
1050
+ collectionId: z.string().describe("The collection ID to create the variable in (e.g., 'VariableCollectionId:123:456'). Get this from figma_get_variables."),
1051
+ resolvedType: z.enum(["COLOR", "FLOAT", "STRING", "BOOLEAN"]).describe("The variable type: COLOR, FLOAT, STRING, or BOOLEAN"),
1052
+ description: z.string().optional().describe("Optional description for the variable"),
1053
+ valuesByMode: z.record(z.any()).optional().describe("Optional initial values by mode ID. Example: { '1:0': '#FF0000', '1:1': '#0000FF' }"),
1054
+ },
1055
+ annotations: { destructiveHint: true },
1012
1056
  }, async ({ name, collectionId, resolvedType, description, valuesByMode }) => {
1013
1057
  try {
1014
1058
  const connector = await this.getDesktopConnector();
@@ -1048,10 +1092,14 @@ Common issues to check:
1048
1092
  }
1049
1093
  });
1050
1094
  // Tool: Create a new variable collection
1051
- this.server.tool("figma_create_variable_collection", "Create a new Figma variable collection. Collections organize variables and define modes (like Light/Dark themes). Requires the F-MCP ATezer Bridge plugin to be running.", {
1052
- name: z.string().describe("Name for the new collection (e.g., 'Brand Colors')"),
1053
- initialModeName: z.string().optional().describe("Name for the initial mode (default mode is created automatically). Example: 'Light'"),
1054
- additionalModes: z.array(z.string()).optional().describe("Additional mode names to create. Example: ['Dark', 'High Contrast']"),
1095
+ this.server.registerTool("figma_create_variable_collection", {
1096
+ description: "Create a new Figma variable collection. Collections organize variables and define modes (like Light/Dark themes). Requires the F-MCP ATezer Bridge plugin to be running.",
1097
+ inputSchema: {
1098
+ name: z.string().describe("Name for the new collection (e.g., 'Brand Colors')"),
1099
+ initialModeName: z.string().optional().describe("Name for the initial mode (default mode is created automatically). Example: 'Light'"),
1100
+ additionalModes: z.array(z.string()).optional().describe("Additional mode names to create. Example: ['Dark', 'High Contrast']"),
1101
+ },
1102
+ annotations: { destructiveHint: true },
1055
1103
  }, async ({ name, initialModeName, additionalModes }) => {
1056
1104
  try {
1057
1105
  const connector = await this.getDesktopConnector();
@@ -1091,8 +1139,12 @@ Common issues to check:
1091
1139
  }
1092
1140
  });
1093
1141
  // Tool: Delete a variable
1094
- this.server.tool("figma_delete_variable", "Delete a Figma variable. WARNING: This is a destructive operation that cannot be undone (except with Figma's undo). Use figma_get_variables first to get variable IDs. Requires the F-MCP ATezer Bridge plugin to be running.", {
1095
- variableId: z.string().describe("The variable ID to delete (e.g., 'VariableID:123:456'). Get this from figma_get_variables."),
1142
+ this.server.registerTool("figma_delete_variable", {
1143
+ description: "Delete a Figma variable. WARNING: This is a destructive operation that cannot be undone (except with Figma's undo). Use figma_get_variables first to get variable IDs. Requires the F-MCP ATezer Bridge plugin to be running.",
1144
+ inputSchema: {
1145
+ variableId: z.string().describe("The variable ID to delete (e.g., 'VariableID:123:456'). Get this from figma_get_variables."),
1146
+ },
1147
+ annotations: { destructiveHint: true },
1096
1148
  }, async ({ variableId }) => {
1097
1149
  try {
1098
1150
  const connector = await this.getDesktopConnector();
@@ -1130,8 +1182,12 @@ Common issues to check:
1130
1182
  }
1131
1183
  });
1132
1184
  // Tool: Delete a variable collection
1133
- this.server.tool("figma_delete_variable_collection", "Delete a Figma variable collection and ALL its variables. WARNING: This is a destructive operation that deletes all variables in the collection and cannot be undone (except with Figma's undo). Requires the F-MCP ATezer Bridge plugin to be running.", {
1134
- collectionId: z.string().describe("The collection ID to delete (e.g., 'VariableCollectionId:123:456'). Get this from figma_get_variables."),
1185
+ this.server.registerTool("figma_delete_variable_collection", {
1186
+ description: "Delete a Figma variable collection and ALL its variables. WARNING: This is a destructive operation that deletes all variables in the collection and cannot be undone (except with Figma's undo). Requires the F-MCP ATezer Bridge plugin to be running.",
1187
+ inputSchema: {
1188
+ collectionId: z.string().describe("The collection ID to delete (e.g., 'VariableCollectionId:123:456'). Get this from figma_get_variables."),
1189
+ },
1190
+ annotations: { destructiveHint: true },
1135
1191
  }, async ({ collectionId }) => {
1136
1192
  try {
1137
1193
  const connector = await this.getDesktopConnector();
@@ -1169,9 +1225,13 @@ Common issues to check:
1169
1225
  }
1170
1226
  });
1171
1227
  // Tool: Rename a variable
1172
- this.server.tool("figma_rename_variable", "Rename an existing Figma variable. This updates the variable's name while preserving all its values and settings. Requires the F-MCP ATezer Bridge plugin to be running.", {
1173
- variableId: z.string().describe("The variable ID to rename (e.g., 'VariableID:123:456'). Get this from figma_get_variables."),
1174
- newName: z.string().describe("The new name for the variable. Can include slashes for grouping (e.g., 'colors/primary/background')."),
1228
+ this.server.registerTool("figma_rename_variable", {
1229
+ description: "Rename an existing Figma variable. This updates the variable's name while preserving all its values and settings. Requires the F-MCP ATezer Bridge plugin to be running.",
1230
+ inputSchema: {
1231
+ variableId: z.string().describe("The variable ID to rename (e.g., 'VariableID:123:456'). Get this from figma_get_variables."),
1232
+ newName: z.string().describe("The new name for the variable. Can include slashes for grouping (e.g., 'colors/primary/background')."),
1233
+ },
1234
+ annotations: { destructiveHint: true },
1175
1235
  }, async ({ variableId, newName }) => {
1176
1236
  try {
1177
1237
  const connector = await this.getDesktopConnector();
@@ -1209,9 +1269,13 @@ Common issues to check:
1209
1269
  }
1210
1270
  });
1211
1271
  // Tool: Add a mode to a collection
1212
- this.server.tool("figma_add_mode", "Add a new mode to an existing Figma variable collection. Modes allow variables to have different values for different contexts (e.g., Light/Dark themes, device sizes). Requires the F-MCP ATezer Bridge plugin to be running.", {
1213
- collectionId: z.string().describe("The collection ID to add the mode to (e.g., 'VariableCollectionId:123:456'). Get this from figma_get_variables."),
1214
- modeName: z.string().describe("The name for the new mode (e.g., 'Dark', 'Mobile', 'High Contrast')."),
1272
+ this.server.registerTool("figma_add_mode", {
1273
+ description: "Add a new mode to an existing Figma variable collection. Modes allow variables to have different values for different contexts (e.g., Light/Dark themes, device sizes). Requires the F-MCP ATezer Bridge plugin to be running.",
1274
+ inputSchema: {
1275
+ collectionId: z.string().describe("The collection ID to add the mode to (e.g., 'VariableCollectionId:123:456'). Get this from figma_get_variables."),
1276
+ modeName: z.string().describe("The name for the new mode (e.g., 'Dark', 'Mobile', 'High Contrast')."),
1277
+ },
1278
+ annotations: { destructiveHint: true },
1215
1279
  }, async ({ collectionId, modeName }) => {
1216
1280
  try {
1217
1281
  const connector = await this.getDesktopConnector();
@@ -1249,10 +1313,14 @@ Common issues to check:
1249
1313
  }
1250
1314
  });
1251
1315
  // Tool: Rename a mode in a collection
1252
- this.server.tool("figma_rename_mode", "Rename an existing mode in a Figma variable collection. Requires the F-MCP ATezer Bridge plugin to be running.", {
1253
- collectionId: z.string().describe("The collection ID containing the mode (e.g., 'VariableCollectionId:123:456'). Get this from figma_get_variables."),
1254
- modeId: z.string().describe("The mode ID to rename (e.g., '123:0'). Get this from the collection's modes array in figma_get_variables."),
1255
- newName: z.string().describe("The new name for the mode (e.g., 'Dark Theme', 'Tablet')."),
1316
+ this.server.registerTool("figma_rename_mode", {
1317
+ description: "Rename an existing mode in a Figma variable collection. Requires the F-MCP ATezer Bridge plugin to be running.",
1318
+ inputSchema: {
1319
+ collectionId: z.string().describe("The collection ID containing the mode (e.g., 'VariableCollectionId:123:456'). Get this from figma_get_variables."),
1320
+ modeId: z.string().describe("The mode ID to rename (e.g., '123:0'). Get this from the collection's modes array in figma_get_variables."),
1321
+ newName: z.string().describe("The new name for the mode (e.g., 'Dark Theme', 'Tablet')."),
1322
+ },
1323
+ annotations: { destructiveHint: true },
1256
1324
  }, async ({ collectionId, modeId, newName }) => {
1257
1325
  try {
1258
1326
  const connector = await this.getDesktopConnector();
@@ -1405,8 +1473,12 @@ Common issues to check:
1405
1473
  return { cacheEntry, fileKey, wasLoaded: true };
1406
1474
  };
1407
1475
  // Tool 1: Get Design System Summary (~1000 tokens response)
1408
- this.server.tool("figma_get_design_system_summary", "Get a compact overview of the design system. Returns categories, component counts, and token collection names WITHOUT full details. Use this first to understand what's available, then use figma_search_components to find specific components. This tool is optimized for minimal token usage.", {
1409
- forceRefresh: z.boolean().optional().default(false).describe("Force refresh the cached data (use sparingly - extraction can take minutes for large files)"),
1476
+ this.server.registerTool("figma_get_design_system_summary", {
1477
+ description: "Get a compact overview of the design system. Returns categories, component counts, and token collection names WITHOUT full details. Use this first to understand what's available, then use figma_search_components to find specific components. This tool is optimized for minimal token usage.",
1478
+ inputSchema: {
1479
+ forceRefresh: z.boolean().optional().default(false).describe("Force refresh the cached data (use sparingly - extraction can take minutes for large files)"),
1480
+ },
1481
+ annotations: { readOnlyHint: true },
1410
1482
  }, async ({ forceRefresh }) => {
1411
1483
  try {
1412
1484
  const { DesignSystemManifestCache, createEmptyManifest, figmaColorToHex, getCategories, getTokenSummary, } = await import('./core/design-system-manifest.js');
@@ -1568,11 +1640,15 @@ Common issues to check:
1568
1640
  }
1569
1641
  });
1570
1642
  // Tool 2: Search Components (~3000 tokens response max, paginated)
1571
- this.server.tool("figma_search_components", "Search for components by name, category, or description. Returns paginated results with component keys for instantiation. Automatically loads the design system cache if needed.", {
1572
- query: z.string().optional().default("").describe("Search query to match component names or descriptions"),
1573
- category: z.string().optional().describe("Filter by category (e.g., 'Button', 'Input', 'Card')"),
1574
- limit: z.number().optional().default(10).describe("Maximum results to return (default: 10, max: 25)"),
1575
- offset: z.number().optional().default(0).describe("Offset for pagination"),
1643
+ this.server.registerTool("figma_search_components", {
1644
+ description: "Search for components by name, category, or description. Returns paginated results with component keys for instantiation. Automatically loads the design system cache if needed.",
1645
+ inputSchema: {
1646
+ query: z.string().optional().default("").describe("Search query to match component names or descriptions"),
1647
+ category: z.string().optional().describe("Filter by category (e.g., 'Button', 'Input', 'Card')"),
1648
+ limit: z.number().optional().default(10).describe("Maximum results to return (default: 10, max: 25)"),
1649
+ offset: z.number().optional().default(0).describe("Offset for pagination"),
1650
+ },
1651
+ annotations: { readOnlyHint: true },
1576
1652
  }, async ({ query, category, limit, offset }) => {
1577
1653
  try {
1578
1654
  const { searchComponents } = await import('./core/design-system-manifest.js');
@@ -1630,9 +1706,13 @@ Common issues to check:
1630
1706
  }
1631
1707
  });
1632
1708
  // Tool 3: Get Component Details (~500 tokens per component)
1633
- this.server.tool("figma_get_component_details", "Get full details for a specific component including all variants, properties, and keys needed for instantiation. Use the component key or name from figma_search_components.", {
1634
- componentKey: z.string().optional().describe("The component key (preferred for exact match)"),
1635
- componentName: z.string().optional().describe("The component name (used if key not provided)"),
1709
+ this.server.registerTool("figma_get_component_details", {
1710
+ description: "Get full details for a specific component including all variants, properties, and keys needed for instantiation. Use the component key or name from figma_search_components.",
1711
+ inputSchema: {
1712
+ componentKey: z.string().optional().describe("The component key (preferred for exact match)"),
1713
+ componentName: z.string().optional().describe("The component name (used if key not provided)"),
1714
+ },
1715
+ annotations: { readOnlyHint: true },
1636
1716
  }, async ({ componentKey, componentName }) => {
1637
1717
  try {
1638
1718
  if (!componentKey && !componentName) {
@@ -1720,10 +1800,14 @@ Common issues to check:
1720
1800
  }
1721
1801
  });
1722
1802
  // Tool 4: Get Token Values (~2000 tokens response max)
1723
- this.server.tool("figma_get_token_values", "Get actual values for design tokens (colors, spacing, etc). Use after figma_get_design_system_summary to get specific token values for implementation.", {
1724
- type: z.enum(["colors", "spacing", "all"]).optional().default("all").describe("Type of tokens to retrieve"),
1725
- filter: z.string().optional().describe("Filter token names (e.g., 'primary' to get all primary colors)"),
1726
- limit: z.number().optional().default(50).describe("Maximum tokens to return (default: 50)"),
1803
+ this.server.registerTool("figma_get_token_values", {
1804
+ description: "Get actual values for design tokens (colors, spacing, etc). Use after figma_get_design_system_summary to get specific token values for implementation.",
1805
+ inputSchema: {
1806
+ type: z.enum(["colors", "spacing", "all"]).optional().default("all").describe("Type of tokens to retrieve"),
1807
+ filter: z.string().optional().describe("Filter token names (e.g., 'primary' to get all primary colors)"),
1808
+ limit: z.number().optional().default(50).describe("Maximum tokens to return (default: 50)"),
1809
+ },
1810
+ annotations: { readOnlyHint: true },
1727
1811
  }, async ({ type, filter, limit }) => {
1728
1812
  try {
1729
1813
  // Auto-load design system cache if needed
@@ -1796,22 +1880,26 @@ Common issues to check:
1796
1880
  }
1797
1881
  });
1798
1882
  // Tool 5: Instantiate Component
1799
- this.server.tool("figma_instantiate_component", `Create an instance of a component from the design system. Works with both published library components (by key) and local/unpublished components (by nodeId).
1883
+ this.server.registerTool("figma_instantiate_component", {
1884
+ description: `Create an instance of a component from the design system. Works with both published library components (by key) and local/unpublished components (by nodeId).
1800
1885
 
1801
1886
  **IMPORTANT: Always re-search before instantiating!**
1802
1887
  NodeIds are session-specific and may be stale from previous conversations. ALWAYS call figma_search_components at the start of each design session to get current, valid identifiers.
1803
1888
 
1804
1889
  **VISUAL VALIDATION WORKFLOW:**
1805
- After instantiating components, use figma_take_screenshot to verify the result looks correct. Check placement, sizing, and visual balance.`, {
1806
- componentKey: z.string().optional().describe("The component key (for published library components). Get this from figma_search_components."),
1807
- nodeId: z.string().optional().describe("The node ID (for local/unpublished components). Get this from figma_search_components. Required if componentKey doesn't work."),
1808
- variant: z.record(z.string()).optional().describe("Variant properties to set (e.g., { Type: 'Simple', State: 'Active' })"),
1809
- overrides: z.record(z.any()).optional().describe("Property overrides (e.g., { 'Button Label': 'Click Me' })"),
1810
- position: z.object({
1811
- x: z.number(),
1812
- y: z.number(),
1813
- }).optional().describe("Position on canvas (default: 0, 0)"),
1814
- parentId: z.string().optional().describe("Parent node ID to append the instance to"),
1890
+ After instantiating components, use figma_take_screenshot to verify the result looks correct. Check placement, sizing, and visual balance.`,
1891
+ inputSchema: {
1892
+ componentKey: z.string().optional().describe("The component key (for published library components). Get this from figma_search_components."),
1893
+ nodeId: z.string().optional().describe("The node ID (for local/unpublished components). Get this from figma_search_components. Required if componentKey doesn't work."),
1894
+ variant: z.record(z.string()).optional().describe("Variant properties to set (e.g., { Type: 'Simple', State: 'Active' })"),
1895
+ overrides: z.record(z.any()).optional().describe("Property overrides (e.g., { 'Button Label': 'Click Me' })"),
1896
+ position: z.object({
1897
+ x: z.number(),
1898
+ y: z.number(),
1899
+ }).optional().describe("Position on canvas (default: 0, 0)"),
1900
+ parentId: z.string().optional().describe("Parent node ID to append the instance to"),
1901
+ },
1902
+ annotations: { destructiveHint: true },
1815
1903
  }, async ({ componentKey, nodeId, variant, overrides, position, parentId }) => {
1816
1904
  try {
1817
1905
  if (!componentKey && !nodeId) {
@@ -1863,10 +1951,14 @@ After instantiating components, use figma_take_screenshot to verify the result l
1863
1951
  // NEW: Component Property Management Tools
1864
1952
  // ============================================================================
1865
1953
  // Tool: Set Node Description
1866
- this.server.tool("figma_set_description", "Set the description text on a component, component set, or style. Descriptions appear in Dev Mode and help document design intent. Supports plain text and markdown formatting.", {
1867
- nodeId: z.string().describe("The node ID of the component or style to update (e.g., '123:456')"),
1868
- description: z.string().describe("The plain text description to set"),
1869
- descriptionMarkdown: z.string().optional().describe("Optional rich text description using markdown formatting"),
1954
+ this.server.registerTool("figma_set_description", {
1955
+ description: "Set the description text on a component, component set, or style. Descriptions appear in Dev Mode and help document design intent. Supports plain text and markdown formatting.",
1956
+ inputSchema: {
1957
+ nodeId: z.string().describe("The node ID of the component or style to update (e.g., '123:456')"),
1958
+ description: z.string().describe("The plain text description to set"),
1959
+ descriptionMarkdown: z.string().optional().describe("Optional rich text description using markdown formatting"),
1960
+ },
1961
+ annotations: { destructiveHint: true },
1870
1962
  }, async ({ nodeId, description, descriptionMarkdown }) => {
1871
1963
  try {
1872
1964
  const connector = await this.getDesktopConnector();
@@ -1900,11 +1992,15 @@ After instantiating components, use figma_take_screenshot to verify the result l
1900
1992
  }
1901
1993
  });
1902
1994
  // Tool: Add Component Property
1903
- this.server.tool("figma_add_component_property", "Add a new component property to a component or component set. Properties enable dynamic content and behavior in component instances. Supported types: BOOLEAN (toggle), TEXT (string), INSTANCE_SWAP (component swap), VARIANT (variant selection).", {
1904
- nodeId: z.string().describe("The component or component set node ID"),
1905
- propertyName: z.string().describe("Name for the new property (e.g., 'Show Icon', 'Button Label')"),
1906
- type: z.enum(["BOOLEAN", "TEXT", "INSTANCE_SWAP", "VARIANT"]).describe("Property type: BOOLEAN for toggles, TEXT for strings, INSTANCE_SWAP for component swaps, VARIANT for variant selection"),
1907
- defaultValue: z.any().describe("Default value for the property. BOOLEAN: true/false, TEXT: string, INSTANCE_SWAP: component key, VARIANT: variant value"),
1995
+ this.server.registerTool("figma_add_component_property", {
1996
+ description: "Add a new component property to a component or component set. Properties enable dynamic content and behavior in component instances. Supported types: BOOLEAN (toggle), TEXT (string), INSTANCE_SWAP (component swap), VARIANT (variant selection).",
1997
+ inputSchema: {
1998
+ nodeId: z.string().describe("The component or component set node ID"),
1999
+ propertyName: z.string().describe("Name for the new property (e.g., 'Show Icon', 'Button Label')"),
2000
+ type: z.enum(["BOOLEAN", "TEXT", "INSTANCE_SWAP", "VARIANT"]).describe("Property type: BOOLEAN for toggles, TEXT for strings, INSTANCE_SWAP for component swaps, VARIANT for variant selection"),
2001
+ defaultValue: z.any().describe("Default value for the property. BOOLEAN: true/false, TEXT: string, INSTANCE_SWAP: component key, VARIANT: variant value"),
2002
+ },
2003
+ annotations: { destructiveHint: true },
1908
2004
  }, async ({ nodeId, propertyName, type, defaultValue }) => {
1909
2005
  try {
1910
2006
  const connector = await this.getDesktopConnector();
@@ -1939,14 +2035,18 @@ After instantiating components, use figma_take_screenshot to verify the result l
1939
2035
  }
1940
2036
  });
1941
2037
  // Tool: Edit Component Property
1942
- this.server.tool("figma_edit_component_property", "Edit an existing component property. Can change the name, default value, or preferred values (for INSTANCE_SWAP). Use the full property name including the unique suffix.", {
1943
- nodeId: z.string().describe("The component or component set node ID"),
1944
- propertyName: z.string().describe("The full property name with suffix (e.g., 'Show Icon#123:456')"),
1945
- newValue: z.object({
1946
- name: z.string().optional().describe("New name for the property"),
1947
- defaultValue: z.any().optional().describe("New default value"),
1948
- preferredValues: z.array(z.any()).optional().describe("Preferred values (INSTANCE_SWAP only)"),
1949
- }).describe("Object with the values to update"),
2038
+ this.server.registerTool("figma_edit_component_property", {
2039
+ description: "Edit an existing component property. Can change the name, default value, or preferred values (for INSTANCE_SWAP). Use the full property name including the unique suffix.",
2040
+ inputSchema: {
2041
+ nodeId: z.string().describe("The component or component set node ID"),
2042
+ propertyName: z.string().describe("The full property name with suffix (e.g., 'Show Icon#123:456')"),
2043
+ newValue: z.object({
2044
+ name: z.string().optional().describe("New name for the property"),
2045
+ defaultValue: z.any().optional().describe("New default value"),
2046
+ preferredValues: z.array(z.any()).optional().describe("Preferred values (INSTANCE_SWAP only)"),
2047
+ }).describe("Object with the values to update"),
2048
+ },
2049
+ annotations: { destructiveHint: true },
1950
2050
  }, async ({ nodeId, propertyName, newValue }) => {
1951
2051
  try {
1952
2052
  const connector = await this.getDesktopConnector();
@@ -1979,9 +2079,13 @@ After instantiating components, use figma_take_screenshot to verify the result l
1979
2079
  }
1980
2080
  });
1981
2081
  // Tool: Delete Component Property
1982
- this.server.tool("figma_delete_component_property", "Delete a component property. Only works with BOOLEAN, TEXT, and INSTANCE_SWAP properties (not VARIANT). This is a destructive operation.", {
1983
- nodeId: z.string().describe("The component or component set node ID"),
1984
- propertyName: z.string().describe("The full property name with suffix (e.g., 'Show Icon#123:456')"),
2082
+ this.server.registerTool("figma_delete_component_property", {
2083
+ description: "Delete a component property. Only works with BOOLEAN, TEXT, and INSTANCE_SWAP properties (not VARIANT). This is a destructive operation.",
2084
+ inputSchema: {
2085
+ nodeId: z.string().describe("The component or component set node ID"),
2086
+ propertyName: z.string().describe("The full property name with suffix (e.g., 'Show Icon#123:456')"),
2087
+ },
2088
+ annotations: { destructiveHint: true },
1985
2089
  }, async ({ nodeId, propertyName }) => {
1986
2090
  try {
1987
2091
  const connector = await this.getDesktopConnector();
@@ -2017,11 +2121,15 @@ After instantiating components, use figma_take_screenshot to verify the result l
2017
2121
  // NEW: Node Manipulation Tools
2018
2122
  // ============================================================================
2019
2123
  // Tool: Resize Node
2020
- this.server.tool("figma_resize_node", "Resize a node to specific dimensions. By default respects child constraints; use withConstraints=false to ignore them.", {
2021
- nodeId: z.string().describe("The node ID to resize"),
2022
- width: z.number().describe("New width in pixels"),
2023
- height: z.number().describe("New height in pixels"),
2024
- withConstraints: z.boolean().optional().default(true).describe("Whether to apply child constraints during resize (default: true)"),
2124
+ this.server.registerTool("figma_resize_node", {
2125
+ description: "Resize a node to specific dimensions. By default respects child constraints; use withConstraints=false to ignore them.",
2126
+ inputSchema: {
2127
+ nodeId: z.string().describe("The node ID to resize"),
2128
+ width: z.number().describe("New width in pixels"),
2129
+ height: z.number().describe("New height in pixels"),
2130
+ withConstraints: z.boolean().optional().default(true).describe("Whether to apply child constraints during resize (default: true)"),
2131
+ },
2132
+ annotations: { destructiveHint: true },
2025
2133
  }, async ({ nodeId, width, height, withConstraints }) => {
2026
2134
  try {
2027
2135
  const connector = await this.getDesktopConnector();
@@ -2054,10 +2162,14 @@ After instantiating components, use figma_take_screenshot to verify the result l
2054
2162
  }
2055
2163
  });
2056
2164
  // Tool: Move Node
2057
- this.server.tool("figma_move_node", "Move a node to a new position within its parent.", {
2058
- nodeId: z.string().describe("The node ID to move"),
2059
- x: z.number().describe("New X position"),
2060
- y: z.number().describe("New Y position"),
2165
+ this.server.registerTool("figma_move_node", {
2166
+ description: "Move a node to a new position within its parent.",
2167
+ inputSchema: {
2168
+ nodeId: z.string().describe("The node ID to move"),
2169
+ x: z.number().describe("New X position"),
2170
+ y: z.number().describe("New Y position"),
2171
+ },
2172
+ annotations: { destructiveHint: true },
2061
2173
  }, async ({ nodeId, x, y }) => {
2062
2174
  try {
2063
2175
  const connector = await this.getDesktopConnector();
@@ -2090,13 +2202,17 @@ After instantiating components, use figma_take_screenshot to verify the result l
2090
2202
  }
2091
2203
  });
2092
2204
  // Tool: Set Node Fills
2093
- this.server.tool("figma_set_fills", "Set the fill colors on a node. Accepts hex color strings (e.g., '#FF0000') or full paint objects.", {
2094
- nodeId: z.string().describe("The node ID to modify"),
2095
- fills: z.array(z.object({
2096
- type: z.literal("SOLID").describe("Fill type (currently only SOLID supported)"),
2097
- color: z.string().describe("Hex color string (e.g., '#FF0000', '#FF000080' for transparency)"),
2098
- opacity: z.number().optional().describe("Opacity 0-1 (default: 1)"),
2099
- })).describe("Array of fill objects"),
2205
+ this.server.registerTool("figma_set_fills", {
2206
+ description: "Set the fill colors on a node. Accepts hex color strings (e.g., '#FF0000') or full paint objects.",
2207
+ inputSchema: {
2208
+ nodeId: z.string().describe("The node ID to modify"),
2209
+ fills: z.array(z.object({
2210
+ type: z.literal("SOLID").describe("Fill type (currently only SOLID supported)"),
2211
+ color: z.string().describe("Hex color string (e.g., '#FF0000', '#FF000080' for transparency)"),
2212
+ opacity: z.number().optional().describe("Opacity 0-1 (default: 1)"),
2213
+ })).describe("Array of fill objects"),
2214
+ },
2215
+ annotations: { destructiveHint: true },
2100
2216
  }, async ({ nodeId, fills }) => {
2101
2217
  try {
2102
2218
  const connector = await this.getDesktopConnector();
@@ -2129,14 +2245,18 @@ After instantiating components, use figma_take_screenshot to verify the result l
2129
2245
  }
2130
2246
  });
2131
2247
  // Tool: Set Node Strokes
2132
- this.server.tool("figma_set_strokes", "Set the stroke (border) on a node. Accepts hex color strings and optional stroke weight.", {
2133
- nodeId: z.string().describe("The node ID to modify"),
2134
- strokes: z.array(z.object({
2135
- type: z.literal("SOLID").describe("Stroke type"),
2136
- color: z.string().describe("Hex color string"),
2137
- opacity: z.number().optional().describe("Opacity 0-1"),
2138
- })).describe("Array of stroke objects"),
2139
- strokeWeight: z.number().optional().describe("Stroke thickness in pixels"),
2248
+ this.server.registerTool("figma_set_strokes", {
2249
+ description: "Set the stroke (border) on a node. Accepts hex color strings and optional stroke weight.",
2250
+ inputSchema: {
2251
+ nodeId: z.string().describe("The node ID to modify"),
2252
+ strokes: z.array(z.object({
2253
+ type: z.literal("SOLID").describe("Stroke type"),
2254
+ color: z.string().describe("Hex color string"),
2255
+ opacity: z.number().optional().describe("Opacity 0-1"),
2256
+ })).describe("Array of stroke objects"),
2257
+ strokeWeight: z.number().optional().describe("Stroke thickness in pixels"),
2258
+ },
2259
+ annotations: { destructiveHint: true },
2140
2260
  }, async ({ nodeId, strokes, strokeWeight }) => {
2141
2261
  try {
2142
2262
  const connector = await this.getDesktopConnector();
@@ -2169,8 +2289,12 @@ After instantiating components, use figma_take_screenshot to verify the result l
2169
2289
  }
2170
2290
  });
2171
2291
  // Tool: Clone Node
2172
- this.server.tool("figma_clone_node", "Duplicate a node. The clone is placed at a slight offset from the original.", {
2173
- nodeId: z.string().describe("The node ID to clone"),
2292
+ this.server.registerTool("figma_clone_node", {
2293
+ description: "Duplicate a node. The clone is placed at a slight offset from the original.",
2294
+ inputSchema: {
2295
+ nodeId: z.string().describe("The node ID to clone"),
2296
+ },
2297
+ annotations: { destructiveHint: true },
2174
2298
  }, async ({ nodeId }) => {
2175
2299
  try {
2176
2300
  const connector = await this.getDesktopConnector();
@@ -2203,8 +2327,12 @@ After instantiating components, use figma_take_screenshot to verify the result l
2203
2327
  }
2204
2328
  });
2205
2329
  // Tool: Delete Node
2206
- this.server.tool("figma_delete_node", "Delete a node from the canvas. WARNING: This is a destructive operation (can be undone with Figma's undo).", {
2207
- nodeId: z.string().describe("The node ID to delete"),
2330
+ this.server.registerTool("figma_delete_node", {
2331
+ description: "Delete a node from the canvas. WARNING: This is a destructive operation (can be undone with Figma's undo).",
2332
+ inputSchema: {
2333
+ nodeId: z.string().describe("The node ID to delete"),
2334
+ },
2335
+ annotations: { destructiveHint: true },
2208
2336
  }, async ({ nodeId }) => {
2209
2337
  try {
2210
2338
  const connector = await this.getDesktopConnector();
@@ -2237,9 +2365,13 @@ After instantiating components, use figma_take_screenshot to verify the result l
2237
2365
  }
2238
2366
  });
2239
2367
  // Tool: Rename Node
2240
- this.server.tool("figma_rename_node", "Rename a node in the layer panel.", {
2241
- nodeId: z.string().describe("The node ID to rename"),
2242
- newName: z.string().describe("The new name for the node"),
2368
+ this.server.registerTool("figma_rename_node", {
2369
+ description: "Rename a node in the layer panel.",
2370
+ inputSchema: {
2371
+ nodeId: z.string().describe("The node ID to rename"),
2372
+ newName: z.string().describe("The new name for the node"),
2373
+ },
2374
+ annotations: { destructiveHint: true },
2243
2375
  }, async ({ nodeId, newName }) => {
2244
2376
  try {
2245
2377
  const connector = await this.getDesktopConnector();
@@ -2272,10 +2404,14 @@ After instantiating components, use figma_take_screenshot to verify the result l
2272
2404
  }
2273
2405
  });
2274
2406
  // Tool: Set Text Content
2275
- this.server.tool("figma_set_text", "Set the text content of a text node. Optionally adjust font size.", {
2276
- nodeId: z.string().describe("The text node ID"),
2277
- text: z.string().describe("The new text content"),
2278
- fontSize: z.number().optional().describe("Optional font size to set"),
2407
+ this.server.registerTool("figma_set_text", {
2408
+ description: "Set the text content of a text node. Optionally adjust font size.",
2409
+ inputSchema: {
2410
+ nodeId: z.string().describe("The text node ID"),
2411
+ text: z.string().describe("The new text content"),
2412
+ fontSize: z.number().optional().describe("Optional font size to set"),
2413
+ },
2414
+ annotations: { destructiveHint: true },
2279
2415
  }, async ({ nodeId, text, fontSize }) => {
2280
2416
  try {
2281
2417
  const connector = await this.getDesktopConnector();
@@ -2309,21 +2445,25 @@ After instantiating components, use figma_take_screenshot to verify the result l
2309
2445
  }
2310
2446
  });
2311
2447
  // Tool: Create Child Node
2312
- this.server.tool("figma_create_child", "Create a new child node inside a parent container. Useful for adding shapes, text, or frames to existing structures.", {
2313
- parentId: z.string().describe("The parent node ID"),
2314
- nodeType: z.enum(["RECTANGLE", "ELLIPSE", "FRAME", "TEXT", "LINE"]).describe("Type of node to create"),
2315
- properties: z.object({
2316
- name: z.string().optional().describe("Name for the new node"),
2317
- x: z.number().optional().describe("X position within parent"),
2318
- y: z.number().optional().describe("Y position within parent"),
2319
- width: z.number().optional().describe("Width (default: 100)"),
2320
- height: z.number().optional().describe("Height (default: 100)"),
2321
- fills: z.array(z.object({
2322
- type: z.literal("SOLID"),
2323
- color: z.string(),
2324
- })).optional().describe("Fill colors (hex strings)"),
2325
- text: z.string().optional().describe("Text content (for TEXT nodes only)"),
2326
- }).optional().describe("Properties for the new node"),
2448
+ this.server.registerTool("figma_create_child", {
2449
+ description: "Create a new child node inside a parent container. Useful for adding shapes, text, or frames to existing structures.",
2450
+ inputSchema: {
2451
+ parentId: z.string().describe("The parent node ID"),
2452
+ nodeType: z.enum(["RECTANGLE", "ELLIPSE", "FRAME", "TEXT", "LINE"]).describe("Type of node to create"),
2453
+ properties: z.object({
2454
+ name: z.string().optional().describe("Name for the new node"),
2455
+ x: z.number().optional().describe("X position within parent"),
2456
+ y: z.number().optional().describe("Y position within parent"),
2457
+ width: z.number().optional().describe("Width (default: 100)"),
2458
+ height: z.number().optional().describe("Height (default: 100)"),
2459
+ fills: z.array(z.object({
2460
+ type: z.literal("SOLID"),
2461
+ color: z.string(),
2462
+ })).optional().describe("Fill colors (hex strings)"),
2463
+ text: z.string().optional().describe("Text content (for TEXT nodes only)"),
2464
+ }).optional().describe("Properties for the new node"),
2465
+ },
2466
+ annotations: { destructiveHint: true },
2327
2467
  }, async ({ parentId, nodeType, properties }) => {
2328
2468
  try {
2329
2469
  const connector = await this.getDesktopConnector();