@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.
Files changed (213) hide show
  1. package/README.md +49 -33
  2. package/dist/cloudflare/core/config.js +0 -8
  3. package/dist/cloudflare/core/console-monitor.js +3 -3
  4. package/dist/cloudflare/core/diagnose-tool.js +96 -0
  5. package/dist/cloudflare/core/figma-tools.js +69 -229
  6. package/dist/cloudflare/core/identity.js +96 -0
  7. package/dist/cloudflare/core/tokens/alias-resolver.js +98 -0
  8. package/dist/cloudflare/core/tokens/config.js +284 -0
  9. package/dist/cloudflare/core/tokens/figma-converter.js +195 -0
  10. package/dist/cloudflare/core/tokens/formatters/css-vars.js +329 -0
  11. package/dist/cloudflare/core/tokens/formatters/dtcg.js +300 -0
  12. package/dist/cloudflare/core/tokens/formatters/index.js +45 -0
  13. package/dist/cloudflare/core/tokens/formatters/json.js +7 -0
  14. package/dist/cloudflare/core/tokens/formatters/less.js +4 -0
  15. package/dist/cloudflare/core/tokens/formatters/scss.js +4 -0
  16. package/dist/cloudflare/core/tokens/formatters/stubs.js +11 -0
  17. package/dist/cloudflare/core/tokens/formatters/style-dictionary-v3.js +4 -0
  18. package/dist/cloudflare/core/tokens/formatters/tailwind-v3.js +4 -0
  19. package/dist/cloudflare/core/tokens/formatters/tailwind-v4.js +4 -0
  20. package/dist/cloudflare/core/tokens/formatters/tokens-studio.js +4 -0
  21. package/dist/cloudflare/core/tokens/formatters/ts-module.js +4 -0
  22. package/dist/cloudflare/core/tokens/index.js +15 -0
  23. package/dist/cloudflare/core/tokens/parsers/css-vars.js +4 -0
  24. package/dist/cloudflare/core/tokens/parsers/dtcg.js +253 -0
  25. package/dist/cloudflare/core/tokens/parsers/index.js +138 -0
  26. package/dist/cloudflare/core/tokens/parsers/json.js +7 -0
  27. package/dist/cloudflare/core/tokens/parsers/scss.js +4 -0
  28. package/dist/cloudflare/core/tokens/parsers/stubs.js +13 -0
  29. package/dist/cloudflare/core/tokens/parsers/style-dictionary-v3.js +4 -0
  30. package/dist/cloudflare/core/tokens/parsers/tailwind-v3.js +4 -0
  31. package/dist/cloudflare/core/tokens/parsers/tailwind-v4.js +4 -0
  32. package/dist/cloudflare/core/tokens/parsers/tokens-studio.js +4 -0
  33. package/dist/cloudflare/core/tokens/schemas.js +148 -0
  34. package/dist/cloudflare/core/tokens/transforms/color.js +12 -0
  35. package/dist/cloudflare/core/tokens/transforms/index.js +29 -0
  36. package/dist/cloudflare/core/tokens/transforms/size.js +7 -0
  37. package/dist/cloudflare/core/tokens/types.js +18 -0
  38. package/dist/cloudflare/core/tokens-tools.js +849 -0
  39. package/dist/cloudflare/core/version-tools.js +151 -7
  40. package/dist/cloudflare/core/websocket-server.js +77 -55
  41. package/dist/cloudflare/index.js +37 -26
  42. package/dist/core/config.d.ts.map +1 -1
  43. package/dist/core/config.js +0 -8
  44. package/dist/core/config.js.map +1 -1
  45. package/dist/core/console-monitor.d.ts +2 -2
  46. package/dist/core/console-monitor.d.ts.map +1 -1
  47. package/dist/core/console-monitor.js +3 -3
  48. package/dist/core/console-monitor.js.map +1 -1
  49. package/dist/core/diagnose-tool.d.ts +33 -0
  50. package/dist/core/diagnose-tool.d.ts.map +1 -0
  51. package/dist/core/diagnose-tool.js +97 -0
  52. package/dist/core/diagnose-tool.js.map +1 -0
  53. package/dist/core/diff/diff-engine.d.ts +14 -0
  54. package/dist/core/diff/diff-engine.d.ts.map +1 -1
  55. package/dist/core/diff/diff-engine.js.map +1 -1
  56. package/dist/core/figma-connector.d.ts +1 -1
  57. package/dist/core/figma-connector.d.ts.map +1 -1
  58. package/dist/core/figma-tools.d.ts +1 -2
  59. package/dist/core/figma-tools.d.ts.map +1 -1
  60. package/dist/core/figma-tools.js +69 -229
  61. package/dist/core/figma-tools.js.map +1 -1
  62. package/dist/core/identity.d.ts +41 -0
  63. package/dist/core/identity.d.ts.map +1 -0
  64. package/dist/core/identity.js +97 -0
  65. package/dist/core/identity.js.map +1 -0
  66. package/dist/core/tokens/alias-resolver.d.ts +40 -0
  67. package/dist/core/tokens/alias-resolver.d.ts.map +1 -0
  68. package/dist/core/tokens/alias-resolver.js +99 -0
  69. package/dist/core/tokens/alias-resolver.js.map +1 -0
  70. package/dist/core/tokens/config.d.ts +352 -0
  71. package/dist/core/tokens/config.d.ts.map +1 -0
  72. package/dist/core/tokens/config.js +285 -0
  73. package/dist/core/tokens/config.js.map +1 -0
  74. package/dist/core/tokens/figma-converter.d.ts +81 -0
  75. package/dist/core/tokens/figma-converter.d.ts.map +1 -0
  76. package/dist/core/tokens/figma-converter.js +196 -0
  77. package/dist/core/tokens/figma-converter.js.map +1 -0
  78. package/dist/core/tokens/formatters/css-vars.d.ts +24 -0
  79. package/dist/core/tokens/formatters/css-vars.d.ts.map +1 -0
  80. package/dist/core/tokens/formatters/css-vars.js +330 -0
  81. package/dist/core/tokens/formatters/css-vars.js.map +1 -0
  82. package/dist/core/tokens/formatters/dtcg.d.ts +28 -0
  83. package/dist/core/tokens/formatters/dtcg.d.ts.map +1 -0
  84. package/dist/core/tokens/formatters/dtcg.js +301 -0
  85. package/dist/core/tokens/formatters/dtcg.js.map +1 -0
  86. package/dist/core/tokens/formatters/index.d.ts +30 -0
  87. package/dist/core/tokens/formatters/index.d.ts.map +1 -0
  88. package/dist/core/tokens/formatters/index.js +46 -0
  89. package/dist/core/tokens/formatters/index.js.map +1 -0
  90. package/dist/core/tokens/formatters/json.d.ts +5 -0
  91. package/dist/core/tokens/formatters/json.d.ts.map +1 -0
  92. package/dist/core/tokens/formatters/json.js +8 -0
  93. package/dist/core/tokens/formatters/json.js.map +1 -0
  94. package/dist/core/tokens/formatters/less.d.ts +4 -0
  95. package/dist/core/tokens/formatters/less.d.ts.map +1 -0
  96. package/dist/core/tokens/formatters/less.js +5 -0
  97. package/dist/core/tokens/formatters/less.js.map +1 -0
  98. package/dist/core/tokens/formatters/scss.d.ts +4 -0
  99. package/dist/core/tokens/formatters/scss.d.ts.map +1 -0
  100. package/dist/core/tokens/formatters/scss.js +5 -0
  101. package/dist/core/tokens/formatters/scss.js.map +1 -0
  102. package/dist/core/tokens/formatters/stubs.d.ts +9 -0
  103. package/dist/core/tokens/formatters/stubs.d.ts.map +1 -0
  104. package/dist/core/tokens/formatters/stubs.js +12 -0
  105. package/dist/core/tokens/formatters/stubs.js.map +1 -0
  106. package/dist/core/tokens/formatters/style-dictionary-v3.d.ts +4 -0
  107. package/dist/core/tokens/formatters/style-dictionary-v3.d.ts.map +1 -0
  108. package/dist/core/tokens/formatters/style-dictionary-v3.js +5 -0
  109. package/dist/core/tokens/formatters/style-dictionary-v3.js.map +1 -0
  110. package/dist/core/tokens/formatters/tailwind-v3.d.ts +4 -0
  111. package/dist/core/tokens/formatters/tailwind-v3.d.ts.map +1 -0
  112. package/dist/core/tokens/formatters/tailwind-v3.js +5 -0
  113. package/dist/core/tokens/formatters/tailwind-v3.js.map +1 -0
  114. package/dist/core/tokens/formatters/tailwind-v4.d.ts +4 -0
  115. package/dist/core/tokens/formatters/tailwind-v4.d.ts.map +1 -0
  116. package/dist/core/tokens/formatters/tailwind-v4.js +5 -0
  117. package/dist/core/tokens/formatters/tailwind-v4.js.map +1 -0
  118. package/dist/core/tokens/formatters/tokens-studio.d.ts +4 -0
  119. package/dist/core/tokens/formatters/tokens-studio.d.ts.map +1 -0
  120. package/dist/core/tokens/formatters/tokens-studio.js +5 -0
  121. package/dist/core/tokens/formatters/tokens-studio.js.map +1 -0
  122. package/dist/core/tokens/formatters/ts-module.d.ts +4 -0
  123. package/dist/core/tokens/formatters/ts-module.d.ts.map +1 -0
  124. package/dist/core/tokens/formatters/ts-module.js +5 -0
  125. package/dist/core/tokens/formatters/ts-module.js.map +1 -0
  126. package/dist/core/tokens/index.d.ts +17 -0
  127. package/dist/core/tokens/index.d.ts.map +1 -0
  128. package/dist/core/tokens/index.js +16 -0
  129. package/dist/core/tokens/index.js.map +1 -0
  130. package/dist/core/tokens/parsers/css-vars.d.ts +3 -0
  131. package/dist/core/tokens/parsers/css-vars.d.ts.map +1 -0
  132. package/dist/core/tokens/parsers/css-vars.js +5 -0
  133. package/dist/core/tokens/parsers/css-vars.js.map +1 -0
  134. package/dist/core/tokens/parsers/dtcg.d.ts +21 -0
  135. package/dist/core/tokens/parsers/dtcg.d.ts.map +1 -0
  136. package/dist/core/tokens/parsers/dtcg.js +254 -0
  137. package/dist/core/tokens/parsers/dtcg.js.map +1 -0
  138. package/dist/core/tokens/parsers/index.d.ts +37 -0
  139. package/dist/core/tokens/parsers/index.d.ts.map +1 -0
  140. package/dist/core/tokens/parsers/index.js +139 -0
  141. package/dist/core/tokens/parsers/index.js.map +1 -0
  142. package/dist/core/tokens/parsers/json.d.ts +4 -0
  143. package/dist/core/tokens/parsers/json.d.ts.map +1 -0
  144. package/dist/core/tokens/parsers/json.js +8 -0
  145. package/dist/core/tokens/parsers/json.js.map +1 -0
  146. package/dist/core/tokens/parsers/scss.d.ts +3 -0
  147. package/dist/core/tokens/parsers/scss.d.ts.map +1 -0
  148. package/dist/core/tokens/parsers/scss.js +5 -0
  149. package/dist/core/tokens/parsers/scss.js.map +1 -0
  150. package/dist/core/tokens/parsers/stubs.d.ts +11 -0
  151. package/dist/core/tokens/parsers/stubs.d.ts.map +1 -0
  152. package/dist/core/tokens/parsers/stubs.js +14 -0
  153. package/dist/core/tokens/parsers/stubs.js.map +1 -0
  154. package/dist/core/tokens/parsers/style-dictionary-v3.d.ts +3 -0
  155. package/dist/core/tokens/parsers/style-dictionary-v3.d.ts.map +1 -0
  156. package/dist/core/tokens/parsers/style-dictionary-v3.js +5 -0
  157. package/dist/core/tokens/parsers/style-dictionary-v3.js.map +1 -0
  158. package/dist/core/tokens/parsers/tailwind-v3.d.ts +3 -0
  159. package/dist/core/tokens/parsers/tailwind-v3.d.ts.map +1 -0
  160. package/dist/core/tokens/parsers/tailwind-v3.js +5 -0
  161. package/dist/core/tokens/parsers/tailwind-v3.js.map +1 -0
  162. package/dist/core/tokens/parsers/tailwind-v4.d.ts +3 -0
  163. package/dist/core/tokens/parsers/tailwind-v4.d.ts.map +1 -0
  164. package/dist/core/tokens/parsers/tailwind-v4.js +5 -0
  165. package/dist/core/tokens/parsers/tailwind-v4.js.map +1 -0
  166. package/dist/core/tokens/parsers/tokens-studio.d.ts +3 -0
  167. package/dist/core/tokens/parsers/tokens-studio.d.ts.map +1 -0
  168. package/dist/core/tokens/parsers/tokens-studio.js +5 -0
  169. package/dist/core/tokens/parsers/tokens-studio.js.map +1 -0
  170. package/dist/core/tokens/schemas.d.ts +152 -0
  171. package/dist/core/tokens/schemas.d.ts.map +1 -0
  172. package/dist/core/tokens/schemas.js +149 -0
  173. package/dist/core/tokens/schemas.js.map +1 -0
  174. package/dist/core/tokens/transforms/color.d.ts +9 -0
  175. package/dist/core/tokens/transforms/color.d.ts.map +1 -0
  176. package/dist/core/tokens/transforms/color.js +13 -0
  177. package/dist/core/tokens/transforms/color.js.map +1 -0
  178. package/dist/core/tokens/transforms/index.d.ts +36 -0
  179. package/dist/core/tokens/transforms/index.d.ts.map +1 -0
  180. package/dist/core/tokens/transforms/index.js +30 -0
  181. package/dist/core/tokens/transforms/index.js.map +1 -0
  182. package/dist/core/tokens/transforms/size.d.ts +7 -0
  183. package/dist/core/tokens/transforms/size.d.ts.map +1 -0
  184. package/dist/core/tokens/transforms/size.js +8 -0
  185. package/dist/core/tokens/transforms/size.js.map +1 -0
  186. package/dist/core/tokens/types.d.ts +228 -0
  187. package/dist/core/tokens/types.d.ts.map +1 -0
  188. package/dist/core/tokens/types.js +19 -0
  189. package/dist/core/tokens/types.js.map +1 -0
  190. package/dist/core/tokens-tools.d.ts +42 -0
  191. package/dist/core/tokens-tools.d.ts.map +1 -0
  192. package/dist/core/tokens-tools.js +850 -0
  193. package/dist/core/tokens-tools.js.map +1 -0
  194. package/dist/core/types/index.d.ts +0 -8
  195. package/dist/core/types/index.d.ts.map +1 -1
  196. package/dist/core/version-tools.d.ts +30 -1
  197. package/dist/core/version-tools.d.ts.map +1 -1
  198. package/dist/core/version-tools.js +151 -7
  199. package/dist/core/version-tools.js.map +1 -1
  200. package/dist/core/websocket-connector.d.ts +1 -1
  201. package/dist/core/websocket-connector.d.ts.map +1 -1
  202. package/dist/core/websocket-server.d.ts +47 -3
  203. package/dist/core/websocket-server.d.ts.map +1 -1
  204. package/dist/core/websocket-server.js +77 -55
  205. package/dist/core/websocket-server.js.map +1 -1
  206. package/dist/local.d.ts +0 -12
  207. package/dist/local.d.ts.map +1 -1
  208. package/dist/local.js +967 -3406
  209. package/dist/local.js.map +1 -1
  210. package/figma-desktop-bridge/code.js +59 -63
  211. package/figma-desktop-bridge/ui.html +85 -11
  212. package/package.json +12 -30
  213. 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
