@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.
@@ -1 +1 @@
1
- {"version":3,"file":"local-plugin-only.d.ts","sourceRoot":"","sources":["../src/local-plugin-only.ts"],"names":[],"mappings":";AAEA;;;;;;;;;GASG;AA0CH,wBAAsB,IAAI,kBA0vBzB"}
1
+ {"version":3,"file":"local-plugin-only.d.ts","sourceRoot":"","sources":["../src/local-plugin-only.ts"],"names":[],"mappings":";AAEA;;;;;;;;;GASG;AA0CH,wBAAsB,IAAI,kBAm2BzB"}
@@ -56,17 +56,21 @@ export async function main() {
56
56
  bridge.start();
57
57
  const server = new McpServer({
58
58
  name: "F-MCP ATezer Bridge (Plugin-only)",
59
- version: "1.0.0",
59
+ version: "1.1.2",
60
60
  });
61
61
  // ---- figma_get_file_data_plugin (no REST, no token) ----
62
- server.tool("figma_get_file_data", "Get file structure and document tree from the open Figma file. No REST API or token needed. Uses plugin only. Start with depth=1 and verbosity=summary for minimal tokens. Use includeLayout/includeVisual/includeTypography for pixel-perfect spec (auto-layout, constraints, fills, typography).", {
63
- depth: z.number().min(0).max(3).optional().default(1),
64
- verbosity: z.enum(["summary", "standard", "full"]).optional().default("summary"),
65
- includeLayout: z.boolean().optional(),
66
- includeVisual: z.boolean().optional(),
67
- includeTypography: z.boolean().optional(),
68
- includeCodeReady: z.boolean().optional(),
69
- outputHint: z.enum(["react", "tailwind"]).optional(),
62
+ server.registerTool("figma_get_file_data", {
63
+ description: "Get file structure and document tree from the open Figma file. No REST API or token needed. Uses plugin only. Start with depth=1 and verbosity=summary for minimal tokens. Use includeLayout/includeVisual/includeTypography for pixel-perfect spec (auto-layout, constraints, fills, typography).",
64
+ inputSchema: {
65
+ depth: z.number().min(0).max(3).optional().default(1),
66
+ verbosity: z.enum(["summary", "standard", "full"]).optional().default("summary"),
67
+ includeLayout: z.boolean().optional(),
68
+ includeVisual: z.boolean().optional(),
69
+ includeTypography: z.boolean().optional(),
70
+ includeCodeReady: z.boolean().optional(),
71
+ outputHint: z.enum(["react", "tailwind"]).optional(),
72
+ },
73
+ annotations: { readOnlyHint: true },
70
74
  }, async ({ depth, verbosity, includeLayout, includeVisual, includeTypography, includeCodeReady, outputHint }) => {
71
75
  try {
72
76
  const conn = getConnector(bridge);
@@ -94,16 +98,20 @@ export async function main() {
94
98
  }
95
99
  });
96
100
  // ---- figma_get_design_context (get_design_context tarzı, token tasarruflu, Figma token yok) ----
97
- server.tool("figma_get_design_context", "Design context for a node or whole file: structure + text, and optionally layout/visual/typography. Returns roleHint/suiComponent (SUI-style name), layoutSummary, colorHex, variantSummary/suggestedProps for instances, incompleteReasons, hasImageFill. Use outputHint: react or tailwind for code-ready layoutSummary. No Figma REST API, no token.", {
98
- nodeId: z.string().optional(),
99
- depth: z.number().min(0).max(3).optional().default(2),
100
- verbosity: z.enum(["summary", "standard", "full"]).optional().default("standard"),
101
- excludeScreenshot: z.boolean().optional(),
102
- includeLayout: z.boolean().optional(),
103
- includeVisual: z.boolean().optional(),
104
- includeTypography: z.boolean().optional(),
105
- includeCodeReady: z.boolean().optional(),
106
- outputHint: z.enum(["react", "tailwind"]).optional(),
101
+ server.registerTool("figma_get_design_context", {
102
+ description: "Design context for a node or whole file: structure + text, and optionally layout/visual/typography. Returns roleHint/suiComponent (SUI-style name), layoutSummary, colorHex, variantSummary/suggestedProps for instances, incompleteReasons, hasImageFill. Use outputHint: react or tailwind for code-ready layoutSummary. No Figma REST API, no token.",
103
+ inputSchema: {
104
+ nodeId: z.string().optional(),
105
+ depth: z.number().min(0).max(3).optional().default(2),
106
+ verbosity: z.enum(["summary", "standard", "full"]).optional().default("standard"),
107
+ excludeScreenshot: z.boolean().optional(),
108
+ includeLayout: z.boolean().optional(),
109
+ includeVisual: z.boolean().optional(),
110
+ includeTypography: z.boolean().optional(),
111
+ includeCodeReady: z.boolean().optional(),
112
+ outputHint: z.enum(["react", "tailwind"]).optional(),
113
+ },
114
+ annotations: { readOnlyHint: true },
107
115
  }, async ({ nodeId, depth, verbosity, includeLayout, includeVisual, includeTypography, includeCodeReady, outputHint }) => {
108
116
  try {
109
117
  const conn = getConnector(bridge);
@@ -133,8 +141,12 @@ export async function main() {
133
141
  }
134
142
  });
135
143
  // ---- figma_get_variables (plugin only, token-friendly default) ----
136
- server.tool("figma_get_variables", "Get design tokens and variables from the open Figma file. No REST API or token. Returns summary by default to save tokens.", {
137
- verbosity: z.enum(["inventory", "summary", "standard", "full"]).optional().default("summary"),
144
+ server.registerTool("figma_get_variables", {
145
+ description: "Get design tokens and variables from the open Figma file. No REST API or token. Returns summary by default to save tokens.",
146
+ inputSchema: {
147
+ verbosity: z.enum(["inventory", "summary", "standard", "full"]).optional().default("summary"),
148
+ },
149
+ annotations: { readOnlyHint: true },
138
150
  }, async ({ verbosity }) => {
139
151
  const conn = getConnector(bridge);
140
152
  const raw = await conn.getVariablesFromPluginUI();
@@ -162,99 +174,155 @@ export async function main() {
162
174
  return { content: [{ type: "text", text: JSON.stringify(out, null, 0) }] };
163
175
  });
164
176
  // ---- figma_get_component ----
165
- server.tool("figma_get_component", "Get component metadata by node ID from the open Figma file. No REST API. Use figma_get_file_data or figma_search_components to find nodeIds.", { nodeId: z.string() }, async ({ nodeId }) => {
177
+ server.registerTool("figma_get_component", {
178
+ description: "Get component metadata by node ID from the open Figma file. No REST API. Use figma_get_file_data or figma_search_components to find nodeIds.",
179
+ inputSchema: { nodeId: z.string() },
180
+ annotations: { readOnlyHint: true },
181
+ }, async ({ nodeId }) => {
166
182
  const conn = getConnector(bridge);
167
183
  const result = await conn.getComponentFromPluginUI(nodeId);
168
184
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
169
185
  });
170
186
  // ---- figma_get_styles (plugin only) ----
171
- server.tool("figma_get_styles", "Get local paint, text, and effect styles from the open Figma file. No REST API. Default verbosity=summary for token saving.", { verbosity: z.enum(["summary", "full"]).optional().default("summary") }, async ({ verbosity }) => {
187
+ server.registerTool("figma_get_styles", {
188
+ description: "Get local paint, text, and effect styles from the open Figma file. No REST API. Default verbosity=summary for token saving.",
189
+ inputSchema: { verbosity: z.enum(["summary", "full"]).optional().default("summary") },
190
+ annotations: { readOnlyHint: true },
191
+ }, async ({ verbosity }) => {
172
192
  const conn = getConnector(bridge);
173
193
  const data = await conn.getLocalStyles(verbosity);
174
194
  return { content: [{ type: "text", text: JSON.stringify(data || {}, null, 0) }] };
175
195
  });
176
196
  // ---- figma_execute ----
177
- server.tool("figma_execute", "Run JavaScript in the Figma plugin context. Full Plugin API available (figma.root, figma.createFrame, etc.). No token needed.", {
178
- code: z.string(),
179
- timeout: z.number().optional().default(5000),
197
+ server.registerTool("figma_execute", {
198
+ description: "Run JavaScript in the Figma plugin context. Full Plugin API available (figma.root, figma.createFrame, etc.). No token needed.",
199
+ inputSchema: {
200
+ code: z.string(),
201
+ timeout: z.number().optional().default(5000),
202
+ },
203
+ annotations: { destructiveHint: true },
180
204
  }, async ({ code, timeout }) => {
181
205
  const conn = getConnector(bridge);
182
206
  const result = await conn.executeCodeViaUI(code, timeout);
183
207
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
184
208
  });
185
209
  // ---- figma_capture_screenshot ----
186
- server.tool("figma_capture_screenshot", "Capture screenshot of a node or current view from the plugin. No REST API.", {
187
- nodeId: z.string().optional(),
188
- format: z.enum(["PNG", "JPG"]).optional().default("PNG"),
189
- scale: z.number().optional().default(2),
210
+ server.registerTool("figma_capture_screenshot", {
211
+ description: "Capture screenshot of a node or current view from the plugin. No REST API.",
212
+ inputSchema: {
213
+ nodeId: z.string().optional(),
214
+ format: z.enum(["PNG", "JPG"]).optional().default("PNG"),
215
+ scale: z.number().optional().default(2),
216
+ },
217
+ annotations: { readOnlyHint: true },
190
218
  }, async ({ nodeId, format, scale }) => {
191
219
  const conn = getConnector(bridge);
192
220
  const result = await conn.captureScreenshot(nodeId ?? null, { format, scale });
193
221
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
194
222
  });
195
223
  // ---- figma_set_instance_properties ----
196
- server.tool("figma_set_instance_properties", "Set component instance properties (TEXT, BOOLEAN, VARIANT, etc.).", {
197
- nodeId: z.string(),
198
- properties: z.record(z.union([z.string(), z.boolean()])),
224
+ server.registerTool("figma_set_instance_properties", {
225
+ description: "Set component instance properties (TEXT, BOOLEAN, VARIANT, etc.).",
226
+ inputSchema: {
227
+ nodeId: z.string(),
228
+ properties: z.record(z.union([z.string(), z.boolean()])),
229
+ },
230
+ annotations: { destructiveHint: true },
199
231
  }, async ({ nodeId, properties }) => {
200
232
  const conn = getConnector(bridge);
201
233
  const result = await conn.setInstanceProperties(nodeId, properties);
202
234
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
203
235
  });
204
236
  // ---- Variable CRUD ----
205
- server.tool("figma_update_variable", "Update a variable value in a mode. Get IDs from figma_get_variables.", {
206
- variableId: z.string(),
207
- modeId: z.string(),
208
- value: z.union([z.string(), z.number(), z.boolean()]),
237
+ server.registerTool("figma_update_variable", {
238
+ description: "Update a variable value in a mode. Get IDs from figma_get_variables.",
239
+ inputSchema: {
240
+ variableId: z.string(),
241
+ modeId: z.string(),
242
+ value: z.union([z.string(), z.number(), z.boolean()]),
243
+ },
244
+ annotations: { destructiveHint: true },
209
245
  }, async (p) => {
210
246
  const conn = getConnector(bridge);
211
247
  const result = await conn.updateVariable(p.variableId, p.modeId, p.value);
212
248
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
213
249
  });
214
- server.tool("figma_create_variable", "Create a variable in a collection. Get collectionId from figma_get_variables.", {
215
- name: z.string(),
216
- collectionId: z.string(),
217
- resolvedType: z.enum(["COLOR", "FLOAT", "STRING", "BOOLEAN"]),
218
- options: z.record(z.any()).optional(),
250
+ server.registerTool("figma_create_variable", {
251
+ description: "Create a variable in a collection. Get collectionId from figma_get_variables.",
252
+ inputSchema: {
253
+ name: z.string(),
254
+ collectionId: z.string(),
255
+ resolvedType: z.enum(["COLOR", "FLOAT", "STRING", "BOOLEAN"]),
256
+ options: z.record(z.any()).optional(),
257
+ },
258
+ annotations: { destructiveHint: true },
219
259
  }, async (p) => {
220
260
  const conn = getConnector(bridge);
221
261
  const result = await conn.createVariable(p.name, p.collectionId, p.resolvedType, p.options);
222
262
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
223
263
  });
224
- server.tool("figma_create_variable_collection", "Create a variable collection.", { name: z.string(), options: z.record(z.any()).optional() }, async (p) => {
264
+ server.registerTool("figma_create_variable_collection", {
265
+ description: "Create a variable collection.",
266
+ inputSchema: { name: z.string(), options: z.record(z.any()).optional() },
267
+ annotations: { destructiveHint: true },
268
+ }, async (p) => {
225
269
  const conn = getConnector(bridge);
226
270
  const result = await conn.createVariableCollection(p.name, p.options);
227
271
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
228
272
  });
229
- server.tool("figma_delete_variable", "Delete a variable.", { variableId: z.string() }, async (p) => {
273
+ server.registerTool("figma_delete_variable", {
274
+ description: "Delete a variable.",
275
+ inputSchema: { variableId: z.string() },
276
+ annotations: { destructiveHint: true },
277
+ }, async (p) => {
230
278
  const conn = getConnector(bridge);
231
279
  const result = await conn.deleteVariable(p.variableId);
232
280
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
233
281
  });
234
- server.tool("figma_delete_variable_collection", "Delete a variable collection.", { collectionId: z.string() }, async (p) => {
282
+ server.registerTool("figma_delete_variable_collection", {
283
+ description: "Delete a variable collection.",
284
+ inputSchema: { collectionId: z.string() },
285
+ annotations: { destructiveHint: true },
286
+ }, async (p) => {
235
287
  const conn = getConnector(bridge);
236
288
  const result = await conn.deleteVariableCollection(p.collectionId);
237
289
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
238
290
  });
239
- server.tool("figma_rename_variable", "Rename a variable.", { variableId: z.string(), newName: z.string() }, async (p) => {
291
+ server.registerTool("figma_rename_variable", {
292
+ description: "Rename a variable.",
293
+ inputSchema: { variableId: z.string(), newName: z.string() },
294
+ annotations: { destructiveHint: true },
295
+ }, async (p) => {
240
296
  const conn = getConnector(bridge);
241
297
  const result = await conn.renameVariable(p.variableId, p.newName);
242
298
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
243
299
  });
244
- server.tool("figma_add_mode", "Add a mode to a collection.", { collectionId: z.string(), modeName: z.string() }, async (p) => {
300
+ server.registerTool("figma_add_mode", {
301
+ description: "Add a mode to a collection.",
302
+ inputSchema: { collectionId: z.string(), modeName: z.string() },
303
+ annotations: { destructiveHint: true },
304
+ }, async (p) => {
245
305
  const conn = getConnector(bridge);
246
306
  const result = await conn.addMode(p.collectionId, p.modeName);
247
307
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
248
308
  });
249
- server.tool("figma_rename_mode", "Rename a mode in a collection.", { collectionId: z.string(), modeId: z.string(), newName: z.string() }, async (p) => {
309
+ server.registerTool("figma_rename_mode", {
310
+ description: "Rename a mode in a collection.",
311
+ inputSchema: { collectionId: z.string(), modeId: z.string(), newName: z.string() },
312
+ annotations: { destructiveHint: true },
313
+ }, async (p) => {
250
314
  const conn = getConnector(bridge);
251
315
  const result = await conn.renameMode(p.collectionId, p.modeId, p.newName);
252
316
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
253
317
  });
254
318
  // ---- Design system summary (minimal tokens) ----
255
- server.tool("figma_get_design_system_summary", "Get a compact overview: variable collection names and component counts. Minimal tokens. No REST API. Uses currentPageOnly: true by default to avoid timeout on large files (SUI); set currentPageOnly: false to scan entire file.", {
256
- currentPageOnly: z.boolean().optional().default(true),
257
- limit: z.number().min(0).optional(),
319
+ server.registerTool("figma_get_design_system_summary", {
320
+ description: "Get a compact overview: variable collection names and component counts. Minimal tokens. No REST API. Uses currentPageOnly: true by default to avoid timeout on large files (SUI); set currentPageOnly: false to scan entire file.",
321
+ inputSchema: {
322
+ currentPageOnly: z.boolean().optional().default(true),
323
+ limit: z.number().min(0).optional(),
324
+ },
325
+ annotations: { readOnlyHint: true },
258
326
  }, async ({ currentPageOnly, limit }) => {
259
327
  const conn = getConnector(bridge);
260
328
  const [vars, components] = await Promise.all([
@@ -273,10 +341,14 @@ export async function main() {
273
341
  return { content: [{ type: "text", text: JSON.stringify(out, null, 0) }] };
274
342
  });
275
343
  // ---- figma_search_components ----
276
- server.tool("figma_search_components", "Search local components by name. Returns nodeIds and names. No REST API. Uses currentPageOnly: true by default to avoid timeout on large files; set currentPageOnly: false to search entire file.", {
277
- query: z.string().optional(),
278
- currentPageOnly: z.boolean().optional().default(true),
279
- limit: z.number().min(0).optional(),
344
+ server.registerTool("figma_search_components", {
345
+ description: "Search local components by name. Returns nodeIds and names. No REST API. Uses currentPageOnly: true by default to avoid timeout on large files; set currentPageOnly: false to search entire file.",
346
+ inputSchema: {
347
+ query: z.string().optional(),
348
+ currentPageOnly: z.boolean().optional().default(true),
349
+ limit: z.number().min(0).optional(),
350
+ },
351
+ annotations: { readOnlyHint: true },
280
352
  }, async ({ query, currentPageOnly, limit }) => {
281
353
  const conn = getConnector(bridge);
282
354
  const result = (await conn.getLocalComponents({ currentPageOnly, limit }));
@@ -293,33 +365,49 @@ export async function main() {
293
365
  return { content: [{ type: "text", text: JSON.stringify({ success: true, components: summary }, null, 0) }] };
294
366
  });
295
367
  // ---- Node operations (short list) ----
296
- server.tool("figma_instantiate_component", "Create a component instance. Use componentKey from figma_search_components or nodeId for local components.", {
297
- componentKey: z.string(),
298
- options: z
299
- .object({
300
- nodeId: z.string().optional(),
301
- position: z.object({ x: z.number(), y: z.number() }).optional(),
302
- parentId: z.string().optional(),
303
- overrides: z.record(z.any()).optional(),
304
- })
305
- .optional(),
368
+ server.registerTool("figma_instantiate_component", {
369
+ description: "Create a component instance. Use componentKey from figma_search_components or nodeId for local components.",
370
+ inputSchema: {
371
+ componentKey: z.string(),
372
+ options: z
373
+ .object({
374
+ nodeId: z.string().optional(),
375
+ position: z.object({ x: z.number(), y: z.number() }).optional(),
376
+ parentId: z.string().optional(),
377
+ overrides: z.record(z.any()).optional(),
378
+ })
379
+ .optional(),
380
+ },
381
+ annotations: { destructiveHint: true },
306
382
  }, async (p) => {
307
383
  const conn = getConnector(bridge);
308
384
  const result = await conn.instantiateComponent(p.componentKey, p.options || {});
309
385
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
310
386
  });
311
- server.tool("figma_refresh_variables", "Refresh variables from the file.", {}, async () => {
387
+ server.registerTool("figma_refresh_variables", {
388
+ description: "Refresh variables from the file.",
389
+ inputSchema: {},
390
+ annotations: { readOnlyHint: false, destructiveHint: false },
391
+ }, async () => {
312
392
  const conn = getConnector(bridge);
313
393
  const result = await conn.refreshVariables();
314
394
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
315
395
  });
316
396
  // ---- Console (plugin buffer, no CDP) ----
317
- server.tool("figma_get_console_logs", "Get plugin console logs (log/warn/error) from the F-MCP plugin buffer. No CDP. Limit default 50.", { limit: z.number().min(1).max(200).optional().default(50) }, async ({ limit }) => {
397
+ server.registerTool("figma_get_console_logs", {
398
+ description: "Get plugin console logs (log/warn/error) from the F-MCP plugin buffer. No CDP. Limit default 50.",
399
+ inputSchema: { limit: z.number().min(1).max(200).optional().default(50) },
400
+ annotations: { readOnlyHint: true },
401
+ }, async ({ limit }) => {
318
402
  const conn = getConnector(bridge);
319
403
  const data = await conn.getConsoleLogs(limit);
320
404
  return { content: [{ type: "text", text: JSON.stringify({ success: true, ...data }, null, 0) }] };
321
405
  });
322
- server.tool("figma_watch_console", "Stream new plugin console logs until timeout. Polls the plugin buffer. Timeout default 30s.", { timeoutSeconds: z.number().min(1).max(120).optional().default(30) }, async ({ timeoutSeconds }) => {
406
+ server.registerTool("figma_watch_console", {
407
+ description: "Stream new plugin console logs until timeout. Polls the plugin buffer. Timeout default 30s.",
408
+ inputSchema: { timeoutSeconds: z.number().min(1).max(120).optional().default(30) },
409
+ annotations: { readOnlyHint: true },
410
+ }, async ({ timeoutSeconds }) => {
323
411
  const conn = getConnector(bridge);
324
412
  const deadline = Date.now() + timeoutSeconds * 1000;
325
413
  const seen = new Set();
@@ -339,34 +427,50 @@ export async function main() {
339
427
  content: [{ type: "text", text: JSON.stringify({ success: true, stream, count: stream.length }, null, 0) }],
340
428
  };
341
429
  });
342
- server.tool("figma_clear_console", "Clear the plugin console log buffer.", {}, async () => {
430
+ server.registerTool("figma_clear_console", {
431
+ description: "Clear the plugin console log buffer.",
432
+ inputSchema: {},
433
+ annotations: { destructiveHint: true },
434
+ }, async () => {
343
435
  const conn = getConnector(bridge);
344
436
  await conn.clearConsole();
345
437
  return { content: [{ type: "text", text: JSON.stringify({ success: true, message: "Console cleared" }, null, 0) }] };
346
438
  });
347
439
  // ---- set_description, get_component_image, get_component_for_development ----
348
- server.tool("figma_set_description", "Set description on a component, component set, or style node. Supports markdown (descriptionMarkdown).", {
349
- nodeId: z.string(),
350
- description: z.string(),
351
- descriptionMarkdown: z.string().optional(),
440
+ server.registerTool("figma_set_description", {
441
+ description: "Set description on a component, component set, or style node. Supports markdown (descriptionMarkdown).",
442
+ inputSchema: {
443
+ nodeId: z.string(),
444
+ description: z.string(),
445
+ descriptionMarkdown: z.string().optional(),
446
+ },
447
+ annotations: { destructiveHint: true },
352
448
  }, async (p) => {
353
449
  const conn = getConnector(bridge);
354
450
  const result = await conn.setNodeDescription(p.nodeId, p.description, p.descriptionMarkdown);
355
451
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
356
452
  });
357
- server.tool("figma_get_component_image", "Get screenshot of a node (component/frame). Returns base64 image. No REST API.", {
358
- nodeId: z.string(),
359
- scale: z.number().min(0.5).max(4).optional().default(2),
360
- format: z.enum(["PNG", "JPG"]).optional().default("PNG"),
453
+ server.registerTool("figma_get_component_image", {
454
+ description: "Get screenshot of a node (component/frame). Returns base64 image. No REST API.",
455
+ inputSchema: {
456
+ nodeId: z.string(),
457
+ scale: z.number().min(0.5).max(4).optional().default(2),
458
+ format: z.enum(["PNG", "JPG"]).optional().default("PNG"),
459
+ },
460
+ annotations: { readOnlyHint: true },
361
461
  }, async ({ nodeId, scale, format }) => {
362
462
  const conn = getConnector(bridge);
363
463
  const result = await conn.captureScreenshot(nodeId, { scale, format });
364
464
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
365
465
  });
366
- server.tool("figma_get_component_for_development", "Get component metadata plus base64 screenshot in one call. For design-to-code workflows.", {
367
- nodeId: z.string(),
368
- scale: z.number().min(0.5).max(4).optional().default(2),
369
- format: z.enum(["PNG", "JPG"]).optional().default("PNG"),
466
+ server.registerTool("figma_get_component_for_development", {
467
+ description: "Get component metadata plus base64 screenshot in one call. For design-to-code workflows.",
468
+ inputSchema: {
469
+ nodeId: z.string(),
470
+ scale: z.number().min(0.5).max(4).optional().default(2),
471
+ format: z.enum(["PNG", "JPG"]).optional().default("PNG"),
472
+ },
473
+ annotations: { readOnlyHint: true },
370
474
  }, async ({ nodeId, scale, format }) => {
371
475
  const conn = getConnector(bridge);
372
476
  const [component, screenshot] = await Promise.all([
@@ -378,53 +482,73 @@ export async function main() {
378
482
  return { content: [{ type: "text", text: JSON.stringify(out, null, 0) }] };
379
483
  });
380
484
  // ---- Batch variables & setup_design_tokens & arrange_component_set ----
381
- server.tool("figma_batch_create_variables", "Create up to 100 variables in one call. Each item: collectionId, name, resolvedType (COLOR/FLOAT/STRING/BOOLEAN), value, modeId. Returns created and failed lists.", {
382
- items: z.array(z.object({
383
- collectionId: z.string(),
384
- name: z.string(),
385
- resolvedType: z.enum(["COLOR", "FLOAT", "STRING", "BOOLEAN"]),
386
- value: z.unknown().optional(),
387
- modeId: z.string().optional(),
388
- valuesByMode: z.record(z.unknown()).optional(),
389
- })).max(100),
485
+ server.registerTool("figma_batch_create_variables", {
486
+ description: "Create up to 100 variables in one call. Each item: collectionId, name, resolvedType (COLOR/FLOAT/STRING/BOOLEAN), value, modeId. Returns created and failed lists.",
487
+ inputSchema: {
488
+ items: z.array(z.object({
489
+ collectionId: z.string(),
490
+ name: z.string(),
491
+ resolvedType: z.enum(["COLOR", "FLOAT", "STRING", "BOOLEAN"]),
492
+ value: z.unknown().optional(),
493
+ modeId: z.string().optional(),
494
+ valuesByMode: z.record(z.unknown()).optional(),
495
+ })).max(100),
496
+ },
497
+ annotations: { destructiveHint: true },
390
498
  }, async ({ items }) => {
391
499
  const conn = getConnector(bridge);
392
500
  const result = await conn.batchCreateVariables(items);
393
501
  return { content: [{ type: "text", text: JSON.stringify({ success: true, ...result }, null, 0) }] };
394
502
  });
395
- server.tool("figma_batch_update_variables", "Update up to 100 variables. Each item: variableId, modeId, value. Returns updated and failed lists.", {
396
- items: z.array(z.object({
397
- variableId: z.string(),
398
- modeId: z.string(),
399
- value: z.union([z.string(), z.number(), z.boolean()]),
400
- })).max(100),
503
+ server.registerTool("figma_batch_update_variables", {
504
+ description: "Update up to 100 variables. Each item: variableId, modeId, value. Returns updated and failed lists.",
505
+ inputSchema: {
506
+ items: z.array(z.object({
507
+ variableId: z.string(),
508
+ modeId: z.string(),
509
+ value: z.union([z.string(), z.number(), z.boolean()]),
510
+ })).max(100),
511
+ },
512
+ annotations: { destructiveHint: true },
401
513
  }, async ({ items }) => {
402
514
  const conn = getConnector(bridge);
403
515
  const result = await conn.batchUpdateVariables(items);
404
516
  return { content: [{ type: "text", text: JSON.stringify({ success: true, ...result }, null, 0) }] };
405
517
  });
406
- server.tool("figma_setup_design_tokens", "Atomically create a variable collection + modes + variables. Rollback on any error. Params: collectionName, modes (array), tokens (array of { name, type?, value? or values? }).", {
407
- collectionName: z.string(),
408
- modes: z.array(z.string()).min(1),
409
- tokens: z.array(z.object({
410
- name: z.string(),
411
- type: z.enum(["COLOR", "FLOAT", "STRING", "BOOLEAN"]).optional(),
412
- value: z.unknown().optional(),
413
- values: z.record(z.unknown()).optional(),
414
- })),
518
+ server.registerTool("figma_setup_design_tokens", {
519
+ description: "Atomically create a variable collection + modes + variables. Rollback on any error. Params: collectionName, modes (array), tokens (array of { name, type?, value? or values? }).",
520
+ inputSchema: {
521
+ collectionName: z.string(),
522
+ modes: z.array(z.string()).min(1),
523
+ tokens: z.array(z.object({
524
+ name: z.string(),
525
+ type: z.enum(["COLOR", "FLOAT", "STRING", "BOOLEAN"]).optional(),
526
+ value: z.unknown().optional(),
527
+ values: z.record(z.unknown()).optional(),
528
+ })),
529
+ },
530
+ annotations: { destructiveHint: true },
415
531
  }, async (p) => {
416
532
  const conn = getConnector(bridge);
417
533
  const result = await conn.setupDesignTokens(p);
418
534
  return { content: [{ type: "text", text: JSON.stringify(result, null, 0) }] };
419
535
  });
420
- server.tool("figma_arrange_component_set", "Combine multiple component nodes into one Figma component set (combineAsVariants). Params: nodeIds (array of at least 2 component node IDs). Returns new component set nodeId.", { nodeIds: z.array(z.string()).min(2) }, async ({ nodeIds }) => {
536
+ server.registerTool("figma_arrange_component_set", {
537
+ description: "Combine multiple component nodes into one Figma component set (combineAsVariants). Params: nodeIds (array of at least 2 component node IDs). Returns new component set nodeId.",
538
+ inputSchema: { nodeIds: z.array(z.string()).min(2) },
539
+ annotations: { destructiveHint: true },
540
+ }, async ({ nodeIds }) => {
421
541
  const conn = getConnector(bridge);
422
542
  const result = await conn.arrangeComponentSet(nodeIds);
423
543
  return { content: [{ type: "text", text: JSON.stringify({ success: true, ...result }, null, 0) }] };
424
544
  });
425
545
  // ---- figma_check_design_parity (design–code gap analysis) ----
426
- server.tool("figma_check_design_parity", "Compare Figma design tokens (variables + styles) with code-side tokens. Critical for design-code gap analysis. Returns matching, inFigmaOnly, inCodeOnly, and divergent (same name, different value). Optional codeTokens: JSON string of expected tokens, e.g. {\"primary\": \"#0066cc\", \"spacing.md\": 16} or {\"primary\": {\"value\": \"#0066cc\"}}.", {
427
- codeTokens: z.string().optional(),
546
+ server.registerTool("figma_check_design_parity", {
547
+ description: "Compare Figma design tokens (variables + styles) with code-side tokens. Critical for design-code gap analysis. Returns matching, inFigmaOnly, inCodeOnly, and divergent (same name, different value). Optional codeTokens: JSON string of expected tokens, e.g. {\"primary\": \"#0066cc\", \"spacing.md\": 16} or {\"primary\": {\"value\": \"#0066cc\"}}.",
548
+ inputSchema: {
549
+ codeTokens: z.string().optional(),
550
+ },
551
+ annotations: { readOnlyHint: true },
428
552
  }, async ({ codeTokens }) => {
429
553
  try {
430
554
  const conn = getConnector(bridge);
@@ -546,8 +670,12 @@ export async function main() {
546
670
  }
547
671
  });
548
672
  // ---- figma_get_token_browser (Token Browser – kurulum özel MCP App) ----
549
- server.tool("figma_get_token_browser", "Token Browser: hierarchical view of design tokens for browsing. Returns variable collections with variables and modes, plus paint and text styles. Use for exploring and auditing tokens in the open Figma file. No REST API.", {
550
- verbosity: z.enum(["summary", "full"]).optional().default("summary"),
673
+ server.registerTool("figma_get_token_browser", {
674
+ description: "Token Browser: hierarchical view of design tokens for browsing. Returns variable collections with variables and modes, plus paint and text styles. Use for exploring and auditing tokens in the open Figma file. No REST API.",
675
+ inputSchema: {
676
+ verbosity: z.enum(["summary", "full"]).optional().default("summary"),
677
+ },
678
+ annotations: { readOnlyHint: true },
551
679
  }, async ({ verbosity }) => {
552
680
  try {
553
681
  const conn = getConnector(bridge);
@@ -611,7 +739,11 @@ export async function main() {
611
739
  }
612
740
  });
613
741
  // ---- figma_get_status (plugin-only) ----
614
- server.tool("figma_get_status", "Check if F-MCP ATezer Bridge plugin is connected. No REST API or token.", {}, async () => {
742
+ server.registerTool("figma_get_status", {
743
+ description: "Check if F-MCP ATezer Bridge plugin is connected. No REST API or token.",
744
+ inputSchema: {},
745
+ annotations: { readOnlyHint: true },
746
+ }, async () => {
615
747
  const connected = bridge.isConnected();
616
748
  const msg = connected
617
749
  ? "F-MCP ATezer Bridge plugin is connected. You can use all figma_* tools."