@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/LICENSE +0 -11
- package/README.md +11 -2
- package/dist/cloudflare/core/figma-tools.js +290 -254
- package/dist/cloudflare/core/plugin-bridge-server.js +64 -16
- package/dist/core/figma-tools.d.ts.map +1 -1
- package/dist/core/figma-tools.js +290 -254
- package/dist/core/figma-tools.js.map +1 -1
- package/dist/core/plugin-bridge-server.d.ts +5 -1
- package/dist/core/plugin-bridge-server.d.ts.map +1 -1
- package/dist/core/plugin-bridge-server.js +64 -16
- package/dist/core/plugin-bridge-server.js.map +1 -1
- package/dist/local-plugin-only.d.ts.map +1 -1
- package/dist/local-plugin-only.js +243 -111
- package/dist/local-plugin-only.js.map +1 -1
- package/dist/local.js +323 -183
- package/dist/local.js.map +1 -1
- package/f-mcp-plugin/code.js +27 -0
- package/f-mcp-plugin/manifest.json +1 -1
- package/f-mcp-plugin/ui.html +100 -29
- package/package.json +3 -3
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: "
|
|
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.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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.
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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.
|
|
458
|
-
duration:
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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.
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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.
|
|
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.
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
890
|
-
|
|
891
|
-
|
|
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.
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
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.
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
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.
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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.
|
|
1095
|
-
|
|
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.
|
|
1134
|
-
|
|
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.
|
|
1173
|
-
|
|
1174
|
-
|
|
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.
|
|
1213
|
-
|
|
1214
|
-
|
|
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.
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
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.
|
|
1409
|
-
|
|
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.
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
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.
|
|
1634
|
-
|
|
1635
|
-
|
|
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.
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
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.
|
|
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
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
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.
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
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.
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
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.
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
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.
|
|
1983
|
-
|
|
1984
|
-
|
|
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.
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
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.
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
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.
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
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.
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
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.
|
|
2173
|
-
|
|
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.
|
|
2207
|
-
|
|
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.
|
|
2241
|
-
|
|
2242
|
-
|
|
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.
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
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.
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
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();
|