@mp3wizard/figma-console-mcp 1.23.1 → 1.27.2
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 +49 -33
- package/dist/cloudflare/core/config.js +0 -8
- package/dist/cloudflare/core/console-monitor.js +3 -3
- package/dist/cloudflare/core/diagnose-tool.js +96 -0
- package/dist/cloudflare/core/figma-tools.js +69 -229
- package/dist/cloudflare/core/identity.js +96 -0
- package/dist/cloudflare/core/tokens/alias-resolver.js +98 -0
- package/dist/cloudflare/core/tokens/config.js +284 -0
- package/dist/cloudflare/core/tokens/figma-converter.js +195 -0
- package/dist/cloudflare/core/tokens/formatters/css-vars.js +329 -0
- package/dist/cloudflare/core/tokens/formatters/dtcg.js +300 -0
- package/dist/cloudflare/core/tokens/formatters/index.js +45 -0
- package/dist/cloudflare/core/tokens/formatters/json.js +7 -0
- package/dist/cloudflare/core/tokens/formatters/less.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/scss.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/stubs.js +11 -0
- package/dist/cloudflare/core/tokens/formatters/style-dictionary-v3.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/tailwind-v3.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/tailwind-v4.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/tokens-studio.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/ts-module.js +4 -0
- package/dist/cloudflare/core/tokens/index.js +15 -0
- package/dist/cloudflare/core/tokens/parsers/css-vars.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/dtcg.js +253 -0
- package/dist/cloudflare/core/tokens/parsers/index.js +138 -0
- package/dist/cloudflare/core/tokens/parsers/json.js +7 -0
- package/dist/cloudflare/core/tokens/parsers/scss.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/stubs.js +13 -0
- package/dist/cloudflare/core/tokens/parsers/style-dictionary-v3.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tailwind-v3.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tailwind-v4.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tokens-studio.js +4 -0
- package/dist/cloudflare/core/tokens/schemas.js +148 -0
- package/dist/cloudflare/core/tokens/transforms/color.js +12 -0
- package/dist/cloudflare/core/tokens/transforms/index.js +29 -0
- package/dist/cloudflare/core/tokens/transforms/size.js +7 -0
- package/dist/cloudflare/core/tokens/types.js +18 -0
- package/dist/cloudflare/core/tokens-tools.js +849 -0
- package/dist/cloudflare/core/version-tools.js +151 -7
- package/dist/cloudflare/core/websocket-server.js +77 -55
- package/dist/cloudflare/index.js +37 -26
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +0 -8
- package/dist/core/config.js.map +1 -1
- package/dist/core/console-monitor.d.ts +2 -2
- package/dist/core/console-monitor.d.ts.map +1 -1
- package/dist/core/console-monitor.js +3 -3
- package/dist/core/console-monitor.js.map +1 -1
- package/dist/core/diagnose-tool.d.ts +33 -0
- package/dist/core/diagnose-tool.d.ts.map +1 -0
- package/dist/core/diagnose-tool.js +97 -0
- package/dist/core/diagnose-tool.js.map +1 -0
- package/dist/core/diff/diff-engine.d.ts +14 -0
- package/dist/core/diff/diff-engine.d.ts.map +1 -1
- package/dist/core/diff/diff-engine.js.map +1 -1
- package/dist/core/figma-connector.d.ts +1 -1
- package/dist/core/figma-connector.d.ts.map +1 -1
- package/dist/core/figma-tools.d.ts +1 -2
- package/dist/core/figma-tools.d.ts.map +1 -1
- package/dist/core/figma-tools.js +69 -229
- package/dist/core/figma-tools.js.map +1 -1
- package/dist/core/identity.d.ts +41 -0
- package/dist/core/identity.d.ts.map +1 -0
- package/dist/core/identity.js +97 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/tokens/alias-resolver.d.ts +40 -0
- package/dist/core/tokens/alias-resolver.d.ts.map +1 -0
- package/dist/core/tokens/alias-resolver.js +99 -0
- package/dist/core/tokens/alias-resolver.js.map +1 -0
- package/dist/core/tokens/config.d.ts +352 -0
- package/dist/core/tokens/config.d.ts.map +1 -0
- package/dist/core/tokens/config.js +285 -0
- package/dist/core/tokens/config.js.map +1 -0
- package/dist/core/tokens/figma-converter.d.ts +81 -0
- package/dist/core/tokens/figma-converter.d.ts.map +1 -0
- package/dist/core/tokens/figma-converter.js +196 -0
- package/dist/core/tokens/figma-converter.js.map +1 -0
- package/dist/core/tokens/formatters/css-vars.d.ts +24 -0
- package/dist/core/tokens/formatters/css-vars.d.ts.map +1 -0
- package/dist/core/tokens/formatters/css-vars.js +330 -0
- package/dist/core/tokens/formatters/css-vars.js.map +1 -0
- package/dist/core/tokens/formatters/dtcg.d.ts +28 -0
- package/dist/core/tokens/formatters/dtcg.d.ts.map +1 -0
- package/dist/core/tokens/formatters/dtcg.js +301 -0
- package/dist/core/tokens/formatters/dtcg.js.map +1 -0
- package/dist/core/tokens/formatters/index.d.ts +30 -0
- package/dist/core/tokens/formatters/index.d.ts.map +1 -0
- package/dist/core/tokens/formatters/index.js +46 -0
- package/dist/core/tokens/formatters/index.js.map +1 -0
- package/dist/core/tokens/formatters/json.d.ts +5 -0
- package/dist/core/tokens/formatters/json.d.ts.map +1 -0
- package/dist/core/tokens/formatters/json.js +8 -0
- package/dist/core/tokens/formatters/json.js.map +1 -0
- package/dist/core/tokens/formatters/less.d.ts +4 -0
- package/dist/core/tokens/formatters/less.d.ts.map +1 -0
- package/dist/core/tokens/formatters/less.js +5 -0
- package/dist/core/tokens/formatters/less.js.map +1 -0
- package/dist/core/tokens/formatters/scss.d.ts +4 -0
- package/dist/core/tokens/formatters/scss.d.ts.map +1 -0
- package/dist/core/tokens/formatters/scss.js +5 -0
- package/dist/core/tokens/formatters/scss.js.map +1 -0
- package/dist/core/tokens/formatters/stubs.d.ts +9 -0
- package/dist/core/tokens/formatters/stubs.d.ts.map +1 -0
- package/dist/core/tokens/formatters/stubs.js +12 -0
- package/dist/core/tokens/formatters/stubs.js.map +1 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.d.ts +4 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.d.ts.map +1 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.js +5 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.js.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v3.d.ts +4 -0
- package/dist/core/tokens/formatters/tailwind-v3.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v3.js +5 -0
- package/dist/core/tokens/formatters/tailwind-v3.js.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v4.d.ts +4 -0
- package/dist/core/tokens/formatters/tailwind-v4.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v4.js +5 -0
- package/dist/core/tokens/formatters/tailwind-v4.js.map +1 -0
- package/dist/core/tokens/formatters/tokens-studio.d.ts +4 -0
- package/dist/core/tokens/formatters/tokens-studio.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tokens-studio.js +5 -0
- package/dist/core/tokens/formatters/tokens-studio.js.map +1 -0
- package/dist/core/tokens/formatters/ts-module.d.ts +4 -0
- package/dist/core/tokens/formatters/ts-module.d.ts.map +1 -0
- package/dist/core/tokens/formatters/ts-module.js +5 -0
- package/dist/core/tokens/formatters/ts-module.js.map +1 -0
- package/dist/core/tokens/index.d.ts +17 -0
- package/dist/core/tokens/index.d.ts.map +1 -0
- package/dist/core/tokens/index.js +16 -0
- package/dist/core/tokens/index.js.map +1 -0
- package/dist/core/tokens/parsers/css-vars.d.ts +3 -0
- package/dist/core/tokens/parsers/css-vars.d.ts.map +1 -0
- package/dist/core/tokens/parsers/css-vars.js +5 -0
- package/dist/core/tokens/parsers/css-vars.js.map +1 -0
- package/dist/core/tokens/parsers/dtcg.d.ts +21 -0
- package/dist/core/tokens/parsers/dtcg.d.ts.map +1 -0
- package/dist/core/tokens/parsers/dtcg.js +254 -0
- package/dist/core/tokens/parsers/dtcg.js.map +1 -0
- package/dist/core/tokens/parsers/index.d.ts +37 -0
- package/dist/core/tokens/parsers/index.d.ts.map +1 -0
- package/dist/core/tokens/parsers/index.js +139 -0
- package/dist/core/tokens/parsers/index.js.map +1 -0
- package/dist/core/tokens/parsers/json.d.ts +4 -0
- package/dist/core/tokens/parsers/json.d.ts.map +1 -0
- package/dist/core/tokens/parsers/json.js +8 -0
- package/dist/core/tokens/parsers/json.js.map +1 -0
- package/dist/core/tokens/parsers/scss.d.ts +3 -0
- package/dist/core/tokens/parsers/scss.d.ts.map +1 -0
- package/dist/core/tokens/parsers/scss.js +5 -0
- package/dist/core/tokens/parsers/scss.js.map +1 -0
- package/dist/core/tokens/parsers/stubs.d.ts +11 -0
- package/dist/core/tokens/parsers/stubs.d.ts.map +1 -0
- package/dist/core/tokens/parsers/stubs.js +14 -0
- package/dist/core/tokens/parsers/stubs.js.map +1 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.d.ts +3 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.d.ts.map +1 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.js +5 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.js.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v3.d.ts +3 -0
- package/dist/core/tokens/parsers/tailwind-v3.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v3.js +5 -0
- package/dist/core/tokens/parsers/tailwind-v3.js.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v4.d.ts +3 -0
- package/dist/core/tokens/parsers/tailwind-v4.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v4.js +5 -0
- package/dist/core/tokens/parsers/tailwind-v4.js.map +1 -0
- package/dist/core/tokens/parsers/tokens-studio.d.ts +3 -0
- package/dist/core/tokens/parsers/tokens-studio.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tokens-studio.js +5 -0
- package/dist/core/tokens/parsers/tokens-studio.js.map +1 -0
- package/dist/core/tokens/schemas.d.ts +152 -0
- package/dist/core/tokens/schemas.d.ts.map +1 -0
- package/dist/core/tokens/schemas.js +149 -0
- package/dist/core/tokens/schemas.js.map +1 -0
- package/dist/core/tokens/transforms/color.d.ts +9 -0
- package/dist/core/tokens/transforms/color.d.ts.map +1 -0
- package/dist/core/tokens/transforms/color.js +13 -0
- package/dist/core/tokens/transforms/color.js.map +1 -0
- package/dist/core/tokens/transforms/index.d.ts +36 -0
- package/dist/core/tokens/transforms/index.d.ts.map +1 -0
- package/dist/core/tokens/transforms/index.js +30 -0
- package/dist/core/tokens/transforms/index.js.map +1 -0
- package/dist/core/tokens/transforms/size.d.ts +7 -0
- package/dist/core/tokens/transforms/size.d.ts.map +1 -0
- package/dist/core/tokens/transforms/size.js +8 -0
- package/dist/core/tokens/transforms/size.js.map +1 -0
- package/dist/core/tokens/types.d.ts +228 -0
- package/dist/core/tokens/types.d.ts.map +1 -0
- package/dist/core/tokens/types.js +19 -0
- package/dist/core/tokens/types.js.map +1 -0
- package/dist/core/tokens-tools.d.ts +42 -0
- package/dist/core/tokens-tools.d.ts.map +1 -0
- package/dist/core/tokens-tools.js +850 -0
- package/dist/core/tokens-tools.js.map +1 -0
- package/dist/core/types/index.d.ts +0 -8
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/version-tools.d.ts +30 -1
- package/dist/core/version-tools.d.ts.map +1 -1
- package/dist/core/version-tools.js +151 -7
- package/dist/core/version-tools.js.map +1 -1
- package/dist/core/websocket-connector.d.ts +1 -1
- package/dist/core/websocket-connector.d.ts.map +1 -1
- package/dist/core/websocket-server.d.ts +47 -3
- package/dist/core/websocket-server.d.ts.map +1 -1
- package/dist/core/websocket-server.js +77 -55
- package/dist/core/websocket-server.js.map +1 -1
- package/dist/local.d.ts +0 -12
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +967 -3406
- package/dist/local.js.map +1 -1
- package/figma-desktop-bridge/code.js +59 -63
- package/figma-desktop-bridge/ui.html +85 -11
- package/package.json +12 -30
- package/figma-desktop-bridge/ui-full.html +0 -1353
|
@@ -79,10 +79,15 @@ const MAX_SCAN_PAGES = 20;
|
|
|
79
79
|
const FIGMA_PAGE_SIZE_MAX = 50;
|
|
80
80
|
// Tool-level cap on max_versions; design brief §4.
|
|
81
81
|
const MAX_VERSIONS_HARD_CAP = 200;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
export function registerVersionTools(server, getFigmaAPI, getCurrentUrl, _options, getCurrentSelectedNodeIds,
|
|
83
|
+
/**
|
|
84
|
+
* v1.25.0: optional metadata-change buffer reader. When wired (local mode),
|
|
85
|
+
* the diff engine consults this to surface description/annotation edits
|
|
86
|
+
* that Figma REST doesn't expose. In cloud mode (no plugin buffer
|
|
87
|
+
* available), this stays undefined and the diff just doesn't surface
|
|
88
|
+
* those edits — but scope_coverage still tells callers about the gap.
|
|
89
|
+
*/
|
|
90
|
+
getMetadataChanges) {
|
|
86
91
|
// Helper: read the current Figma selection as a list of node IDs, or null
|
|
87
92
|
// if no selection getter is wired (cloud mode) or selection is empty.
|
|
88
93
|
const readSelection = () => {
|
|
@@ -91,6 +96,13 @@ export function registerVersionTools(server, getFigmaAPI, getCurrentUrl, _option
|
|
|
91
96
|
const ids = getCurrentSelectedNodeIds();
|
|
92
97
|
return ids && ids.length > 0 ? ids : null;
|
|
93
98
|
};
|
|
99
|
+
// v1.25.0: convert an ISO8601 timestamp to Unix ms. Tolerates missing input.
|
|
100
|
+
const toUnixMs = (iso) => {
|
|
101
|
+
if (!iso)
|
|
102
|
+
return null;
|
|
103
|
+
const t = Date.parse(iso);
|
|
104
|
+
return Number.isFinite(t) ? t : null;
|
|
105
|
+
};
|
|
94
106
|
// -----------------------------------------------------------------------
|
|
95
107
|
// Tool: figma_get_file_versions
|
|
96
108
|
// -----------------------------------------------------------------------
|
|
@@ -435,14 +447,80 @@ export function registerVersionTools(server, getFigmaAPI, getCurrentUrl, _option
|
|
|
435
447
|
}
|
|
436
448
|
const fromMeta = extractFileMeta(fromFile.data, from_version);
|
|
437
449
|
const toMeta = extractFileMeta(toFile.data, to_version);
|
|
450
|
+
// v1.25.0: query the plugin metadata buffer for description/annotation
|
|
451
|
+
// changes within the diff's time window. The buffer is only populated
|
|
452
|
+
// when the Desktop Bridge plugin was connected during the edit — so
|
|
453
|
+
// edits made offline (or before this MCP session started) won't appear.
|
|
454
|
+
// That limit is surfaced in scope_coverage + notes.
|
|
455
|
+
const fromMs = toUnixMs(fromMeta.last_modified) ?? undefined;
|
|
456
|
+
const toMs = toUnixMs(toMeta.last_modified) ?? Date.now();
|
|
457
|
+
let bufferedMetadata = [];
|
|
458
|
+
let metadataBufferAvailable = false;
|
|
459
|
+
let unscopedMetadataChanges = [];
|
|
460
|
+
if (getMetadataChanges) {
|
|
461
|
+
metadataBufferAvailable = true;
|
|
462
|
+
try {
|
|
463
|
+
bufferedMetadata = getMetadataChanges({
|
|
464
|
+
fileKey,
|
|
465
|
+
since: fromMs,
|
|
466
|
+
until: toMs,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
catch (e) {
|
|
470
|
+
logger.warn({ err: e }, "Metadata buffer lookup failed; continuing without metadata changes");
|
|
471
|
+
bufferedMetadata = [];
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// Attach buffer entries to the scoped node diffs whose id matches.
|
|
475
|
+
// Entries that don't match any scoped id surface separately so the
|
|
476
|
+
// caller can still see "the buffer has 3 description edits on nodes
|
|
477
|
+
// you didn't ask about" instead of dropping them on the floor.
|
|
478
|
+
if (bufferedMetadata.length > 0) {
|
|
479
|
+
const scopedById = new Map(scoped.map((n) => [n.node_id, n]));
|
|
480
|
+
const consumed = new Set();
|
|
481
|
+
for (const entry of bufferedMetadata) {
|
|
482
|
+
const target = scopedById.get(entry.node_id);
|
|
483
|
+
if (target) {
|
|
484
|
+
(target.metadata_changes ??= []).push({
|
|
485
|
+
field: entry.field,
|
|
486
|
+
new_value: entry.new_value,
|
|
487
|
+
timestamp: entry.timestamp,
|
|
488
|
+
source: "plugin_buffer",
|
|
489
|
+
});
|
|
490
|
+
target.change_count += 1;
|
|
491
|
+
consumed.add(entry);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
unscopedMetadataChanges = bufferedMetadata.filter((e) => !consumed.has(e));
|
|
495
|
+
}
|
|
438
496
|
const notes = [];
|
|
439
|
-
|
|
497
|
+
const hasScopedNodes = !!(component_ids && component_ids.length > 0);
|
|
498
|
+
if (!hasScopedNodes) {
|
|
440
499
|
notes.push("Only page-structure diff returned. Pass component_ids to get per-component analysis (added/removed children, property changes, binding changes), or have a node selected in Figma when you call.");
|
|
441
500
|
}
|
|
442
501
|
if (usedSelection) {
|
|
443
502
|
notes.push(`Auto-scoped to ${component_ids?.length ?? 0} node(s) from the current Figma selection. Pass component_ids explicitly to override.`);
|
|
444
503
|
}
|
|
504
|
+
// Always-on coverage warnings — the failure mode we're guarding against is
|
|
505
|
+
// the user (or an AI client) believing "no changes found" means "nothing
|
|
506
|
+
// changed," when in fact the change is in a category this tool doesn't
|
|
507
|
+
// track. Better to be loud about limits than silently miss real edits.
|
|
508
|
+
if (hasScopedNodes) {
|
|
509
|
+
notes.push("Component-scoped diff covers the canonical components only. INSTANCES of these components placed elsewhere on the canvas (documentation examples, hero frames, mockups) are NOT diffed. For forensic per-session edits including instance changes, use figma_get_design_changes. To diff a specific instance, pass its node ID explicitly.");
|
|
510
|
+
}
|
|
511
|
+
notes.push("Raw layout/visual properties are NOT tracked. This includes layoutSizingHorizontal/Vertical (hug vs. fill), primaryAxisSizingMode/counterAxisSizingMode, raw paddings/widths/cornerRadius when not bound to a variable, and unbound fills/strokes/effects. This tool surfaces structural deltas (children, property defs, name, description) and variable-BINDING deltas only.");
|
|
445
512
|
notes.push("Variable VALUE history is not retrievable from Figma REST API. Variable definition value changes between these versions are not represented; only binding-reference changes on scoped nodes are detected.");
|
|
513
|
+
// v1.25.0: surface metadata-buffer state. Either "tracked via plugin buffer"
|
|
514
|
+
// (and any limits) or "not tracked at all" (cloud mode / plugin absent).
|
|
515
|
+
if (metadataBufferAvailable) {
|
|
516
|
+
notes.push("Description and annotation changes ARE tracked when the Desktop Bridge plugin was connected during the edit. They appear under each node's metadata_changes[]. Edits made while the plugin was disconnected (or before this MCP session started) WON'T appear in the buffer — the diff will silently miss them.");
|
|
517
|
+
if (bufferedMetadata.length === 0) {
|
|
518
|
+
notes.push("No description or annotation changes were captured by the plugin buffer in this version window. Either no such edits happened, or they occurred while the plugin was disconnected.");
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
notes.push("Description and annotation changes are NOT being tracked — no metadata buffer is wired (typically cloud mode without an active Desktop Bridge connection). For description/annotation visibility, use local mode with the plugin running.");
|
|
523
|
+
}
|
|
446
524
|
if (fetchErrors.length > 0) {
|
|
447
525
|
notes.push(`Failed to fetch ${fetchErrors.length} of ${component_ids?.length ?? 0} requested nodes — see _fetch_errors.`);
|
|
448
526
|
}
|
|
@@ -452,7 +530,7 @@ export function registerVersionTools(server, getFigmaAPI, getCurrentUrl, _option
|
|
|
452
530
|
from: fromMeta,
|
|
453
531
|
to: toMeta,
|
|
454
532
|
page_structure: pageDiff,
|
|
455
|
-
scoped_nodes:
|
|
533
|
+
scoped_nodes: hasScopedNodes ? scoped : undefined,
|
|
456
534
|
summary: {
|
|
457
535
|
page_changes: pageDiff.summary.added + pageDiff.summary.removed + pageDiff.summary.renamed,
|
|
458
536
|
scoped_nodes_requested: component_ids?.length ?? 0,
|
|
@@ -462,6 +540,72 @@ export function registerVersionTools(server, getFigmaAPI, getCurrentUrl, _option
|
|
|
462
540
|
api_calls_made: apiCalls,
|
|
463
541
|
cache_hits: cacheHits,
|
|
464
542
|
},
|
|
543
|
+
// scope_coverage is an always-on, machine-readable summary of what the
|
|
544
|
+
// diff DID and DID NOT examine. AI clients should check this before
|
|
545
|
+
// concluding "nothing else changed." See notes[] for prose warnings.
|
|
546
|
+
scope_coverage: {
|
|
547
|
+
page_structure_diffed: true,
|
|
548
|
+
component_ids_diffed: component_ids ?? [],
|
|
549
|
+
max_depth: 2,
|
|
550
|
+
/**
|
|
551
|
+
* v1.25.0: metadata-buffer state. When the plugin is connected
|
|
552
|
+
* during edits, description/annotation changes ARE tracked.
|
|
553
|
+
* `metadata_buffer.available: false` means the diff is REST-only
|
|
554
|
+
* and description/annotation edits are invisible.
|
|
555
|
+
*/
|
|
556
|
+
metadata_buffer: {
|
|
557
|
+
available: metadataBufferAvailable,
|
|
558
|
+
entries_in_window: bufferedMetadata.length,
|
|
559
|
+
entries_matched_to_scoped_nodes: bufferedMetadata.length - unscopedMetadataChanges.length,
|
|
560
|
+
entries_outside_scope: unscopedMetadataChanges.length,
|
|
561
|
+
},
|
|
562
|
+
tracks: [
|
|
563
|
+
"page structure (added/removed/renamed pages)",
|
|
564
|
+
"component children (added/removed)",
|
|
565
|
+
"componentPropertyDefinitions (added/removed/type/default)",
|
|
566
|
+
"name and description changes on scoped nodes",
|
|
567
|
+
"variable binding references on scoped nodes",
|
|
568
|
+
...(metadataBufferAvailable
|
|
569
|
+
? [
|
|
570
|
+
"component descriptions via plugin session buffer (when plugin connected during edit)",
|
|
571
|
+
"Dev Mode annotations via plugin session buffer (when plugin connected during edit)",
|
|
572
|
+
]
|
|
573
|
+
: []),
|
|
574
|
+
],
|
|
575
|
+
does_not_track: [
|
|
576
|
+
"instances of components on the canvas (unless passed as component_ids)",
|
|
577
|
+
"raw layout properties (layoutSizingHorizontal/Vertical, unbound paddings/widths)",
|
|
578
|
+
"raw visual properties (cornerRadius, unbound fills/strokes/effects, opacity)",
|
|
579
|
+
"variable VALUE changes (Figma REST does not expose this)",
|
|
580
|
+
"style content changes (only style add/remove via component reachability)",
|
|
581
|
+
...(metadataBufferAvailable
|
|
582
|
+
? [
|
|
583
|
+
"description/annotation edits made while the Desktop Bridge plugin was disconnected",
|
|
584
|
+
]
|
|
585
|
+
: [
|
|
586
|
+
"component descriptions (Figma REST omits them; no plugin buffer wired in this mode)",
|
|
587
|
+
"Dev Mode annotations (Figma REST omits them; no plugin buffer wired in this mode)",
|
|
588
|
+
]),
|
|
589
|
+
"canvas frame edits outside scoped components",
|
|
590
|
+
],
|
|
591
|
+
complementary_tools: [
|
|
592
|
+
"figma_get_design_changes — forensic per-session edits including raw property changes on instances",
|
|
593
|
+
"figma_get_variables — current variable state (no history available)",
|
|
594
|
+
"figma_get_styles — current style state",
|
|
595
|
+
"figma_get_component — live description and annotation state for a single node",
|
|
596
|
+
],
|
|
597
|
+
},
|
|
598
|
+
unscoped_metadata_changes: unscopedMetadataChanges.length > 0
|
|
599
|
+
? unscopedMetadataChanges.map((e) => ({
|
|
600
|
+
node_id: e.node_id,
|
|
601
|
+
node_name: e.node_name,
|
|
602
|
+
node_type: e.node_type,
|
|
603
|
+
field: e.field,
|
|
604
|
+
new_value: e.new_value,
|
|
605
|
+
timestamp: e.timestamp,
|
|
606
|
+
source: "plugin_buffer",
|
|
607
|
+
}))
|
|
608
|
+
: undefined,
|
|
465
609
|
notes,
|
|
466
610
|
_fetch_errors: fetchErrors.length > 0 ? fetchErrors : undefined,
|
|
467
611
|
};
|
|
@@ -550,7 +694,7 @@ export function registerVersionTools(server, getFigmaAPI, getCurrentUrl, _option
|
|
|
550
694
|
// -----------------------------------------------------------------------
|
|
551
695
|
// Tool: figma_diff_versions
|
|
552
696
|
// -----------------------------------------------------------------------
|
|
553
|
-
server.tool("figma_diff_versions", "Diff two versions of a Figma file. Always returns a cheap page-structure diff (added/removed/renamed pages, 2 API calls). Pass component_ids to additionally get per-node deep diffs at depth=2 (added/removed children, name/description changes, componentPropertyDefinitions changes for COMPONENT_SETs, boundVariables deltas) — costs 2 API calls per scoped node. Use 'current' for to_version to diff against HEAD.
|
|
697
|
+
server.tool("figma_diff_versions", "Diff two versions of a Figma file. Always returns a cheap page-structure diff (added/removed/renamed pages, 2 API calls). Pass component_ids to additionally get per-node deep diffs at depth=2 (added/removed children, name/description changes, componentPropertyDefinitions changes for COMPONENT_SETs, boundVariables deltas) — costs 2 API calls per scoped node. Use 'current' for to_version to diff against HEAD. v1.25.0: when the Desktop Bridge plugin is connected, description and Dev Mode annotation changes are ALSO tracked via a session buffer and surfaced under `scoped_nodes[].metadata_changes[]` and `unscoped_metadata_changes[]` — Figma REST omits these from version snapshots so they're otherwise invisible. STILL NOT tracked: instances of components on the canvas, raw layout properties (layoutSizingHorizontal/Vertical, unbound paddings/widths), raw visual properties (cornerRadius, unbound fills), variable VALUE changes, style content, and metadata edits made while the plugin was disconnected. See `scope_coverage` and `notes[]` for the full coverage map and complementary tools.", {
|
|
554
698
|
fileUrl: z
|
|
555
699
|
.string()
|
|
556
700
|
.url()
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { WebSocketServer as WSServer, WebSocket } from 'ws';
|
|
16
16
|
import { EventEmitter } from 'events';
|
|
17
17
|
import { createServer as createHttpServer } from 'http';
|
|
18
|
-
import { readFileSync
|
|
18
|
+
import { readFileSync } from 'fs';
|
|
19
19
|
import { join } from 'path';
|
|
20
20
|
import { createChildLogger } from './logger.js';
|
|
21
21
|
import { PACKAGE_ROOT } from './resolve-package-root.js';
|
|
@@ -28,29 +28,6 @@ try {
|
|
|
28
28
|
catch {
|
|
29
29
|
// Non-critical — version will show as 0.0.0
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Load the full plugin UI HTML content that gets served to the bootloader.
|
|
33
|
-
* Falls back to a minimal error page if the file isn't found.
|
|
34
|
-
*/
|
|
35
|
-
function loadPluginUIContent() {
|
|
36
|
-
const candidates = [
|
|
37
|
-
// Primary: relative to package root (works in both CJS and ESM)
|
|
38
|
-
join(PACKAGE_ROOT, 'figma-desktop-bridge', 'ui-full.html'),
|
|
39
|
-
// Fallback: relative to cwd (development / monorepo setups)
|
|
40
|
-
join(process.cwd(), 'figma-desktop-bridge', 'ui-full.html'),
|
|
41
|
-
];
|
|
42
|
-
for (const path of candidates) {
|
|
43
|
-
try {
|
|
44
|
-
if (existsSync(path)) {
|
|
45
|
-
return readFileSync(path, 'utf-8');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
// try next candidate
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return '<html><body><p>Plugin UI not found. Please reinstall figma-console-mcp.</p></body></html>';
|
|
53
|
-
}
|
|
54
31
|
const logger = createChildLogger({ component: 'websocket-server' });
|
|
55
32
|
export class FigmaWebSocketServer extends EventEmitter {
|
|
56
33
|
constructor(options) {
|
|
@@ -69,8 +46,6 @@ export class FigmaWebSocketServer extends EventEmitter {
|
|
|
69
46
|
this._startedAt = Date.now();
|
|
70
47
|
this.consoleBufferSize = 1000;
|
|
71
48
|
this.documentChangeBufferSize = 200;
|
|
72
|
-
/** Cached plugin UI HTML content — loaded once and served to bootloader requests */
|
|
73
|
-
this._pluginUIContent = null;
|
|
74
49
|
/** Heartbeat interval for detecting dead connections via ping/pong */
|
|
75
50
|
this._heartbeatInterval = null;
|
|
76
51
|
this.options = options;
|
|
@@ -78,7 +53,10 @@ export class FigmaWebSocketServer extends EventEmitter {
|
|
|
78
53
|
}
|
|
79
54
|
/**
|
|
80
55
|
* Handle HTTP requests on the same port as WebSocket.
|
|
81
|
-
* Serves
|
|
56
|
+
* Serves a JSON `/health` (also at `/`) endpoint with version and connected-file
|
|
57
|
+
* info. No plugin-UI route exists — the plugin loads its own `ui.html` from disk
|
|
58
|
+
* via the Figma plugin runtime; the legacy `/plugin/ui` bootloader endpoint was
|
|
59
|
+
* removed in the Phase 3 cleanup.
|
|
82
60
|
*/
|
|
83
61
|
handleHttpRequest(req, res) {
|
|
84
62
|
// CORS headers for Figma plugin iframe (sandboxed, origin: null)
|
|
@@ -91,18 +69,6 @@ export class FigmaWebSocketServer extends EventEmitter {
|
|
|
91
69
|
return;
|
|
92
70
|
}
|
|
93
71
|
const url = req.url || '/';
|
|
94
|
-
// Plugin UI endpoint — bootloader redirects here
|
|
95
|
-
if (url === '/plugin/ui' || url === '/plugin/ui/') {
|
|
96
|
-
if (!this._pluginUIContent) {
|
|
97
|
-
this._pluginUIContent = loadPluginUIContent();
|
|
98
|
-
}
|
|
99
|
-
res.writeHead(200, {
|
|
100
|
-
'Content-Type': 'text/html; charset=utf-8',
|
|
101
|
-
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
102
|
-
});
|
|
103
|
-
res.end(this._pluginUIContent);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
72
|
// Health/version endpoint
|
|
107
73
|
if (url === '/health' || url === '/') {
|
|
108
74
|
const now = Date.now();
|
|
@@ -294,22 +260,6 @@ export class FigmaWebSocketServer extends EventEmitter {
|
|
|
294
260
|
}
|
|
295
261
|
return;
|
|
296
262
|
}
|
|
297
|
-
// Bootloader request: send the full plugin UI HTML
|
|
298
|
-
if (message.type === 'GET_PLUGIN_UI') {
|
|
299
|
-
if (!this._pluginUIContent) {
|
|
300
|
-
this._pluginUIContent = loadPluginUIContent();
|
|
301
|
-
}
|
|
302
|
-
try {
|
|
303
|
-
ws.send(JSON.stringify({
|
|
304
|
-
type: 'PLUGIN_UI_CONTENT',
|
|
305
|
-
html: this._pluginUIContent,
|
|
306
|
-
}));
|
|
307
|
-
}
|
|
308
|
-
catch {
|
|
309
|
-
// Non-critical — bootloader will show error
|
|
310
|
-
}
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
263
|
// Unsolicited data from plugin (FILE_INFO, events, forwarded data)
|
|
314
264
|
if (message.type) {
|
|
315
265
|
// FILE_INFO promotes pending clients to named clients
|
|
@@ -335,6 +285,34 @@ export class FigmaWebSocketServer extends EventEmitter {
|
|
|
335
285
|
}
|
|
336
286
|
this.emit('documentChange', { fileKey: found?.fileKey ?? null, ...message.data });
|
|
337
287
|
}
|
|
288
|
+
// v1.25.0: buffer description/annotation changes for the specific file.
|
|
289
|
+
// These are the diff-engine blind spots that Figma REST doesn't expose;
|
|
290
|
+
// the diff engine consults this buffer when correlating to a version
|
|
291
|
+
// range, so changes made while the plugin was connected become visible.
|
|
292
|
+
if (message.type === 'METADATA_CHANGE' && message.data) {
|
|
293
|
+
const found = this.findClientByWs(ws);
|
|
294
|
+
if (found) {
|
|
295
|
+
const changes = Array.isArray(message.data.changes) ? message.data.changes : [];
|
|
296
|
+
for (const c of changes) {
|
|
297
|
+
if (!c || typeof c.node_id !== 'string' || !c.field)
|
|
298
|
+
continue;
|
|
299
|
+
const entry = {
|
|
300
|
+
node_id: c.node_id,
|
|
301
|
+
node_name: c.node_name ?? null,
|
|
302
|
+
node_type: c.node_type ?? null,
|
|
303
|
+
field: c.field === 'annotations' ? 'annotations' : 'description',
|
|
304
|
+
new_value: c.new_value,
|
|
305
|
+
timestamp: typeof c.timestamp === 'number' ? c.timestamp : Date.now(),
|
|
306
|
+
};
|
|
307
|
+
found.client.metadataChanges.push(entry);
|
|
308
|
+
if (found.client.metadataChanges.length > this.documentChangeBufferSize) {
|
|
309
|
+
found.client.metadataChanges.shift();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
found.client.lastActivity = Date.now();
|
|
313
|
+
}
|
|
314
|
+
this.emit('metadataChange', { fileKey: found?.fileKey ?? null, changes: message.data.changes });
|
|
315
|
+
}
|
|
338
316
|
// Track selection changes — user interaction makes this the active file
|
|
339
317
|
if (message.type === 'SELECTION_CHANGE' && message.data) {
|
|
340
318
|
const found = this.findClientByWs(ws);
|
|
@@ -436,6 +414,7 @@ export class FigmaWebSocketServer extends EventEmitter {
|
|
|
436
414
|
},
|
|
437
415
|
selection: existing?.selection || null,
|
|
438
416
|
documentChanges: existing?.documentChanges || [],
|
|
417
|
+
metadataChanges: existing?.metadataChanges || [],
|
|
439
418
|
consoleLogs: existing?.consoleLogs || [],
|
|
440
419
|
lastActivity: Date.now(),
|
|
441
420
|
lastPongAt: Date.now(),
|
|
@@ -661,6 +640,49 @@ export class FigmaWebSocketServer extends EventEmitter {
|
|
|
661
640
|
client.documentChanges = [];
|
|
662
641
|
return count;
|
|
663
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* v1.25.0: Get buffered description/annotation change events for a file.
|
|
645
|
+
*
|
|
646
|
+
* Used by `figma_diff_versions` to surface changes that Figma REST doesn't
|
|
647
|
+
* expose. The diff engine passes a time window (from-version → to-version
|
|
648
|
+
* `last_modified` timestamps converted to Unix ms) and optional scoping by
|
|
649
|
+
* node IDs.
|
|
650
|
+
*
|
|
651
|
+
* Defaults to the active file if `fileKey` is omitted.
|
|
652
|
+
*/
|
|
653
|
+
getMetadataChanges(options) {
|
|
654
|
+
const fileKey = options?.fileKey ?? this._activeFileKey;
|
|
655
|
+
if (!fileKey)
|
|
656
|
+
return [];
|
|
657
|
+
const client = this.clients.get(fileKey);
|
|
658
|
+
if (!client)
|
|
659
|
+
return [];
|
|
660
|
+
let filtered = [...client.metadataChanges];
|
|
661
|
+
if (options?.since !== undefined) {
|
|
662
|
+
filtered = filtered.filter((e) => e.timestamp >= options.since);
|
|
663
|
+
}
|
|
664
|
+
if (options?.until !== undefined) {
|
|
665
|
+
filtered = filtered.filter((e) => e.timestamp <= options.until);
|
|
666
|
+
}
|
|
667
|
+
if (options?.nodeIds && options.nodeIds.length > 0) {
|
|
668
|
+
const set = new Set(options.nodeIds);
|
|
669
|
+
filtered = filtered.filter((e) => set.has(e.node_id));
|
|
670
|
+
}
|
|
671
|
+
return filtered;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* v1.25.0: Clear metadata-change buffer for the active file.
|
|
675
|
+
*/
|
|
676
|
+
clearMetadataChanges() {
|
|
677
|
+
if (!this._activeFileKey)
|
|
678
|
+
return 0;
|
|
679
|
+
const client = this.clients.get(this._activeFileKey);
|
|
680
|
+
if (!client)
|
|
681
|
+
return 0;
|
|
682
|
+
const count = client.metadataChanges.length;
|
|
683
|
+
client.metadataChanges = [];
|
|
684
|
+
return count;
|
|
685
|
+
}
|
|
664
686
|
/**
|
|
665
687
|
* Get console logs from the active file with optional filtering
|
|
666
688
|
*/
|
package/dist/cloudflare/index.js
CHANGED
|
@@ -15,7 +15,6 @@ import { BrowserManager } from "./browser-manager.js";
|
|
|
15
15
|
import { ConsoleMonitor } from "./core/console-monitor.js";
|
|
16
16
|
import { getConfig } from "./core/config.js";
|
|
17
17
|
import { createChildLogger } from "./core/logger.js";
|
|
18
|
-
import { testBrowserRendering } from "./test-browser.js";
|
|
19
18
|
import { FigmaAPI, extractFileKey } from "./core/figma-api.js";
|
|
20
19
|
import { registerFigmaAPITools } from "./core/figma-tools.js";
|
|
21
20
|
import { registerDesignCodeTools } from "./core/design-code-tools.js";
|
|
@@ -25,9 +24,12 @@ import { registerAnnotationTools } from "./core/annotation-tools.js";
|
|
|
25
24
|
import { registerDeepComponentTools } from "./core/deep-component-tools.js";
|
|
26
25
|
import { registerDesignSystemTools } from "./core/design-system-tools.js";
|
|
27
26
|
import { registerAccessibilityTools } from "./core/accessibility-tools.js";
|
|
27
|
+
import { registerDiagnoseTool } from "./core/diagnose-tool.js";
|
|
28
|
+
import { wrapServerForIdentity } from "./core/identity.js";
|
|
28
29
|
import { generatePairingCode } from "./core/cloud-websocket-relay.js";
|
|
29
30
|
import { CloudWebSocketConnector } from "./core/cloud-websocket-connector.js";
|
|
30
31
|
import { registerWriteTools } from "./core/write-tools.js";
|
|
32
|
+
import { registerTokensTools } from "./core/tokens-tools.js";
|
|
31
33
|
import { registerFigJamTools } from "./core/figjam-tools.js";
|
|
32
34
|
import { registerSlidesTools } from "./core/slides-tools.js";
|
|
33
35
|
// Re-export PluginRelayDO so Cloudflare Workers can bind it as a Durable Object
|
|
@@ -68,10 +70,16 @@ function isFigmaPAT(token) {
|
|
|
68
70
|
export class FigmaConsoleMCPv3 extends McpAgent {
|
|
69
71
|
constructor() {
|
|
70
72
|
super(...arguments);
|
|
71
|
-
this.server =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
this.server = (() => {
|
|
74
|
+
const s = new McpServer({
|
|
75
|
+
name: "Figma Console MCP",
|
|
76
|
+
version: "1.27.1",
|
|
77
|
+
});
|
|
78
|
+
// Identity wrap — every tool's response and thrown error gets stamped
|
|
79
|
+
// with our MCP name so cross-MCP attribution is unambiguous.
|
|
80
|
+
wrapServerForIdentity(s);
|
|
81
|
+
return s;
|
|
82
|
+
})();
|
|
75
83
|
this.browserManager = null;
|
|
76
84
|
this.consoleMonitor = null;
|
|
77
85
|
this.figmaAPI = null;
|
|
@@ -309,7 +317,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
|
|
|
309
317
|
}
|
|
310
318
|
async init() {
|
|
311
319
|
// Tool 1: Get Console Logs
|
|
312
|
-
this.server.tool("figma_get_console_logs", "Retrieve console logs from
|
|
320
|
+
this.server.tool("figma_get_console_logs", "Retrieve console logs from a Cloudflare Browser Rendering session opened with figma_navigate. Captures plugin console output ([Main], [Swapper], etc. prefixes) and page-level logs. NOTE: cloud mode does NOT currently capture logs from a paired Desktop Bridge plugin — for plugin sandbox logs, use Local mode. Call figma_navigate first to open the file and start browser monitoring.", {
|
|
313
321
|
count: z.number().optional().default(100).describe("Number of recent logs to retrieve"),
|
|
314
322
|
level: z
|
|
315
323
|
.enum(["log", "info", "warn", "error", "debug", "all"])
|
|
@@ -376,7 +384,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
|
|
|
376
384
|
nodeId: z
|
|
377
385
|
.string()
|
|
378
386
|
.optional()
|
|
379
|
-
.describe("Optional node ID to screenshot. If
|
|
387
|
+
.describe("Optional node ID to screenshot (e.g., '123:456'). If omitted, uses the node-id query param from the Cloudflare Browser Rendering page's current URL."),
|
|
380
388
|
scale: z
|
|
381
389
|
.number()
|
|
382
390
|
.min(0.01)
|
|
@@ -777,6 +785,8 @@ export class FigmaConsoleMCPv3 extends McpAgent {
|
|
|
777
785
|
};
|
|
778
786
|
// Register all write/manipulation tools via shared function
|
|
779
787
|
registerWriteTools(this.server, getCloudDesktopConnector);
|
|
788
|
+
// Register token sync tools — figma_export_tokens and figma_import_tokens.
|
|
789
|
+
registerTokensTools(this.server, getCloudDesktopConnector, { isRemoteMode: true });
|
|
780
790
|
// Register FigJam-specific tools (sticky notes, connectors, tables, etc.)
|
|
781
791
|
registerFigJamTools(this.server, getCloudDesktopConnector);
|
|
782
792
|
// Register Annotation tools (read/write design annotations via Desktop Bridge)
|
|
@@ -787,7 +797,7 @@ export class FigmaConsoleMCPv3 extends McpAgent {
|
|
|
787
797
|
registerSlidesTools(this.server, getCloudDesktopConnector);
|
|
788
798
|
// Register Figma API tools (Tools 8-14)
|
|
789
799
|
// Pass isRemoteMode: true to suppress Desktop Bridge mentions in tool descriptions
|
|
790
|
-
registerFigmaAPITools(this.server, async () => await this.getFigmaAPI(), () => this.browserManager?.getCurrentUrl() || null,
|
|
800
|
+
registerFigmaAPITools(this.server, async () => await this.getFigmaAPI(), () => this.browserManager?.getCurrentUrl() || null, undefined, // variablesCache
|
|
791
801
|
{ isRemoteMode: true }, getCloudDesktopConnector);
|
|
792
802
|
// Register Design-Code Parity & Documentation tools
|
|
793
803
|
registerDesignCodeTools(this.server, async () => await this.getFigmaAPI(), () => this.browserManager?.getCurrentUrl() || null, undefined, // variablesCache
|
|
@@ -807,6 +817,15 @@ export class FigmaConsoleMCPv3 extends McpAgent {
|
|
|
807
817
|
catch (e) {
|
|
808
818
|
// Silently skip if axe-core/jsdom not available in Workers environment
|
|
809
819
|
}
|
|
820
|
+
// Register figma_diagnose for cloud mode. Plugin state isn't directly
|
|
821
|
+
// observable from here (the paired plugin's WS lives in the relay DO),
|
|
822
|
+
// so we report mode and let the cross-MCP disclaimer do most of the work.
|
|
823
|
+
registerDiagnoseTool(this.server, {
|
|
824
|
+
mode: "cloud",
|
|
825
|
+
getServerVersion: () => "cloud",
|
|
826
|
+
getPluginState: () => null,
|
|
827
|
+
getTokenState: () => ({ hasToken: false }),
|
|
828
|
+
});
|
|
810
829
|
// Note: MCP Apps (Token Browser, Dashboard) are registered in local.ts only
|
|
811
830
|
// They require Node.js file system APIs that don't work in Cloudflare Workers
|
|
812
831
|
}
|
|
@@ -1058,8 +1077,9 @@ export default {
|
|
|
1058
1077
|
});
|
|
1059
1078
|
const statelessServer = new McpServer({
|
|
1060
1079
|
name: "Figma Console MCP",
|
|
1061
|
-
version: "1.
|
|
1080
|
+
version: "1.27.1",
|
|
1062
1081
|
});
|
|
1082
|
+
wrapServerForIdentity(statelessServer);
|
|
1063
1083
|
// ================================================================
|
|
1064
1084
|
// Cloud Write Relay — Pairing Tool (stateless /mcp path)
|
|
1065
1085
|
// Uses KV keyed by bearer token instead of DO storage
|
|
@@ -1137,6 +1157,7 @@ export default {
|
|
|
1137
1157
|
}
|
|
1138
1158
|
// Register all write/manipulation tools via shared function
|
|
1139
1159
|
registerWriteTools(statelessServer, getCloudDesktopConnector);
|
|
1160
|
+
registerTokensTools(statelessServer, getCloudDesktopConnector, { isRemoteMode: true });
|
|
1140
1161
|
// Register FigJam-specific tools
|
|
1141
1162
|
registerFigJamTools(statelessServer, getCloudDesktopConnector);
|
|
1142
1163
|
// Register Annotation tools
|
|
@@ -1146,10 +1167,7 @@ export default {
|
|
|
1146
1167
|
// Register Figma Slides tools
|
|
1147
1168
|
registerSlidesTools(statelessServer, getCloudDesktopConnector);
|
|
1148
1169
|
// Register REST API tools with the authenticated Figma API
|
|
1149
|
-
registerFigmaAPITools(statelessServer, async () => statelessApi, getCloudFileUrl, ()
|
|
1150
|
-
() => null, // No browser manager
|
|
1151
|
-
undefined, // No ensureInitialized
|
|
1152
|
-
new Map(), // Fresh variables cache per request
|
|
1170
|
+
registerFigmaAPITools(statelessServer, async () => statelessApi, getCloudFileUrl, new Map(), // Fresh variables cache per request
|
|
1153
1171
|
{ isRemoteMode: true }, getCloudDesktopConnector);
|
|
1154
1172
|
registerDesignCodeTools(statelessServer, async () => statelessApi, getCloudFileUrl, new Map(), // Fresh variables cache per request
|
|
1155
1173
|
{ isRemoteMode: true }, getCloudDesktopConnector);
|
|
@@ -1694,25 +1712,18 @@ export default {
|
|
|
1694
1712
|
return new Response(JSON.stringify({
|
|
1695
1713
|
status: "healthy",
|
|
1696
1714
|
service: "Figma Console MCP",
|
|
1697
|
-
version: "1.
|
|
1715
|
+
version: "1.27.1",
|
|
1698
1716
|
endpoints: {
|
|
1699
1717
|
mcp: ["/sse", "/mcp"],
|
|
1700
1718
|
oauth_mcp_spec: ["/.well-known/oauth-authorization-server", "/authorize", "/token", "/oauth/register"],
|
|
1701
1719
|
oauth_legacy: ["/oauth/authorize", "/oauth/callback"],
|
|
1702
|
-
utility: ["/
|
|
1720
|
+
utility: ["/health"]
|
|
1703
1721
|
},
|
|
1704
1722
|
oauth_configured: !!env.FIGMA_OAUTH_CLIENT_ID
|
|
1705
1723
|
}), {
|
|
1706
1724
|
headers: { "Content-Type": "application/json" },
|
|
1707
1725
|
});
|
|
1708
1726
|
}
|
|
1709
|
-
// Browser Rendering API test endpoint
|
|
1710
|
-
if (url.pathname === "/test-browser") {
|
|
1711
|
-
const results = await testBrowserRendering(env);
|
|
1712
|
-
return new Response(JSON.stringify(results, null, 2), {
|
|
1713
|
-
headers: { "Content-Type": "application/json" },
|
|
1714
|
-
});
|
|
1715
|
-
}
|
|
1716
1727
|
// Serve favicon
|
|
1717
1728
|
if (url.pathname === "/favicon.ico") {
|
|
1718
1729
|
// Redirect to custom Figma Console icon
|
|
@@ -1740,13 +1751,13 @@ export default {
|
|
|
1740
1751
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1741
1752
|
<title>Figma Console MCP - The Most Comprehensive MCP Server for Figma</title>
|
|
1742
1753
|
<link rel="icon" type="image/svg+xml" href="https://docs.figma-console-mcp.southleft.com/favicon.svg">
|
|
1743
|
-
<meta name="description" content="Turn your Figma design system into a living API.
|
|
1754
|
+
<meta name="description" content="Turn your Figma design system into a living API. 103+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
|
|
1744
1755
|
|
|
1745
1756
|
<!-- Open Graph -->
|
|
1746
1757
|
<meta property="og:type" content="website">
|
|
1747
1758
|
<meta property="og:url" content="https://figma-console-mcp.southleft.com">
|
|
1748
1759
|
<meta property="og:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
|
|
1749
|
-
<meta property="og:description" content="The most comprehensive MCP server for Figma.
|
|
1760
|
+
<meta property="og:description" content="The most comprehensive MCP server for Figma. 103+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
|
|
1750
1761
|
<meta property="og:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
|
|
1751
1762
|
<meta property="og:image:width" content="1200">
|
|
1752
1763
|
<meta property="og:image:height" content="630">
|
|
@@ -1754,7 +1765,7 @@ export default {
|
|
|
1754
1765
|
<!-- Twitter -->
|
|
1755
1766
|
<meta name="twitter:card" content="summary_large_image">
|
|
1756
1767
|
<meta name="twitter:title" content="Figma Console MCP - Turn Your Design System Into a Living API">
|
|
1757
|
-
<meta name="twitter:description" content="The most comprehensive MCP server for Figma.
|
|
1768
|
+
<meta name="twitter:description" content="The most comprehensive MCP server for Figma. 103+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
|
|
1758
1769
|
<meta name="twitter:image" content="https://docs.figma-console-mcp.southleft.com/images/og-image.jpg">
|
|
1759
1770
|
|
|
1760
1771
|
<meta name="theme-color" content="#0D9488">
|
|
@@ -2641,7 +2652,7 @@ export default {
|
|
|
2641
2652
|
<div class="grid-cell showcase-cell rule-left">
|
|
2642
2653
|
<div class="showcase-label">What AI Can Access</div>
|
|
2643
2654
|
<div class="showcase-stat">
|
|
2644
|
-
<span class="number">
|
|
2655
|
+
<span class="number">103+</span>
|
|
2645
2656
|
<span class="label">MCP tools for Figma</span>
|
|
2646
2657
|
</div>
|
|
2647
2658
|
<div class="capability-list">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAiErD;;GAEG;AACH,wBAAgB,UAAU,IAAI,YAAY,CAqBzC;AA2BD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAkCzD;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAIxC"}
|
package/dist/core/config.js
CHANGED
|
@@ -48,10 +48,6 @@ const DEFAULT_CONFIG = {
|
|
|
48
48
|
quality: 90,
|
|
49
49
|
storePath: join(process.env.TMPDIR || '/tmp', 'figma-console-mcp', 'screenshots'),
|
|
50
50
|
},
|
|
51
|
-
local: {
|
|
52
|
-
debugHost: process.env.FIGMA_DEBUG_HOST || 'localhost',
|
|
53
|
-
debugPort: parseInt(process.env.FIGMA_DEBUG_PORT || '9222', 10),
|
|
54
|
-
},
|
|
55
51
|
};
|
|
56
52
|
/**
|
|
57
53
|
* Possible config file locations (checked in order)
|
|
@@ -111,10 +107,6 @@ function mergeConfig(defaults, overrides) {
|
|
|
111
107
|
...defaults.screenshots,
|
|
112
108
|
...(overrides.screenshots || {}),
|
|
113
109
|
},
|
|
114
|
-
local: {
|
|
115
|
-
...defaults.local,
|
|
116
|
-
...(overrides.local || {}),
|
|
117
|
-
},
|
|
118
110
|
};
|
|
119
111
|
}
|
|
120
112
|
/**
|
package/dist/core/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B;;GAEG;AACH,SAAS,UAAU;IACjB,uDAAuD;IACvD,IAAI,OAAO,UAAU,KAAK,WAAW,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;QAChE,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,4BAA4B;IAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,EAAE,CAAC;IAC1D,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QACpD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,4CAA4C;IAC5C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,cAAc,GAAiB;IACnC,IAAI,EAAE,UAAU,EAAE;IAClB,OAAO,EAAE;QACP,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE;YACJ,+CAA+C;YAC/C,yBAAyB;YACzB,cAAc,EAAE,yCAAyC;SAC1D;KACF;IACD,OAAO,EAAE;QACP,UAAU,EAAE,IAAI;QAChB,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;QACvD,UAAU,EAAE;YACV,eAAe,EAAE,GAAG;YACpB,cAAc,EAAE,EAAE;YAClB,cAAc,EAAE,CAAC;YACjB,gBAAgB,EAAE,IAAI;SACvB;KACF;IACD,WAAW,EAAE;QACX,aAAa,EAAE,KAAK;QACpB,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,MAAM,EAAE,mBAAmB,EAAE,aAAa,CAAC;KAClF;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,8 +18,8 @@ export declare class ConsoleMonitor {
|
|
|
18
18
|
private lastUrl;
|
|
19
19
|
constructor(config: ConsoleConfig);
|
|
20
20
|
/**
|
|
21
|
-
* Start monitoring console logs on a page
|
|
22
|
-
* Accepts
|
|
21
|
+
* Start monitoring console logs on a page.
|
|
22
|
+
* Accepts the @cloudflare/puppeteer Page type (cloud mode only).
|
|
23
23
|
*/
|
|
24
24
|
startMonitoring(page: any): Promise<void>;
|
|
25
25
|
/**
|