@mp3wizard/figma-console-mcp 1.32.1 → 1.32.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/cloudflare/core/deep-component-tools.js +1 -1
- package/dist/cloudflare/core/design-code-tools.js +60 -17
- package/dist/cloudflare/core/figma-tools.js +1 -1
- package/dist/cloudflare/core/tokens-tools.js +1 -1
- package/dist/cloudflare/core/write-tools.js +32 -7
- package/dist/cloudflare/index.js +3 -3
- package/dist/core/deep-component-tools.js +1 -1
- package/dist/core/deep-component-tools.js.map +1 -1
- package/dist/core/design-code-tools.d.ts.map +1 -1
- package/dist/core/design-code-tools.js +60 -17
- package/dist/core/design-code-tools.js.map +1 -1
- package/dist/core/figma-tools.js +1 -1
- package/dist/core/figma-tools.js.map +1 -1
- package/dist/core/tokens-tools.js +1 -1
- package/dist/core/write-tools.d.ts.map +1 -1
- package/dist/core/write-tools.js +32 -7
- package/dist/core/write-tools.js.map +1 -1
- package/figma-desktop-bridge/code.js +76 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
> **Your design system as an API.** Model Context Protocol server that bridges design and development—giving AI assistants complete access to Figma for **extraction**, **creation**, **debugging**, and **bidirectional token sync**.
|
|
10
10
|
|
|
11
|
-
> **🆕
|
|
11
|
+
> **🆕 Component docs now name your color tokens (v1.32.1):** `figma_generate_component_doc` was documenting colors as raw hex (with a `—` in the Figma Variable column) even when the fills and strokes were bound to variables — while spacing tokens documented correctly. It now resolves color **and** spacing variable names through the Desktop Bridge (Plugin API, so it works on every Figma plan — not just Enterprise), and the States, Color Tokens, and Spacing tables all render real token names like `color/content/default` and `spacing/1`. Thanks to Robin Di Capua for catching it. [See what's new →](CHANGELOG.md#1321---2026-06-30)
|
|
12
12
|
|
|
13
13
|
## What is this?
|
|
14
14
|
|
|
@@ -808,9 +808,10 @@ The architecture supports adding new apps with minimal boilerplate — each app
|
|
|
808
808
|
|
|
809
809
|
## 🛤️ Roadmap
|
|
810
810
|
|
|
811
|
-
**Current Status:** v1.32.
|
|
811
|
+
**Current Status:** v1.32.1 (Stable) - Production-ready. Latest fix: `figma_generate_component_doc` now resolves color **and** spacing variable names in generated docs (it was emitting raw hex for variable-bound colors). Built on WCAG-accurate accessibility auditing (line height below 1.5× is no longer mis-flagged as a failure; readability hints decoupled from conformance checks and scoped to multi-line text; code-side WCAG 1.4.12 check), a self-healing Desktop Bridge connection (zombie-process reaper + auto-reconnect watchdog — fixes the recurring "not connected until restart" bug), native variable binding on fills/strokes + typography control in the write tools, shared-library inspection (key-based component resolution + library variable read/import without Enterprise plan), 10-format token export pipeline (DTCG, CSS, Tailwind v4, Tailwind v3, SCSS, TS module, JSON flat/nested, Style Dictionary v3, Tokens Studio), bidirectional Figma↔code token sync, version history & time-series awareness, FigJam + Slides support, Cloud Write Relay, Design System Kit, WebSocket-only connectivity, smart multi-file tracking, **106 tools** (Local) / **95 tools** (Cloud) / **9 tools** (Remote read-only), Comments API, cross-MCP identity disambiguation, and MCP Apps.
|
|
812
812
|
|
|
813
813
|
**Recent Releases:**
|
|
814
|
+
- [x] **v1.32.1** - Documentation-generator fix reported by Robin Di Capua: `figma_generate_component_doc` documented **colors** as raw hex (with `—` in the Figma Variable column) even when fills/strokes were bound to variables, while spacing tokens documented correctly. Two root causes — an id→name lookup that read the wrong keys (`.id`/`.name` instead of `variableId`/`variableName`), and variable names only ever being sourced from the Enterprise-only REST `/variables/local` endpoint (403 elsewhere). The generator now resolves names via the Desktop Bridge Plugin API (works on every plan) and threads them through the States, Color Tokens, and Spacing tables, so real token names like `color/content/default` and `spacing/1` appear. No new tools, no arg-shape changes, no plugin re-import required. 1203 tests passing.
|
|
814
815
|
- [x] **v1.32.0** - Accessibility-audit correctness fix reported by Isabella (a11y collaborator): `figma_lint_design` was flagging line height below 1.5× as an accessibility failure on hundreds of components. That misreads **WCAG 1.4.12 Text Spacing**, which requires content to *support* user spacing overrides without loss — not that designs *ship* at 1.5× — so a sub-1.5 line height is not a conformance failure. Line/paragraph-spacing checks are now scoped to multi-line text (single-line labels and buttons exempt); readability hints (`text-size`, `line-height`, `letter-spacing`, `paragraph-spacing`) are decoupled from the `wcag` group into an opt-in `best-practice` group, so the default audit (`['wcag','design-system','layout']`) and `rules: ['wcag']` return genuine conformance only; and a new code-side `text-spacing-support` advisory in `figma_scan_code_accessibility` flags fixed-px typography — where 1.4.12/1.4.4 are actually verifiable. No new tools, no arg-shape changes; **plugin re-import required** to pick up the new audit behavior (bridge protocol unchanged, so an un-updated plugin stays compatible). 1196 tests passing.
|
|
815
816
|
- [x] **v1.31.0** - Fixes the most-reported reliability bug: the Desktop Bridge connection dropping and staying down until you closed the plugin, restarted your MCP client, or killed ports by hand. Root cause was **zombie MCP server processes** squatting the WebSocket port range (9223–9232) after a bad shutdown. The reaper now escalates `SIGTERM` → `SIGKILL` (a hung server that ignores graceful shutdown can no longer survive), sweeps the range every 5 minutes via an `unref`'d periodic reaper, and a shutdown backstop prevents a server from zombifying in the first place. The redesigned Desktop Bridge plugin adds an auto-reconnect watchdog (re-probes every ~12s while disconnected), a context-aware **Pause / Resume / Reconnect** button, and a live server-count badge. No new tools; **plugin re-import required** (bridge `ui.html` + `code.js` changed). 1190 tests passing, including an integration test that spawns a real `SIGTERM`-ignoring process and asserts the reaper kills it.
|
|
816
817
|
- [x] **v1.30.0** - Native variable binding + typography in the structured write tools, closing the Plugin API gaps that used to force raw `figma_execute`. `figma_set_fills` / `figma_set_strokes` accept a `variableId` to bind a fill/stroke to a color variable via `setBoundVariableForPaint` (any plan, via the bridge). `figma_set_text` gains `fontFamily` / `fontStyle` with space-insensitive normalization (`SemiBold` → `Semi Bold`) and graceful `Regular` fallback. `figma_instantiate_component` pre-loads instance text fonts before applying overrides (fixes silently-skipped text overrides on non-Regular weights) and returns a `warnings` array for failed overrides. Also fixes a mixed-font crash in `figma_set_text` and a `ui.html` relay that was dropping new message fields. No new tools; **plugin re-import required** (bridge `ui.html` + `code.js` changed). Validated live; 1185 tests passing.
|
|
@@ -78,7 +78,7 @@ export function registerDeepComponentTools(server, getDesktopConnector) {
|
|
|
78
78
|
// -----------------------------------------------------------------------
|
|
79
79
|
// Tool: figma_analyze_component_set
|
|
80
80
|
// -----------------------------------------------------------------------
|
|
81
|
-
server.tool("figma_analyze_component_set", "Analyze a Figma COMPONENT_SET to extract variant state machine and cross-variant diffs for code generation. Returns: (1) variant axes (size, state) with all values, (2) CSS pseudo-class mappings for interaction states (hover→:hover, focus→:focus-visible, disabled→:disabled, error→[aria-invalid]), (3) visual diff from default state per variant (only changed properties — fill token, stroke token, stroke weight, text color, opacity, effects, visibility), (4) component property definitions mapped to code props (BOOLEAN→boolean, TEXT→string, INSTANCE_SWAP→slot/ReactNode). Use this on the parent COMPONENT_SET node, not individual variants. Requires Desktop Bridge plugin.", {
|
|
81
|
+
server.tool("figma_analyze_component_set", "Analyze a Figma COMPONENT_SET to extract variant state machine and cross-variant diffs for code generation. Returns: (1) variant axes (size, state) with all values, (2) CSS pseudo-class mappings for interaction states (hover→:hover, focus→:focus-visible, disabled→:disabled, error→[aria-invalid]), (3) visual diff from default state per variant (only changed properties — fill token, stroke token, stroke weight, text color, opacity, effects, visibility), (4) component property definitions mapped to code props (BOOLEAN→boolean, TEXT→string, INSTANCE_SWAP→slot/ReactNode), and (5) slots — Figma SLOT properties (name, preferredValues = the components a slot accepts, and limitViolations) to implement as named slots / children props (React {children} or named ReactNode; Web Components <slot name>). Use this on the parent COMPONENT_SET node, not individual variants. Requires Desktop Bridge plugin.", {
|
|
82
82
|
nodeId: z
|
|
83
83
|
.string()
|
|
84
84
|
.describe("COMPONENT_SET node ID (the parent of all variants, e.g., '214:274')"),
|
|
@@ -477,7 +477,7 @@ function buildAnatomyLines(node, lines, prefix, isLast, depth, maxDepth) {
|
|
|
477
477
|
/**
|
|
478
478
|
* Collect spacing tokens with their bound variable names.
|
|
479
479
|
*/
|
|
480
|
-
function collectSpacingTokens(node) {
|
|
480
|
+
function collectSpacingTokens(node, varNameMap = new Map()) {
|
|
481
481
|
const tokens = [];
|
|
482
482
|
const boundVars = node.boundVariables || {};
|
|
483
483
|
const spacingProps = [
|
|
@@ -493,7 +493,11 @@ function collectSpacingTokens(node) {
|
|
|
493
493
|
const value = node[key];
|
|
494
494
|
if (value !== undefined && value !== null) {
|
|
495
495
|
const varBinding = boundVars[key];
|
|
496
|
-
|
|
496
|
+
// Bound spacing variables expose their id as boundVariables[key].id.
|
|
497
|
+
// Resolve it to a friendly token name (e.g. `spacing/1`) when the caller
|
|
498
|
+
// supplied a name map; fall back to the raw id so the binding stays visible.
|
|
499
|
+
const varId = typeof varBinding?.id === "string" ? varBinding.id : undefined;
|
|
500
|
+
const varName = varId ? varNameMap.get(varId) || varId : undefined;
|
|
497
501
|
tokens.push({
|
|
498
502
|
property: label,
|
|
499
503
|
value,
|
|
@@ -843,20 +847,26 @@ function compareTokens(enrichedData, codeSpec, discrepancies) {
|
|
|
843
847
|
});
|
|
844
848
|
}
|
|
845
849
|
}
|
|
846
|
-
// Cross-reference design variables with code tokens
|
|
850
|
+
// Cross-reference design variables with code tokens.
|
|
851
|
+
// enrichment entries key off variableName (NOT name); reading `.name` here left
|
|
852
|
+
// every comparison undefined (and would throw on .toLowerCase()). Use variableName
|
|
853
|
+
// and skip any entries that never resolved to a real token name.
|
|
847
854
|
if (enrichedData.variables_used && ct.usedTokens) {
|
|
848
|
-
const
|
|
855
|
+
const designTokens = enrichedData.variables_used
|
|
856
|
+
.map((v) => v.variableName)
|
|
857
|
+
.filter((n) => typeof n === "string" && n.length > 0);
|
|
858
|
+
const designTokenNames = designTokens.map((n) => n.toLowerCase());
|
|
849
859
|
const codeTokenNames = ct.usedTokens.map((t) => t.toLowerCase());
|
|
850
|
-
for (const
|
|
851
|
-
const normalizedName =
|
|
860
|
+
for (const tokenName of designTokens) {
|
|
861
|
+
const normalizedName = tokenName.toLowerCase();
|
|
852
862
|
if (!codeTokenNames.some((ct) => ct.includes(normalizedName) || normalizedName.includes(ct))) {
|
|
853
863
|
discrepancies.push({
|
|
854
864
|
category: "tokens",
|
|
855
|
-
property: `token:${
|
|
865
|
+
property: `token:${tokenName}`,
|
|
856
866
|
severity: "minor",
|
|
857
|
-
designValue:
|
|
867
|
+
designValue: tokenName,
|
|
858
868
|
codeValue: null,
|
|
859
|
-
message: `Design uses token "${
|
|
869
|
+
message: `Design uses token "${tokenName}" but code doesn't reference it`,
|
|
860
870
|
suggestion: `Add token reference in code`,
|
|
861
871
|
});
|
|
862
872
|
}
|
|
@@ -1680,13 +1690,16 @@ function deduplicateColors(colors) {
|
|
|
1680
1690
|
}
|
|
1681
1691
|
return Array.from(seen.values());
|
|
1682
1692
|
}
|
|
1683
|
-
function generateVisualSpecsSection(node, enrichedData, variantData) {
|
|
1693
|
+
function generateVisualSpecsSection(node, enrichedData, variantData, varNameMap = new Map()) {
|
|
1684
1694
|
const lines = ["", "## Token Specification", ""];
|
|
1685
|
-
//
|
|
1686
|
-
|
|
1695
|
+
// Fill any gaps in the caller-supplied name map from enrichment data.
|
|
1696
|
+
// enrichment entries key off variableId/variableName (NOT id/name), and only
|
|
1697
|
+
// carry a useful name when it actually resolved (not the raw VariableID).
|
|
1687
1698
|
if (enrichedData?.variables_used) {
|
|
1688
1699
|
for (const v of enrichedData.variables_used) {
|
|
1689
|
-
|
|
1700
|
+
if (v.variableId && v.variableName && v.variableName !== v.variableId && !varNameMap.has(v.variableId)) {
|
|
1701
|
+
varNameMap.set(v.variableId, v.variableName);
|
|
1702
|
+
}
|
|
1690
1703
|
}
|
|
1691
1704
|
}
|
|
1692
1705
|
// Per-variant color token table
|
|
@@ -1740,7 +1753,7 @@ function generateVisualSpecsSection(node, enrichedData, variantData) {
|
|
|
1740
1753
|
}
|
|
1741
1754
|
// Spacing tokens with variable names
|
|
1742
1755
|
const visualNode = resolveVisualNode(node);
|
|
1743
|
-
const spacingTokens = collectSpacingTokens(visualNode);
|
|
1756
|
+
const spacingTokens = collectSpacingTokens(visualNode, varNameMap);
|
|
1744
1757
|
if (spacingTokens.length > 0) {
|
|
1745
1758
|
lines.push("### Spacing Tokens");
|
|
1746
1759
|
lines.push("");
|
|
@@ -2565,11 +2578,41 @@ export function registerDesignCodeTools(server, getFigmaAPI, getCurrentUrl, vari
|
|
|
2565
2578
|
logger.warn("Enrichment failed, proceeding without token data");
|
|
2566
2579
|
}
|
|
2567
2580
|
}
|
|
2568
|
-
// Build variable name lookup for per-variant color
|
|
2581
|
+
// Build variable name lookup (id → token name) for per-variant color
|
|
2582
|
+
// AND spacing collection. Two sources, in order of authority:
|
|
2583
|
+
// 1. Desktop Bridge local variables (Plugin API getLocalVariablesAsync) —
|
|
2584
|
+
// works on EVERY Figma plan and is the only reliable id→name source.
|
|
2585
|
+
// The REST /files/:key/variables/local endpoint is Enterprise-only
|
|
2586
|
+
// (returns 403 on all other plans), so enrichment's variable map is
|
|
2587
|
+
// almost always empty and bound colors would otherwise render as raw
|
|
2588
|
+
// hex / raw VariableIDs.
|
|
2589
|
+
// 2. Enrichment variables_used — fallback for non-bridge (Cloud/Remote)
|
|
2590
|
+
// paths. NOTE: entries key off variableId/variableName (NOT id/name),
|
|
2591
|
+
// and their name is only useful when it actually resolved to a token
|
|
2592
|
+
// name (not the raw VariableID).
|
|
2569
2593
|
const varNameMap = new Map();
|
|
2570
2594
|
if (enrichedData?.variables_used) {
|
|
2571
2595
|
for (const v of enrichedData.variables_used) {
|
|
2572
|
-
|
|
2596
|
+
if (v.variableId && v.variableName && v.variableName !== v.variableId) {
|
|
2597
|
+
varNameMap.set(v.variableId, v.variableName);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
if (getDesktopConnector) {
|
|
2602
|
+
try {
|
|
2603
|
+
const connector = await getDesktopConnector();
|
|
2604
|
+
const varsResult = await connector.getVariables();
|
|
2605
|
+
const varList = varsResult?.variables || varsResult?.result?.variables;
|
|
2606
|
+
if (Array.isArray(varList)) {
|
|
2607
|
+
for (const v of varList) {
|
|
2608
|
+
if (v?.id && v?.name)
|
|
2609
|
+
varNameMap.set(v.id, v.name);
|
|
2610
|
+
}
|
|
2611
|
+
logger.info({ count: varList.length }, "Resolved variable names via Desktop Bridge for docs");
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
catch {
|
|
2615
|
+
logger.warn("Could not load bridge variables for doc token names — colors may fall back to hex");
|
|
2573
2616
|
}
|
|
2574
2617
|
}
|
|
2575
2618
|
// Collect per-variant color/icon data
|
|
@@ -2657,7 +2700,7 @@ export function registerDesignCodeTools(server, getFigmaAPI, getCurrentUrl, vari
|
|
|
2657
2700
|
}
|
|
2658
2701
|
}
|
|
2659
2702
|
if (s.visualSpecs) {
|
|
2660
|
-
parts.push(generateVisualSpecsSection(nodeForVisual, enrichedData, variantData));
|
|
2703
|
+
parts.push(generateVisualSpecsSection(nodeForVisual, enrichedData, variantData, varNameMap));
|
|
2661
2704
|
includedSections.push("visualSpecs");
|
|
2662
2705
|
}
|
|
2663
2706
|
if (s.typography) {
|
|
@@ -2403,7 +2403,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, variab
|
|
|
2403
2403
|
}
|
|
2404
2404
|
});
|
|
2405
2405
|
// Tool 13: Get Component for Development (UI Implementation)
|
|
2406
|
-
server.tool("figma_get_component_for_development", "Get component data optimized for high-fidelity UI implementation. Returns a deep component tree (depth 4) with design tokens (boundVariables), interaction states (reactions), sizing constraints (min/max/layoutSizing), text behavior (autoResize, truncation), and design annotations. Automatically includes 2x rendered image. Use when user asks to: 'build this component', 'implement this in React/Vue', 'generate code for', or needs both visual reference and technical specs for production-quality, accessible, token-aware code. For just metadata/descriptions, use figma_get_component. For just image, use figma_get_component_image. For full annotation details, use figma_get_annotations. To resolve variable IDs to names/values, use figma_get_variables.", {
|
|
2406
|
+
server.tool("figma_get_component_for_development", "Get component data optimized for high-fidelity UI implementation. Returns a deep component tree (depth 4) with design tokens (boundVariables), interaction states (reactions), sizing constraints (min/max/layoutSizing), text behavior (autoResize, truncation), Figma slots (named freeform-content regions on a component, with the components each accepts and any min/max limit violations), and design annotations. Automatically includes 2x rendered image. Use when user asks to: 'build this component', 'implement this in React/Vue', 'generate code for', or needs both visual reference and technical specs for production-quality, accessible, token-aware code. For just metadata/descriptions, use figma_get_component. For just image, use figma_get_component_image. For full annotation details, use figma_get_annotations. To resolve variable IDs to names/values, use figma_get_variables.", {
|
|
2407
2407
|
fileUrl: z
|
|
2408
2408
|
.string()
|
|
2409
2409
|
.url()
|
|
@@ -30,7 +30,7 @@ const logger = createChildLogger({ component: "tokens-tools" });
|
|
|
30
30
|
* on every exported token document. Kept in sync with package.json by
|
|
31
31
|
* scripts/release.sh — see step 3 of the release flow.
|
|
32
32
|
*/
|
|
33
|
-
const MCP_VERSION = "1.32.
|
|
33
|
+
const MCP_VERSION = "1.32.1";
|
|
34
34
|
const EXPORT_TOOL_DESCRIPTION = `Export Figma variables to design token files in your codebase. Bidirectional with figma_import_tokens — together they replace Style Dictionary and Tokens Studio's export pipeline for the popular styling methods.
|
|
35
35
|
|
|
36
36
|
FULLY-IMPLEMENTED OUTPUT FORMATS:
|
|
@@ -76,28 +76,53 @@ Layers: If your code creates helper frames, placeholder nodes, or intermediate l
|
|
|
76
76
|
// VARIABLE MANAGEMENT TOOLS
|
|
77
77
|
// ============================================================================
|
|
78
78
|
// Tool: Update a variable's value
|
|
79
|
-
server.tool("figma_update_variable", "Update a single variable's value. For
|
|
79
|
+
server.tool("figma_update_variable", "Update a single variable's value and/or its description. Pass modeId+value to change the value in a mode; pass description to set the 'How to use this variable' text shown in the Variables panel (and exported as DTCG $description). At least one of {modeId+value} or {description} is required. For many value updates at once, use figma_batch_update_variables (10-50x faster). Use figma_get_variables first for IDs. COLOR: hex '#FF0000', FLOAT: number, STRING: text, BOOLEAN: true/false. Requires Desktop Bridge plugin.", {
|
|
80
80
|
variableId: z
|
|
81
81
|
.string()
|
|
82
82
|
.describe("The variable ID to update (e.g., 'VariableID:123:456'). Get this from figma_get_variables."),
|
|
83
83
|
modeId: z
|
|
84
84
|
.string()
|
|
85
|
-
.
|
|
85
|
+
.optional()
|
|
86
|
+
.describe("The mode ID to update the value in (e.g., '1:0'). Required when updating a value. Get this from the variable's collection modes."),
|
|
86
87
|
value: z
|
|
87
88
|
.union([z.string(), z.number(), z.boolean()])
|
|
88
|
-
.
|
|
89
|
-
|
|
89
|
+
.optional()
|
|
90
|
+
.describe("The new value (requires modeId). For COLOR: hex string like '#FF0000'. For FLOAT: number. For STRING: text. For BOOLEAN: true/false."),
|
|
91
|
+
description: z
|
|
92
|
+
.string()
|
|
93
|
+
.optional()
|
|
94
|
+
.describe("Set the variable's description (the 'How to use this variable' field; exported as DTCG $description). Pass an empty string to clear it."),
|
|
95
|
+
}, async ({ variableId, modeId, value, description }) => {
|
|
90
96
|
try {
|
|
91
97
|
const connector = await getDesktopConnector();
|
|
92
|
-
const
|
|
98
|
+
const hasValueUpdate = modeId !== undefined && value !== undefined;
|
|
99
|
+
const hasDescriptionUpdate = description !== undefined;
|
|
100
|
+
if ((modeId !== undefined) !== (value !== undefined)) {
|
|
101
|
+
throw new Error("Both modeId and value are required together to update a value.");
|
|
102
|
+
}
|
|
103
|
+
if (!hasValueUpdate && !hasDescriptionUpdate) {
|
|
104
|
+
throw new Error("Nothing to update: provide modeId+value to change the value, and/or description to set the description.");
|
|
105
|
+
}
|
|
106
|
+
let variable;
|
|
107
|
+
const updated = [];
|
|
108
|
+
if (hasValueUpdate) {
|
|
109
|
+
const result = await connector.updateVariable(variableId, modeId, value);
|
|
110
|
+
variable = result.variable;
|
|
111
|
+
updated.push("value");
|
|
112
|
+
}
|
|
113
|
+
if (hasDescriptionUpdate) {
|
|
114
|
+
const result = await connector.setVariableDescription(variableId, description);
|
|
115
|
+
variable = result.variable || variable;
|
|
116
|
+
updated.push("description");
|
|
117
|
+
}
|
|
93
118
|
return {
|
|
94
119
|
content: [
|
|
95
120
|
{
|
|
96
121
|
type: "text",
|
|
97
122
|
text: JSON.stringify({
|
|
98
123
|
success: true,
|
|
99
|
-
message: `Variable "${
|
|
100
|
-
variable
|
|
124
|
+
message: `Variable "${variable?.name ?? variableId}" updated successfully (${updated.join(" + ")})`,
|
|
125
|
+
variable,
|
|
101
126
|
timestamp: Date.now(),
|
|
102
127
|
}),
|
|
103
128
|
},
|
package/dist/cloudflare/index.js
CHANGED
|
@@ -74,7 +74,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
|
|
|
74
74
|
this.server = (() => {
|
|
75
75
|
const s = new McpServer({
|
|
76
76
|
name: "Figma Console MCP",
|
|
77
|
-
version: "1.32.
|
|
77
|
+
version: "1.32.1",
|
|
78
78
|
});
|
|
79
79
|
// Identity wrap — every tool's response and thrown error gets stamped
|
|
80
80
|
// with our MCP name so cross-MCP attribution is unambiguous.
|
|
@@ -1083,7 +1083,7 @@ export default {
|
|
|
1083
1083
|
});
|
|
1084
1084
|
const statelessServer = new McpServer({
|
|
1085
1085
|
name: "Figma Console MCP",
|
|
1086
|
-
version: "1.32.
|
|
1086
|
+
version: "1.32.1",
|
|
1087
1087
|
});
|
|
1088
1088
|
wrapServerForIdentity(statelessServer);
|
|
1089
1089
|
// ================================================================
|
|
@@ -1720,7 +1720,7 @@ export default {
|
|
|
1720
1720
|
return new Response(JSON.stringify({
|
|
1721
1721
|
status: "healthy",
|
|
1722
1722
|
service: "Figma Console MCP",
|
|
1723
|
-
version: "1.32.
|
|
1723
|
+
version: "1.32.1",
|
|
1724
1724
|
endpoints: {
|
|
1725
1725
|
mcp: ["/sse", "/mcp"],
|
|
1726
1726
|
oauth_mcp_spec: ["/.well-known/oauth-authorization-server", "/authorize", "/token", "/oauth/register"],
|
|
@@ -78,7 +78,7 @@ export function registerDeepComponentTools(server, getDesktopConnector) {
|
|
|
78
78
|
// -----------------------------------------------------------------------
|
|
79
79
|
// Tool: figma_analyze_component_set
|
|
80
80
|
// -----------------------------------------------------------------------
|
|
81
|
-
server.tool("figma_analyze_component_set", "Analyze a Figma COMPONENT_SET to extract variant state machine and cross-variant diffs for code generation. Returns: (1) variant axes (size, state) with all values, (2) CSS pseudo-class mappings for interaction states (hover→:hover, focus→:focus-visible, disabled→:disabled, error→[aria-invalid]), (3) visual diff from default state per variant (only changed properties — fill token, stroke token, stroke weight, text color, opacity, effects, visibility), (4) component property definitions mapped to code props (BOOLEAN→boolean, TEXT→string, INSTANCE_SWAP→slot/ReactNode). Use this on the parent COMPONENT_SET node, not individual variants. Requires Desktop Bridge plugin.", {
|
|
81
|
+
server.tool("figma_analyze_component_set", "Analyze a Figma COMPONENT_SET to extract variant state machine and cross-variant diffs for code generation. Returns: (1) variant axes (size, state) with all values, (2) CSS pseudo-class mappings for interaction states (hover→:hover, focus→:focus-visible, disabled→:disabled, error→[aria-invalid]), (3) visual diff from default state per variant (only changed properties — fill token, stroke token, stroke weight, text color, opacity, effects, visibility), (4) component property definitions mapped to code props (BOOLEAN→boolean, TEXT→string, INSTANCE_SWAP→slot/ReactNode), and (5) slots — Figma SLOT properties (name, preferredValues = the components a slot accepts, and limitViolations) to implement as named slots / children props (React {children} or named ReactNode; Web Components <slot name>). Use this on the parent COMPONENT_SET node, not individual variants. Requires Desktop Bridge plugin.", {
|
|
82
82
|
nodeId: z
|
|
83
83
|
.string()
|
|
84
84
|
.describe("COMPONENT_SET node ID (the parent of all variants, e.g., '214:274')"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-component-tools.js","sourceRoot":"","sources":["../../src/core/deep-component-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,SAAS,EAAE,sBAAsB,EAAE,CAAC,CAAC;AAExE,MAAM,UAAU,0BAA0B,CACzC,MAAiB,EACjB,mBAAuC;IAEvC,MAAM,CAAC,IAAI,CACV,0CAA0C,EAC1C,sqBAAsqB,EACtqB;QACC,MAAM,EAAE,CAAC;aACP,MAAM,EAAE;aACR,QAAQ,CAAC,gDAAgD,CAAC;QAC5D,KAAK,EAAE,CAAC,CAAC,UAAU,CAClB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAC9C,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CACjC,CAAC,QAAQ,CAAC,wGAAwG,CAAC;KACpH,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,GAAG,EAAE,EAAE,EAAE,EAAE;QAChC,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,2BAA2B,CAAC,CAAC;YAE1E,MAAM,SAAS,GAAG,MAAM,mBAAmB,EAAE,CAAC;YAC9C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,gBAAgB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;YAEtE,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,6BAA6B,CAAC,CAAC;YACjE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC;YAEnC,0CAA0C;YAC1C,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;YAEtD,MAAM,QAAQ,GAAQ;gBACrB,MAAM;gBACN,SAAS,EAAE,IAAI;gBACf,QAAQ,EAAE;oBACT,OAAO,EAAE,4BAA4B;oBACrC,SAAS,EAAE,YAAY;oBACvB,cAAc,EAAE,MAAM;oBACtB,iBAAiB,EAAE,IAAI,CAAC,gBAAgB,IAAI,CAAC;oBAC7C,IAAI,EAAE;wBACL,uDAAuD,YAAY,IAAI;wBACvE,0EAA0E;wBAC1E,6EAA6E;wBAC7E,6EAA6E;wBAC7E,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,mFAAmF,CAAC,CAAC,CAAC,IAAI;qBACzG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;iBAC3B;aACD,CAAC;YAEF,OAAO;gBACN,OAAO,EAAE;oBACR;wBACC,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;qBAC9B;iBACD;aACD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,kCAAkC,CAAC,CAAC;YAE5D,OAAO;gBACN,OAAO,EAAE;oBACR;wBACC,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACpB,KAAK,EAAE,uBAAuB;4BAC9B,OAAO,EAAE,kCAAkC,OAAO,EAAE;4BACpD,IAAI,EAAE,gJAAgJ;yBACtJ,CAAC;qBACF;iBACD;gBACD,OAAO,EAAE,IAAI;aACb,CAAC;QACH,CAAC;IACF,CAAC,CACD,CAAC;IAEF,0EAA0E;IAC1E,oCAAoC;IACpC,0EAA0E;IAC1E,MAAM,CAAC,IAAI,CACV,6BAA6B,EAC7B,
|
|
1
|
+
{"version":3,"file":"deep-component-tools.js","sourceRoot":"","sources":["../../src/core/deep-component-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"design-code-tools.d.ts","sourceRoot":"","sources":["../../src/core/design-code-tools.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"design-code-tools.d.ts","sourceRoot":"","sources":["../../src/core/design-code-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -477,7 +477,7 @@ function buildAnatomyLines(node, lines, prefix, isLast, depth, maxDepth) {
|
|
|
477
477
|
/**
|
|
478
478
|
* Collect spacing tokens with their bound variable names.
|
|
479
479
|
*/
|
|
480
|
-
function collectSpacingTokens(node) {
|
|
480
|
+
function collectSpacingTokens(node, varNameMap = new Map()) {
|
|
481
481
|
const tokens = [];
|
|
482
482
|
const boundVars = node.boundVariables || {};
|
|
483
483
|
const spacingProps = [
|
|
@@ -493,7 +493,11 @@ function collectSpacingTokens(node) {
|
|
|
493
493
|
const value = node[key];
|
|
494
494
|
if (value !== undefined && value !== null) {
|
|
495
495
|
const varBinding = boundVars[key];
|
|
496
|
-
|
|
496
|
+
// Bound spacing variables expose their id as boundVariables[key].id.
|
|
497
|
+
// Resolve it to a friendly token name (e.g. `spacing/1`) when the caller
|
|
498
|
+
// supplied a name map; fall back to the raw id so the binding stays visible.
|
|
499
|
+
const varId = typeof varBinding?.id === "string" ? varBinding.id : undefined;
|
|
500
|
+
const varName = varId ? varNameMap.get(varId) || varId : undefined;
|
|
497
501
|
tokens.push({
|
|
498
502
|
property: label,
|
|
499
503
|
value,
|
|
@@ -843,20 +847,26 @@ function compareTokens(enrichedData, codeSpec, discrepancies) {
|
|
|
843
847
|
});
|
|
844
848
|
}
|
|
845
849
|
}
|
|
846
|
-
// Cross-reference design variables with code tokens
|
|
850
|
+
// Cross-reference design variables with code tokens.
|
|
851
|
+
// enrichment entries key off variableName (NOT name); reading `.name` here left
|
|
852
|
+
// every comparison undefined (and would throw on .toLowerCase()). Use variableName
|
|
853
|
+
// and skip any entries that never resolved to a real token name.
|
|
847
854
|
if (enrichedData.variables_used && ct.usedTokens) {
|
|
848
|
-
const
|
|
855
|
+
const designTokens = enrichedData.variables_used
|
|
856
|
+
.map((v) => v.variableName)
|
|
857
|
+
.filter((n) => typeof n === "string" && n.length > 0);
|
|
858
|
+
const designTokenNames = designTokens.map((n) => n.toLowerCase());
|
|
849
859
|
const codeTokenNames = ct.usedTokens.map((t) => t.toLowerCase());
|
|
850
|
-
for (const
|
|
851
|
-
const normalizedName =
|
|
860
|
+
for (const tokenName of designTokens) {
|
|
861
|
+
const normalizedName = tokenName.toLowerCase();
|
|
852
862
|
if (!codeTokenNames.some((ct) => ct.includes(normalizedName) || normalizedName.includes(ct))) {
|
|
853
863
|
discrepancies.push({
|
|
854
864
|
category: "tokens",
|
|
855
|
-
property: `token:${
|
|
865
|
+
property: `token:${tokenName}`,
|
|
856
866
|
severity: "minor",
|
|
857
|
-
designValue:
|
|
867
|
+
designValue: tokenName,
|
|
858
868
|
codeValue: null,
|
|
859
|
-
message: `Design uses token "${
|
|
869
|
+
message: `Design uses token "${tokenName}" but code doesn't reference it`,
|
|
860
870
|
suggestion: `Add token reference in code`,
|
|
861
871
|
});
|
|
862
872
|
}
|
|
@@ -1680,13 +1690,16 @@ function deduplicateColors(colors) {
|
|
|
1680
1690
|
}
|
|
1681
1691
|
return Array.from(seen.values());
|
|
1682
1692
|
}
|
|
1683
|
-
function generateVisualSpecsSection(node, enrichedData, variantData) {
|
|
1693
|
+
function generateVisualSpecsSection(node, enrichedData, variantData, varNameMap = new Map()) {
|
|
1684
1694
|
const lines = ["", "## Token Specification", ""];
|
|
1685
|
-
//
|
|
1686
|
-
|
|
1695
|
+
// Fill any gaps in the caller-supplied name map from enrichment data.
|
|
1696
|
+
// enrichment entries key off variableId/variableName (NOT id/name), and only
|
|
1697
|
+
// carry a useful name when it actually resolved (not the raw VariableID).
|
|
1687
1698
|
if (enrichedData?.variables_used) {
|
|
1688
1699
|
for (const v of enrichedData.variables_used) {
|
|
1689
|
-
|
|
1700
|
+
if (v.variableId && v.variableName && v.variableName !== v.variableId && !varNameMap.has(v.variableId)) {
|
|
1701
|
+
varNameMap.set(v.variableId, v.variableName);
|
|
1702
|
+
}
|
|
1690
1703
|
}
|
|
1691
1704
|
}
|
|
1692
1705
|
// Per-variant color token table
|
|
@@ -1740,7 +1753,7 @@ function generateVisualSpecsSection(node, enrichedData, variantData) {
|
|
|
1740
1753
|
}
|
|
1741
1754
|
// Spacing tokens with variable names
|
|
1742
1755
|
const visualNode = resolveVisualNode(node);
|
|
1743
|
-
const spacingTokens = collectSpacingTokens(visualNode);
|
|
1756
|
+
const spacingTokens = collectSpacingTokens(visualNode, varNameMap);
|
|
1744
1757
|
if (spacingTokens.length > 0) {
|
|
1745
1758
|
lines.push("### Spacing Tokens");
|
|
1746
1759
|
lines.push("");
|
|
@@ -2565,11 +2578,41 @@ export function registerDesignCodeTools(server, getFigmaAPI, getCurrentUrl, vari
|
|
|
2565
2578
|
logger.warn("Enrichment failed, proceeding without token data");
|
|
2566
2579
|
}
|
|
2567
2580
|
}
|
|
2568
|
-
// Build variable name lookup for per-variant color
|
|
2581
|
+
// Build variable name lookup (id → token name) for per-variant color
|
|
2582
|
+
// AND spacing collection. Two sources, in order of authority:
|
|
2583
|
+
// 1. Desktop Bridge local variables (Plugin API getLocalVariablesAsync) —
|
|
2584
|
+
// works on EVERY Figma plan and is the only reliable id→name source.
|
|
2585
|
+
// The REST /files/:key/variables/local endpoint is Enterprise-only
|
|
2586
|
+
// (returns 403 on all other plans), so enrichment's variable map is
|
|
2587
|
+
// almost always empty and bound colors would otherwise render as raw
|
|
2588
|
+
// hex / raw VariableIDs.
|
|
2589
|
+
// 2. Enrichment variables_used — fallback for non-bridge (Cloud/Remote)
|
|
2590
|
+
// paths. NOTE: entries key off variableId/variableName (NOT id/name),
|
|
2591
|
+
// and their name is only useful when it actually resolved to a token
|
|
2592
|
+
// name (not the raw VariableID).
|
|
2569
2593
|
const varNameMap = new Map();
|
|
2570
2594
|
if (enrichedData?.variables_used) {
|
|
2571
2595
|
for (const v of enrichedData.variables_used) {
|
|
2572
|
-
|
|
2596
|
+
if (v.variableId && v.variableName && v.variableName !== v.variableId) {
|
|
2597
|
+
varNameMap.set(v.variableId, v.variableName);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
if (getDesktopConnector) {
|
|
2602
|
+
try {
|
|
2603
|
+
const connector = await getDesktopConnector();
|
|
2604
|
+
const varsResult = await connector.getVariables();
|
|
2605
|
+
const varList = varsResult?.variables || varsResult?.result?.variables;
|
|
2606
|
+
if (Array.isArray(varList)) {
|
|
2607
|
+
for (const v of varList) {
|
|
2608
|
+
if (v?.id && v?.name)
|
|
2609
|
+
varNameMap.set(v.id, v.name);
|
|
2610
|
+
}
|
|
2611
|
+
logger.info({ count: varList.length }, "Resolved variable names via Desktop Bridge for docs");
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
catch {
|
|
2615
|
+
logger.warn("Could not load bridge variables for doc token names — colors may fall back to hex");
|
|
2573
2616
|
}
|
|
2574
2617
|
}
|
|
2575
2618
|
// Collect per-variant color/icon data
|
|
@@ -2657,7 +2700,7 @@ export function registerDesignCodeTools(server, getFigmaAPI, getCurrentUrl, vari
|
|
|
2657
2700
|
}
|
|
2658
2701
|
}
|
|
2659
2702
|
if (s.visualSpecs) {
|
|
2660
|
-
parts.push(generateVisualSpecsSection(nodeForVisual, enrichedData, variantData));
|
|
2703
|
+
parts.push(generateVisualSpecsSection(nodeForVisual, enrichedData, variantData, varNameMap));
|
|
2661
2704
|
includedSections.push("visualSpecs");
|
|
2662
2705
|
}
|
|
2663
2706
|
if (s.typography) {
|