- // Tool Registration
84
- // ============================================================================
85
- export function registerVersionTools(server, getFigmaAPI, getCurrentUrl, _options, getCurrentSelectedNodeIds) {
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
- if (!component_ids || component_ids.length === 0) {
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: component_ids && component_ids.length > 0 ? scoped : undefined,
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. NOTE: variable VALUE history is not retrievable from Figma REST and is not represented in this diff.", {
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, existsSync } from 'fs';
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 plugin UI content for the bootloader and health checks.
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
  */
@@ -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 = new McpServer({
72
- name: "Figma Console MCP",
73
- version: "1.23.0",
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 Figma. Captures all plugin console output including [Main], [Swapper], etc. prefixes. Call figma_navigate first to initialize browser monitoring.", {
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 not provided, uses the currently viewed page/frame from the browser URL."),
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, () => this.consoleMonitor || null, () => this.browserManager || null, () => this.ensureInitialized(), undefined, // variablesCache
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.23.0",
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, () => null, // No console monitor
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.23.0",
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: ["/test-browser", "/health"]
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. 100+ tools give AI assistants deep access to design tokens, component specs, variables, and programmatic design creation.">
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. 100+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
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. 100+ tools give AI assistants deep access to design tokens, components, variables, and programmatic design creation.">
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">100+</span>
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;AAqErD;;GAEG;AACH,wBAAgB,UAAU,IAAI,YAAY,CAqBzC;AA+BD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAkCzD;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAIxC"}
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"}
@@ -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
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
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 any puppeteer Page type (puppeteer-core or @cloudflare/puppeteer)
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
  /**