@atezer/figma-mcp-bridge 1.7.29 → 1.7.30
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/CHANGELOG.md +37 -0
- package/README.md +1 -1
- package/dist/core/plugin-bridge-connector.d.ts +6 -0
- package/dist/core/plugin-bridge-connector.d.ts.map +1 -1
- package/dist/core/plugin-bridge-connector.js +13 -0
- package/dist/core/plugin-bridge-connector.js.map +1 -1
- package/dist/core/plugin-bridge-server.d.ts +6 -0
- package/dist/core/plugin-bridge-server.d.ts.map +1 -1
- package/dist/core/plugin-bridge-server.js +22 -1
- package/dist/core/plugin-bridge-server.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/local-plugin-only.d.ts.map +1 -1
- package/dist/local-plugin-only.js +217 -31
- package/dist/local-plugin-only.js.map +1 -1
- package/package.json +1 -1
- package/skills/figma-canvas-ops/SKILL.md +7 -4
- package/skills/fmcp-project-rules/SKILL.md +9 -5
- package/skills/generate-figma-screen/SKILL.md +24 -15
|
@@ -98,6 +98,42 @@ function getErrorHint(category) {
|
|
|
98
98
|
default: return "Hata mesajini kontrol et.";
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
/** Analyze figma_execute code for common mistakes. Returns advisory warnings (never blocks execution). */
|
|
102
|
+
function analyzeCodeForWarnings(code) {
|
|
103
|
+
const warnings = [];
|
|
104
|
+
// 1. FILL before appendChild — must set FILL *after* node is in auto-layout parent
|
|
105
|
+
if (/layoutSizing(?:Horizontal|Vertical)\s*=\s*['"]FILL['"]/i.test(code)) {
|
|
106
|
+
const fillIdx = code.search(/layoutSizing(?:Horizontal|Vertical)\s*=\s*['"]FILL['"]/i);
|
|
107
|
+
const appendIdx = code.indexOf("appendChild");
|
|
108
|
+
if (appendIdx === -1 || fillIdx < appendIdx) {
|
|
109
|
+
warnings.push("layoutSizingHorizontal/Vertical = 'FILL' appendChild'dan ONCE ayarlanmis. " +
|
|
110
|
+
"Oncesinde hata verir. FILL'i appendChild SONRASINA tasi.");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// 2. Sync API usage — should use Async versions
|
|
114
|
+
const syncApis = [
|
|
115
|
+
{ sync: "getLocalPaintStyles(", async: "getLocalPaintStylesAsync(" },
|
|
116
|
+
{ sync: "getLocalTextStyles(", async: "getLocalTextStylesAsync(" },
|
|
117
|
+
{ sync: "getLocalEffectStyles(", async: "getLocalEffectStylesAsync(" },
|
|
118
|
+
{ sync: "getLocalGridStyles(", async: "getLocalGridStylesAsync(" },
|
|
119
|
+
];
|
|
120
|
+
for (const api of syncApis) {
|
|
121
|
+
if (code.includes(api.sync) && !code.includes(api.async)) {
|
|
122
|
+
warnings.push(`Sync API '${api.sync.slice(0, -1)}' tespit edildi. 'await ${api.async.slice(0, -1)}' kullanin — dynamic-page modunda sync API'ler calismaz.`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// 3. Font not loaded before text modification
|
|
126
|
+
if ((/\.characters\s*=/.test(code) || code.includes(".insertCharacters") || code.includes(".deleteCharacters")) &&
|
|
127
|
+
!code.includes("loadFontAsync")) {
|
|
128
|
+
warnings.push("Text icerik degisikligi (characters) tespit edildi, ancak loadFontAsync cagrisi yok. " +
|
|
129
|
+
"Metin degistirmeden once 'await figma.loadFontAsync(node.fontName)' ekleyin.");
|
|
130
|
+
}
|
|
131
|
+
// 4. Sync page assignment — does not work
|
|
132
|
+
if (/figma\.currentPage\s*=/.test(code) && !code.includes("setCurrentPageAsync")) {
|
|
133
|
+
warnings.push("'figma.currentPage = ...' calismaz. 'await figma.setCurrentPageAsync(page)' kullanin.");
|
|
134
|
+
}
|
|
135
|
+
return warnings;
|
|
136
|
+
}
|
|
101
137
|
/** Wrap a tool handler with try-catch to prevent unhandled rejections. */
|
|
102
138
|
function safeToolHandler(handler) {
|
|
103
139
|
return async (params) => {
|
|
@@ -328,7 +364,13 @@ export async function main() {
|
|
|
328
364
|
}));
|
|
329
365
|
// ---- figma_execute ----
|
|
330
366
|
server.registerTool("figma_execute", {
|
|
331
|
-
description: "Run JavaScript in the Figma plugin context. Full Plugin API available. Use fileKey or figmaUrl to target a specific file."
|
|
367
|
+
description: "Run JavaScript in the Figma plugin context. Full Plugin API available. Use fileKey or figmaUrl to target a specific file. " +
|
|
368
|
+
"Common mistakes are detected and returned as _warnings: " +
|
|
369
|
+
"(1) layoutSizingHorizontal/Vertical='FILL' must be set AFTER appendChild, " +
|
|
370
|
+
"(2) use getLocalPaintStylesAsync not getLocalPaintStyles, " +
|
|
371
|
+
"(3) call loadFontAsync before .characters=, " +
|
|
372
|
+
"(4) use setCurrentPageAsync not figma.currentPage=. " +
|
|
373
|
+
"For component instances: use setProperties({...}), NOT findAll(TEXT).",
|
|
332
374
|
inputSchema: {
|
|
333
375
|
figmaUrl: z.string().optional().describe("Figma or FigJam file URL for routing."),
|
|
334
376
|
fileKey: z.string().optional().describe("Target a specific connected file."),
|
|
@@ -345,6 +387,9 @@ export async function main() {
|
|
|
345
387
|
}
|
|
346
388
|
const clampedTimeout = Math.max(3000, Math.min(timeout ?? 15000, 120000));
|
|
347
389
|
invalidateCache();
|
|
390
|
+
// Run static analysis BEFORE execution so warnings are available in ALL response paths
|
|
391
|
+
const codeWarnings = analyzeCodeForWarnings(code);
|
|
392
|
+
const warningsField = codeWarnings.length > 0 ? { _warnings: codeWarnings } : {};
|
|
348
393
|
const startTime = Date.now();
|
|
349
394
|
try {
|
|
350
395
|
const conn = getConnector(bridge, resolveFileKey(figmaUrl, fileKey));
|
|
@@ -366,6 +411,7 @@ export async function main() {
|
|
|
366
411
|
errorCategory: category,
|
|
367
412
|
_metrics: { durationMs, timeoutMs: clampedTimeout },
|
|
368
413
|
hint,
|
|
414
|
+
...warningsField,
|
|
369
415
|
}) }],
|
|
370
416
|
isError: true,
|
|
371
417
|
};
|
|
@@ -373,7 +419,7 @@ export async function main() {
|
|
|
373
419
|
let enriched;
|
|
374
420
|
try {
|
|
375
421
|
enriched = typeof result === "object" && result !== null
|
|
376
|
-
? { ...result, _metrics: { durationMs, timeoutMs: clampedTimeout } }
|
|
422
|
+
? { ...result, _metrics: { durationMs, timeoutMs: clampedTimeout }, ...warningsField }
|
|
377
423
|
: result;
|
|
378
424
|
}
|
|
379
425
|
catch {
|
|
@@ -399,6 +445,7 @@ export async function main() {
|
|
|
399
445
|
error: msg,
|
|
400
446
|
_metrics: { durationMs, timeoutMs: clampedTimeout },
|
|
401
447
|
hint,
|
|
448
|
+
...warningsField,
|
|
402
449
|
}) }],
|
|
403
450
|
isError: true,
|
|
404
451
|
};
|
|
@@ -581,7 +628,9 @@ export async function main() {
|
|
|
581
628
|
}));
|
|
582
629
|
// ---- Node operations (short list) ----
|
|
583
630
|
server.registerTool("figma_instantiate_component", {
|
|
584
|
-
description: "Create a component instance. Use componentKey from figma_search_components or
|
|
631
|
+
description: "Create a component instance. Use componentKey from figma_search_components, figma_search_assets, or REST API. " +
|
|
632
|
+
"Supports library components (importComponentByKeyAsync) and local components (by nodeId). " +
|
|
633
|
+
"After creation: use overrides with setProperties({...}) for component properties — do NOT use findAll(TEXT) to modify instance text.",
|
|
585
634
|
inputSchema: {
|
|
586
635
|
componentKey: z.string(),
|
|
587
636
|
options: z
|
|
@@ -1071,14 +1120,16 @@ export async function main() {
|
|
|
1071
1120
|
}
|
|
1072
1121
|
});
|
|
1073
1122
|
server.registerTool("figma_create_text", {
|
|
1074
|
-
description: "Create a new text node on the current page. Returns the created node ID."
|
|
1123
|
+
description: "Create a new text node on the current page. Returns the created node ID. " +
|
|
1124
|
+
"IMPORTANT: fontFamily defaults to 'Inter' — if using a design system (e.g. SUI uses SHBGrotesk), specify the DS font. " +
|
|
1125
|
+
"For DS text with proper token binding, prefer figma_execute with importStyleByKeyAsync + setTextStyleIdAsync instead.",
|
|
1075
1126
|
inputSchema: {
|
|
1076
1127
|
text: z.string().describe("Text content"),
|
|
1077
1128
|
x: z.number().optional().default(0),
|
|
1078
1129
|
y: z.number().optional().default(0),
|
|
1079
1130
|
name: z.string().optional().describe("Node name (default: text content)"),
|
|
1080
1131
|
fontSize: z.number().optional().default(16),
|
|
1081
|
-
fontFamily: z.string().optional().default("Inter"),
|
|
1132
|
+
fontFamily: z.string().optional().default("Inter").describe("Font family — defaults to Inter. Specify DS font if using a design system (e.g. SHBGrotesk for SUI)."),
|
|
1082
1133
|
fontStyle: z.string().optional().default("Regular"),
|
|
1083
1134
|
fillColor: z.string().optional().describe("Text color hex e.g. '#000000'"),
|
|
1084
1135
|
parentId: z.string().optional().describe("Parent node ID"),
|
|
@@ -1226,36 +1277,171 @@ export async function main() {
|
|
|
1226
1277
|
});
|
|
1227
1278
|
// ---- figma_search_assets (team library search via plugin) ----
|
|
1228
1279
|
server.registerTool("figma_search_assets", {
|
|
1229
|
-
description: "Search for
|
|
1230
|
-
"
|
|
1280
|
+
description: "Search for team library variable collections (with import keys) and file-local components/component sets. " +
|
|
1281
|
+
"Variables come from enabled team libraries via figma.teamLibrary API. " +
|
|
1282
|
+
"Components are file-local only — for remote library component discovery, use figma_rest_api GET /v1/files/{fileKey}/components. " +
|
|
1283
|
+
"Pass assetTypes to filter: ['variables'], ['components'], or both (default).",
|
|
1231
1284
|
inputSchema: {
|
|
1285
|
+
figmaUrl: z.string().optional().describe("Figma or FigJam file URL for routing."),
|
|
1286
|
+
fileKey: z.string().optional().describe("Target a specific connected file."),
|
|
1232
1287
|
query: z.string().optional().describe("Search query to filter by name"),
|
|
1288
|
+
assetTypes: z.array(z.string()).optional().describe("Asset types to search: 'variables', 'components'. Default: both."),
|
|
1289
|
+
limit: z.number().min(1).max(80).optional().describe("Max results per asset type (default 25, max 80)"),
|
|
1290
|
+
currentPageOnly: z.boolean().optional().describe("For components: search current page only (default true)"),
|
|
1233
1291
|
},
|
|
1234
1292
|
annotations: { readOnlyHint: true },
|
|
1235
|
-
}, async ({ query }) => {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1293
|
+
}, safeToolHandler(async ({ figmaUrl, fileKey, query, assetTypes, limit, currentPageOnly }) => {
|
|
1294
|
+
const conn = getConnector(bridge, resolveFileKey(figmaUrl, fileKey));
|
|
1295
|
+
const result = await conn.searchLibraryAssets({
|
|
1296
|
+
query: query || undefined,
|
|
1297
|
+
assetTypes: assetTypes?.length ? assetTypes : undefined,
|
|
1298
|
+
limit: limit ?? undefined,
|
|
1299
|
+
currentPageOnly,
|
|
1300
|
+
});
|
|
1301
|
+
const data = result;
|
|
1302
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true, ...data }) }] };
|
|
1303
|
+
}));
|
|
1304
|
+
// ---- figma_get_library_variables (team library variable discovery with import keys) ----
|
|
1305
|
+
server.registerTool("figma_get_library_variables", {
|
|
1306
|
+
description: "List variables from team library collections with import keys. " +
|
|
1307
|
+
"Uses figma.teamLibrary API — works in the TARGET file, no need to connect the DS source file. " +
|
|
1308
|
+
"Returns variable name, key (for importVariableByKeyAsync), resolvedType, collection, and library name. " +
|
|
1309
|
+
"Use the returned keys with figma_bind_variable or figma.variables.importVariableByKeyAsync() in figma_execute.",
|
|
1310
|
+
inputSchema: {
|
|
1311
|
+
figmaUrl: z.string().optional().describe("Figma file URL for routing."),
|
|
1312
|
+
fileKey: z.string().optional().describe("Target a specific connected file."),
|
|
1313
|
+
query: z.string().optional().describe("Filter variables by name (case-insensitive contains)"),
|
|
1314
|
+
collectionName: z.string().optional().describe("Filter by collection name (exact match)"),
|
|
1315
|
+
libraryName: z.string().optional().describe("Filter by library name (exact match, e.g. '❖ SUI')"),
|
|
1316
|
+
limit: z.number().min(1).max(500).optional().describe("Max results (default 100)"),
|
|
1317
|
+
},
|
|
1318
|
+
annotations: { readOnlyHint: true },
|
|
1319
|
+
}, safeToolHandler(async ({ figmaUrl, fileKey, query, collectionName, libraryName, limit }) => {
|
|
1320
|
+
const conn = getConnector(bridge, resolveFileKey(figmaUrl, fileKey));
|
|
1321
|
+
const maxResults = limit ?? 100;
|
|
1322
|
+
const q = query ? query.toLowerCase() : "";
|
|
1323
|
+
const code = `
|
|
1324
|
+
if (!figma.teamLibrary) return { success: false, error: "teamLibrary API not available" };
|
|
1325
|
+
var cols = await figma.teamLibrary.getAvailableLibraryVariableCollectionsAsync();
|
|
1326
|
+
var filtered = cols;
|
|
1327
|
+
${collectionName ? `filtered = filtered.filter(function(c) { return c.name === ${JSON.stringify(collectionName)}; });` : ""}
|
|
1328
|
+
${libraryName ? `filtered = filtered.filter(function(c) { return c.libraryName === ${JSON.stringify(libraryName)}; });` : ""}
|
|
1329
|
+
var results = [];
|
|
1330
|
+
for (var ci = 0; ci < filtered.length && results.length < ${maxResults}; ci++) {
|
|
1331
|
+
var col = filtered[ci];
|
|
1332
|
+
var vars = await figma.teamLibrary.getVariablesInLibraryCollectionAsync(col.key);
|
|
1333
|
+
for (var vi = 0; vi < vars.length && results.length < ${maxResults}; vi++) {
|
|
1334
|
+
var v = vars[vi];
|
|
1335
|
+
var nm = (v.name || "").toLowerCase();
|
|
1336
|
+
if (!${JSON.stringify(q)} || nm.indexOf(${JSON.stringify(q)}) >= 0) {
|
|
1337
|
+
results.push({ name: v.name, key: v.key, resolvedType: v.resolvedType, collection: col.name, library: col.libraryName });
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return { success: true, count: results.length, variables: results };
|
|
1342
|
+
`;
|
|
1343
|
+
const result = await conn.executeCodeViaUI(code, 30000);
|
|
1344
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
1345
|
+
}));
|
|
1346
|
+
// ---- figma_bind_variable (import variable and bind to node property) ----
|
|
1347
|
+
server.registerTool("figma_bind_variable", {
|
|
1348
|
+
description: "Import a library variable by key and bind it to a node property. " +
|
|
1349
|
+
"For colors: binds to fills or strokes via setBoundVariableForPaint. " +
|
|
1350
|
+
"For spacing/sizing: binds via setBoundVariable (paddingLeft, itemSpacing, cornerRadius, etc.). " +
|
|
1351
|
+
"Get variableKey from figma_get_library_variables. " +
|
|
1352
|
+
"The node's fill/spacing will dynamically update when the DS token changes.",
|
|
1353
|
+
inputSchema: {
|
|
1354
|
+
figmaUrl: z.string().optional().describe("Figma file URL for routing."),
|
|
1355
|
+
fileKey: z.string().optional().describe("Target a specific connected file."),
|
|
1356
|
+
nodeId: z.string().describe("Target node ID"),
|
|
1357
|
+
variableKey: z.string().describe("Variable import key from figma_get_library_variables"),
|
|
1358
|
+
property: z.enum([
|
|
1359
|
+
"fills", "strokes",
|
|
1360
|
+
"paddingLeft", "paddingRight", "paddingTop", "paddingBottom",
|
|
1361
|
+
"itemSpacing", "counterAxisSpacing",
|
|
1362
|
+
"topLeftRadius", "topRightRadius", "bottomLeftRadius", "bottomRightRadius", "cornerRadius",
|
|
1363
|
+
"strokeWeight", "opacity",
|
|
1364
|
+
"width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight",
|
|
1365
|
+
]).describe("Node property to bind the variable to"),
|
|
1366
|
+
paintIndex: z.number().optional().default(0).describe("For fills/strokes: which paint index (default 0)"),
|
|
1367
|
+
},
|
|
1368
|
+
annotations: { destructiveHint: true },
|
|
1369
|
+
}, safeToolHandler(async ({ figmaUrl, fileKey, nodeId, variableKey, property, paintIndex }) => {
|
|
1370
|
+
invalidateCache();
|
|
1371
|
+
const conn = getConnector(bridge, resolveFileKey(figmaUrl, fileKey));
|
|
1372
|
+
const idx = paintIndex ?? 0;
|
|
1373
|
+
const code = `
|
|
1374
|
+
var variable = await figma.variables.importVariableByKeyAsync(${JSON.stringify(variableKey)});
|
|
1375
|
+
var node = await figma.getNodeByIdAsync(${JSON.stringify(nodeId)});
|
|
1376
|
+
if (!node) throw new Error("Node not found: " + ${JSON.stringify(nodeId)});
|
|
1377
|
+
var prop = ${JSON.stringify(property)};
|
|
1378
|
+
if (prop === "fills" || prop === "strokes") {
|
|
1379
|
+
var paints = [];
|
|
1380
|
+
for (var i = 0; i < node[prop].length; i++) paints.push(node[prop][i]);
|
|
1381
|
+
if (!paints[${idx}]) throw new Error("No paint at index ${idx} on " + prop);
|
|
1382
|
+
var boundPaint = figma.variables.setBoundVariableForPaint(paints[${idx}], "color", variable);
|
|
1383
|
+
paints[${idx}] = boundPaint;
|
|
1384
|
+
node[prop] = paints;
|
|
1385
|
+
} else {
|
|
1386
|
+
node.setBoundVariable(prop, variable);
|
|
1387
|
+
}
|
|
1388
|
+
return { success: true, nodeId: ${JSON.stringify(nodeId)}, property: prop, variableName: variable.name, variableId: variable.id };
|
|
1389
|
+
`;
|
|
1390
|
+
const result = await conn.executeCodeViaUI(code, 10000);
|
|
1391
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
1392
|
+
}));
|
|
1393
|
+
// ---- figma_import_style (import text/paint/effect style from library) ----
|
|
1394
|
+
server.registerTool("figma_import_style", {
|
|
1395
|
+
description: "Import a text, paint, or effect style from a team library by key, and optionally apply it to a node. " +
|
|
1396
|
+
"IMPORTANT: This API only imports PUBLISHED LIBRARY styles, NOT local file styles. " +
|
|
1397
|
+
"For local styles, use 'node.fillStyleId = style.id' (or textStyleId/effectStyleId) directly via figma_execute. " +
|
|
1398
|
+
"Get library style keys from .claude/libraries/ cache or REST API: figma_rest_api GET /v1/files/{fileKey}/styles. " +
|
|
1399
|
+
"For TEXT styles: applies via setTextStyleIdAsync (includes font, size, weight). " +
|
|
1400
|
+
"For PAINT styles: applies via fillStyleId. For EFFECT styles: applies via effectStyleId.",
|
|
1401
|
+
inputSchema: {
|
|
1402
|
+
figmaUrl: z.string().optional().describe("Figma file URL for routing."),
|
|
1403
|
+
fileKey: z.string().optional().describe("Target a specific connected file."),
|
|
1404
|
+
styleKey: z.string().describe("Library style key (must be from a PUBLISHED team library, not a local style)"),
|
|
1405
|
+
nodeId: z.string().optional().describe("Node ID to apply the style to (optional — omit to just import)"),
|
|
1406
|
+
},
|
|
1407
|
+
annotations: { destructiveHint: true },
|
|
1408
|
+
}, safeToolHandler(async ({ figmaUrl, fileKey, styleKey, nodeId }) => {
|
|
1409
|
+
invalidateCache();
|
|
1410
|
+
const conn = getConnector(bridge, resolveFileKey(figmaUrl, fileKey));
|
|
1411
|
+
const code = `
|
|
1412
|
+
var style;
|
|
1413
|
+
try {
|
|
1414
|
+
style = await figma.importStyleByKeyAsync(${JSON.stringify(styleKey)});
|
|
1415
|
+
} catch (e) {
|
|
1416
|
+
var origMsg = e && e.message ? e.message : String(e);
|
|
1417
|
+
throw new Error(
|
|
1418
|
+
"importStyleByKeyAsync failed for key '" + ${JSON.stringify(styleKey)} + "'. " +
|
|
1419
|
+
"This API only works with PUBLISHED LIBRARY styles. " +
|
|
1420
|
+
"Local file styles cannot be imported this way — use 'node.fillStyleId/textStyleId/effectStyleId = <localStyleId>' directly via figma_execute. " +
|
|
1421
|
+
"To find library style keys, use REST API: GET /v1/files/{fileKey}/styles. " +
|
|
1422
|
+
"Original error: " + origMsg
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
var applied = false;
|
|
1426
|
+
${nodeId ? `
|
|
1427
|
+
var node = await figma.getNodeByIdAsync(${JSON.stringify(nodeId)});
|
|
1428
|
+
if (!node) throw new Error("Node not found: " + ${JSON.stringify(nodeId)});
|
|
1429
|
+
if (style.type === "TEXT" && node.type === "TEXT") {
|
|
1430
|
+
await node.setTextStyleIdAsync(style.id);
|
|
1431
|
+
applied = true;
|
|
1432
|
+
} else if (style.type === "PAINT") {
|
|
1433
|
+
node.fillStyleId = style.id;
|
|
1434
|
+
applied = true;
|
|
1435
|
+
} else if (style.type === "EFFECT") {
|
|
1436
|
+
node.effectStyleId = style.id;
|
|
1437
|
+
applied = true;
|
|
1438
|
+
}
|
|
1439
|
+
` : ""}
|
|
1440
|
+
return { success: true, styleId: style.id, styleName: style.name, styleType: style.type, applied: applied };
|
|
1441
|
+
`;
|
|
1442
|
+
const result = await conn.executeCodeViaUI(code, 10000);
|
|
1443
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
1444
|
+
}));
|
|
1259
1445
|
// ---- figma_plugin_diagnostics ----
|
|
1260
1446
|
server.registerTool("figma_plugin_diagnostics", {
|
|
1261
1447
|
description: "Get diagnostic info about plugin connection health: uptime, connected clients, " +
|