@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
@@ -7,8 +7,8 @@ import * as fs from "fs";
7
7
  import * as path from "path";
8
8
  import { extractFileKey, extractFigmaUrlInfo, formatVariables, formatComponentData, withTimeout } from "./figma-api.js";
9
9
  import { createChildLogger } from "./logger.js";
10
+ import { identifiedError, withIdentity } from "./identity.js";
10
11
  import { EnrichmentService } from "./enrichment/index.js";
11
- import { SnippetInjector } from "./snippet-injector.js";
12
12
  import { extractNodeSpec, validateReconstructionSpec, listVariants } from "./figma-reconstruction-spec.js";
13
13
  const logger = createChildLogger({ component: "figma-tools" });
14
14
  // Initialize enrichment service
@@ -81,8 +81,6 @@ function scanCodebaseComponents(componentsDir) {
81
81
  }
82
82
  return registry;
83
83
  }
84
- // Initialize snippet injector
85
- const snippetInjector = new SnippetInjector();
86
84
  // ============================================================================
87
85
  // Cache Management & Data Processing Helpers
88
86
  // ============================================================================
@@ -132,14 +130,19 @@ function calculateSizeKB(data) {
132
130
  * @returns Response content array with optional AI instruction
133
131
  */
134
132
  function adaptiveResponse(responseData, options) {
135
- const sizeKB = calculateSizeKB(responseData);
133
+ // Tag every response with our MCP identity so LLMs can attribute it
134
+ // unambiguously when other Figma-related MCPs are also connected.
135
+ const tagged = responseData && typeof responseData === "object" && !Array.isArray(responseData)
136
+ ? withIdentity(responseData)
137
+ : { _mcp: "figma-console-mcp", data: responseData };
138
+ const sizeKB = calculateSizeKB(tagged);
136
139
  // No compression needed
137
140
  if (sizeKB <= RESPONSE_SIZE_THRESHOLDS.IDEAL_SIZE_KB) {
138
141
  return {
139
142
  content: [
140
143
  {
141
144
  type: "text",
142
- text: JSON.stringify(responseData),
145
+ text: JSON.stringify(tagged),
143
146
  },
144
147
  ],
145
148
  };
@@ -200,11 +203,14 @@ function adaptiveResponse(responseData, options) {
200
203
  });
201
204
  }
202
205
  }
203
- // Build response content
206
+ // Build response content (tagged with identity so cross-MCP attribution is clear)
207
+ const taggedFinal = finalData && typeof finalData === "object" && !Array.isArray(finalData)
208
+ ? withIdentity(finalData)
209
+ : { _mcp: "figma-console-mcp", data: finalData };
204
210
  const content = [
205
211
  {
206
212
  type: "text",
207
- text: JSON.stringify(finalData),
213
+ text: JSON.stringify(taggedFinal),
208
214
  },
209
215
  ];
210
216
  // Add AI instruction as separate content block if needed
@@ -647,8 +653,21 @@ function resolveVariableAliases(variables, allVariablesMap, collectionsMap) {
647
653
  /**
648
654
  * Register Figma API tools with the MCP server
649
655
  */
650
- export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getConsoleMonitor, getBrowserManager, ensureInitialized, variablesCache, options, getDesktopConnector) {
656
+ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, variablesCache, options, getDesktopConnector) {
651
657
  const isRemoteMode = options?.isRemoteMode ?? false;
658
+ /**
659
+ * Build a designer-readable REST-auth error tagged with our MCP identity.
660
+ * Shows only the remediation path that applies to the caller's mode — local
661
+ * users never see OAuth instructions, cloud users never see env-var
662
+ * instructions. Identity prefix lets LLMs disambiguate this error from
663
+ * errors thrown by other Figma-related MCP servers running in parallel.
664
+ */
665
+ function restAuthError(context, originalError, extra) {
666
+ const remediation = isRemoteMode
667
+ ? "Re-authenticate via the OAuth flow in your MCP client, or pass a Figma personal access token (figd_...) as a Bearer token."
668
+ : "Set FIGMA_ACCESS_TOKEN in your MCP client config to a Figma personal access token. Generate one at https://www.figma.com/developers/api#access-tokens.";
669
+ return identifiedError(`${context}\nError: ${originalError}\n\nTo fix: ${remediation}${extra ? `\n\n${extra}` : ""}`);
670
+ }
652
671
  // Tool 8: Get File Data (General Purpose)
653
672
  // NOTE: For specific use cases, consider using specialized tools:
654
673
  // - figma_get_component_for_development: For UI component implementation
@@ -688,13 +707,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
688
707
  }
689
708
  catch (apiError) {
690
709
  const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
691
- throw new Error(`Cannot retrieve file data. REST API authentication required.\n` +
692
- `Error: ${errorMessage}\n\n` +
693
- `To fix:\n` +
694
- `1. Local mode: Set FIGMA_ACCESS_TOKEN environment variable\n` +
695
- `2. Cloud mode: Authenticate via OAuth\n\n` +
696
- `Note: figma_get_file_data requires REST API access. ` +
697
- `For component-specific data, use figma_get_component which has Desktop Bridge fallback.`);
710
+ throw restAuthError("Cannot retrieve file data. REST API authentication required.", errorMessage, "Note: figma_get_file_data requires REST API access. For component-specific data, use figma_get_component which has Desktop Bridge fallback.");
698
711
  }
699
712
  // Use provided URL or current URL from browser
700
713
  const url = fileUrl || getCurrentUrl();
@@ -843,18 +856,17 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
843
856
  /**
844
857
  * Tool 9: Get Variables (Design Tokens)
845
858
  *
846
- * WORKFLOW:
847
- * - Primary: Attempts to fetch variables via Figma REST API (requires Enterprise plan)
848
- * - Fallback: On 403 error, provides console-based extraction snippet
849
- *
850
- * TWO-CALL PATTERN (when API unavailable):
851
- * 1. First call: Returns snippet + instructions (useConsoleFallback: true, default)
852
- * 2. User runs snippet in Figma plugin console
853
- * 3. Second call: Parses captured data (parseFromConsole: true)
859
+ * RESOLUTION ORDER (in order, first success wins):
860
+ * 1. Cache hit (if fresh and refreshCache=false)
861
+ * 2. Desktop Bridge plugin via WebSocket works on any Figma plan
862
+ * 3. REST API — requires Enterprise plan (returns 403 otherwise)
863
+ * 4. Styles API — partial fallback for non-Enterprise (styles, not variables)
854
864
  *
855
- * IMPORTANT: Snippet requires Figma Plugin API context, not browser DevTools console.
865
+ * The legacy `parseFromConsole` two-call console-snippet workflow was
866
+ * removed in the Phase 3 cleanup. Setting parseFromConsole=true now
867
+ * throws an identified error pointing the caller at the bridge.
856
868
  */
857
- server.tool("figma_get_variables", "Extract design tokens and variables from a Figma file with code export support (CSS, Tailwind, TypeScript, Sass). Use when user asks for: design system tokens, variables, color/spacing values, theme data, or code exports. Handles multi-mode variables (Light/Dark themes). NOT for component metadata (use figma_get_component). Supports filtering by collection/mode/name and verbosity control to prevent token exhaustion. Enterprise plan required for Variables API; automatically falls back to Styles API or console-based extraction if unavailable. TIP: For full design system extraction (tokens + components + styles combined), prefer figma_get_design_system_kit instead — it returns everything in one optimized call.", {
869
+ server.tool("figma_get_variables", "Extract design tokens and variables from a Figma file with code export support (CSS, Tailwind, TypeScript, Sass). Use when user asks for: design system tokens, variables, color/spacing values, theme data, or code exports. Handles multi-mode variables (Light/Dark themes). NOT for component metadata (use figma_get_component). Supports filtering by collection/mode/name and verbosity control to prevent token exhaustion. Resolution order: Desktop Bridge plugin (works on any plan) Variables REST API (Enterprise only) Styles API as a partial fallback. TIP: For full design system extraction (tokens + components + styles combined), prefer figma_get_design_system_kit instead — it returns everything in one optimized call.", {
858
870
  fileUrl: z
859
871
  .string()
860
872
  .url()
@@ -912,7 +924,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
912
924
  .boolean()
913
925
  .optional()
914
926
  .default(false)
915
- .describe("Return variables as resource_link references instead of full data. Drastically reduces payload size (100+ variables = ~20KB vs >1MB). Use with figma_get_variable_by_id to fetch specific variables. Recommended for large variable sets. Default: false"),
927
+ .describe("Return variables as resource_link references instead of full data. Drastically reduces payload size (100+ variables = ~20KB vs >1MB). Recommended for large variable sets — combine with format='filtered' + namePattern/collection/mode to fetch only the variables you need. Default: false"),
916
928
  refreshCache: z
917
929
  .boolean()
918
930
  .optional()
@@ -922,20 +934,12 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
922
934
  .boolean()
923
935
  .optional()
924
936
  .default(true)
925
- .describe("Enable automatic fallback to console-based extraction when REST API returns 403 (Figma Enterprise plan required). " +
926
- "When enabled, provides a JavaScript snippet that users run in Figma's plugin console. " +
927
- "This is STEP 1 of a two-call workflow. After receiving the snippet, instruct the user to run it, then call this tool again with parseFromConsole=true. " +
928
- "Default: true. Set to false only to disable the fallback entirely."),
937
+ .describe("DEPRECATED has no effect. The console-snippet workflow was removed in the Phase 3 CDP cleanup; the Desktop Bridge plugin now handles all non-REST variable extraction automatically. Kept for parameter compatibility only — safe to ignore."),
929
938
  parseFromConsole: z
930
939
  .boolean()
931
940
  .optional()
932
941
  .default(false)
933
- .describe("Parse variables from console logs after user has executed the snippet. " +
934
- "This is STEP 2 of the two-call workflow. Set to true ONLY after: " +
935
- "(1) you received a console snippet from the first call, " +
936
- "(2) instructed the user to run it in Figma's PLUGIN console (Plugins → Development → Open Console or existing plugin), " +
937
- "(3) user confirmed they ran the snippet and saw '✅ Variables data captured!' message. " +
938
- "Default: false. Never set to true on the first call."),
942
+ .describe("DEPRECATED setting this to true now raises an explicit error. The Puppeteer-based console parser no longer exists. Open the Figma Console MCP Desktop Bridge plugin in Figma Desktop and call figma_get_variables() without parseFromConsole; the plugin returns full variable data through the WebSocket bridge."),
939
943
  page: z
940
944
  .number()
941
945
  .int()
@@ -1271,13 +1275,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1271
1275
  // Check if REST API token is available
1272
1276
  const hasToken = !!process.env.FIGMA_ACCESS_TOKEN;
1273
1277
  let restApiSucceeded = false;
1274
- // Detect Desktop Bridge availability early (needed for priority decision)
1275
- if (ensureInitialized && !getDesktopConnector && !parseFromConsole) {
1276
- logger.info("Calling ensureInitialized to initialize browser manager (legacy path)");
1277
- await ensureInitialized();
1278
- }
1279
- const browserManager = getBrowserManager?.();
1280
- const hasDesktopConnection = !!getDesktopConnector || !!browserManager;
1278
+ const hasDesktopConnection = !!getDesktopConnector;
1281
1279
  // PRIORITY LOGIC:
1282
1280
  // 1. If Desktop Bridge connected → Try Desktop Bridge FIRST (instant, all plans, full Plugin API data)
1283
1281
  // 2. If no Desktop Bridge OR it fails → Try REST API as fallback (Enterprise users)
@@ -1434,7 +1432,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1434
1432
  const content = [
1435
1433
  {
1436
1434
  type: "text",
1437
- text: `Variables for file ${fileKey} (${localFormatted.variables.length} variables). Use figma_get_variable_by_id to fetch specific variables:\n\n`,
1435
+ text: `Variables for file ${fileKey} (${localFormatted.variables.length} variables). Call figma_get_variables again with format='filtered' and a namePattern/collection/mode filter to fetch specific variables:\n\n`,
1438
1436
  },
1439
1437
  ];
1440
1438
  for (const variable of localFormatted.variables) {
@@ -1529,21 +1527,11 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1529
1527
  }
1530
1528
  // PRIMARY: Try Desktop Bridge (instant, all plans, full Plugin API data including aliases)
1531
1529
  // Also used as fallback when REST API fails (403, timeout, rate limit)
1532
- if (hasDesktopConnection && !parseFromConsole && !restApiSucceeded) {
1530
+ if (hasDesktopConnection && !parseFromConsole && !restApiSucceeded && getDesktopConnector) {
1533
1531
  try {
1534
1532
  logger.info({ fileKey }, "Attempting to get variables via Desktop connection");
1535
- let connector;
1536
- if (getDesktopConnector) {
1537
- connector = await getDesktopConnector();
1538
- }
1539
- else {
1540
- // Fallback: direct connector (legacy path)
1541
- const { FigmaDesktopConnector } = await import('./figma-desktop-connector.js');
1542
- const page = await browserManager.getPage();
1543
- connector = new FigmaDesktopConnector(page);
1544
- await connector.initialize();
1545
- }
1546
- logger.info({ transport: connector.getTransportType?.() || 'unknown' }, "Desktop connector ready");
1533
+ const connector = await getDesktopConnector();
1534
+ logger.info({ transport: connector.getTransportType?.() || 'websocket' }, "Desktop connector ready");
1547
1535
  // When refreshCache is requested, bypass the plugin UI's stale snapshot
1548
1536
  // and fetch live data directly from the Figma Plugin API
1549
1537
  const desktopResult = refreshCache
@@ -1551,7 +1539,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1551
1539
  : await connector.getVariablesFromPluginUI(fileKey);
1552
1540
  // EXECUTE_CODE responses come back wrapped one level deeper:
1553
1541
  // `{ success: true, result: { success: true, variables, ... } }`
1554
- // because handleResult in ui-full.html nests the script return value
1542
+ // because handleResult in ui.html nests the script return value
1555
1543
  // under `result`. The plugin-UI cache path (GET_VARIABLES_DATA) does
1556
1544
  // not nest. Unwrap when we detect the EXECUTE_CODE shape so both
1557
1545
  // paths produce a uniform { success, variables, ... } below. See #68.
@@ -1742,21 +1730,6 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1742
1730
  message: errorMessage,
1743
1731
  stack: errorStack
1744
1732
  }, "Desktop connection failed, falling back to other methods");
1745
- // Try to log to browser console if we have access to page
1746
- try {
1747
- if (browserManager) {
1748
- const page = await browserManager.getPage();
1749
- await page.evaluate((msg, stack) => {
1750
- console.error('[FIGMA_TOOLS] ❌ Desktop connection failed:', msg);
1751
- if (stack) {
1752
- console.error('[FIGMA_TOOLS] Stack trace:', stack);
1753
- }
1754
- }, errorMessage, errorStack);
1755
- }
1756
- }
1757
- catch (logError) {
1758
- // Ignore logging errors
1759
- }
1760
1733
  // Continue to try REST API fallback
1761
1734
  }
1762
1735
  }
@@ -1827,53 +1800,18 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1827
1800
  logger.warn({ error: msg, fileKey }, "REST API fallback failed");
1828
1801
  }
1829
1802
  }
1830
- // LAST RESORT: Parse from console logs if requested
1803
+ // LAST RESORT: parseFromConsole was a Puppeteer-era workflow that read
1804
+ // the magic-string output of a console snippet from the browser's
1805
+ // console buffer. After the Phase 3 CDP cleanup there is no longer a
1806
+ // Puppeteer-attached console for the snippet's output to land in, so
1807
+ // the flag has become a no-op. Tell the caller what to do instead.
1831
1808
  if (parseFromConsole) {
1832
- const consoleMonitor = getConsoleMonitor?.();
1833
- if (!consoleMonitor) {
1834
- throw new Error("Console monitoring not available. Make sure browser is connected to Figma.");
1835
- }
1836
- logger.info({ fileKey }, "Parsing variables from console logs");
1837
- // Get recent logs
1838
- const logs = consoleMonitor.getLogs({ count: 100, level: "log" });
1839
- const varLog = snippetInjector.findVariablesLog(logs);
1840
- if (!varLog) {
1841
- throw new Error("No variables found in console logs.\n\n" +
1842
- "Did you run the snippet in Figma's plugin console? Here's the correct workflow:\n\n" +
1843
- "1. Call figma_get_variables() without parameters (you may have already done this)\n" +
1844
- "2. Copy the provided snippet\n" +
1845
- "3. Open Figma Desktop → Plugins → Development → Open Console\n" +
1846
- "4. Paste and run the snippet in the PLUGIN console (not browser DevTools)\n" +
1847
- "5. Wait for '✅ Variables data captured!' confirmation\n" +
1848
- "6. Then call figma_get_variables({ parseFromConsole: true })\n\n" +
1849
- "Note: The browser console won't work - you need a plugin console for the figma.variables API.");
1850
- }
1851
- // Parse variables from log
1852
- const parsedData = snippetInjector.parseVariablesFromLog(varLog);
1853
- if (!parsedData) {
1854
- throw new Error("Failed to parse variables from console log");
1855
- }
1856
- return {
1857
- content: [
1858
- {
1859
- type: "text",
1860
- text: JSON.stringify({
1861
- fileKey,
1862
- source: "console_capture",
1863
- local: {
1864
- summary: {
1865
- total_variables: parsedData.variables.length,
1866
- total_collections: parsedData.variableCollections.length,
1867
- },
1868
- collections: parsedData.variableCollections,
1869
- variables: parsedData.variables,
1870
- },
1871
- timestamp: parsedData.timestamp,
1872
- enriched: false,
1873
- }),
1874
- },
1875
- ],
1876
- };
1809
+ throw identifiedError("parseFromConsole is no longer supported.\n\n" +
1810
+ "The console-snippet workflow it relied on required a Puppeteer browser " +
1811
+ "connection to Figma Desktop, which was removed in Phase 3 of the cleanup. " +
1812
+ "To extract variables, open the Figma Console MCP Desktop Bridge plugin in " +
1813
+ "Figma Desktop and call figma_get_variables() without parseFromConsole " +
1814
+ "the plugin returns full variable data through the WebSocket bridge.");
1877
1815
  }
1878
1816
  // No more fallback options available
1879
1817
  throw new Error(`Cannot retrieve variables. All methods failed.\n\n` +
@@ -1882,8 +1820,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1882
1820
  `✗ Desktop Bridge (failed or not available)\n` +
1883
1821
  `\nTo fix:\n` +
1884
1822
  `1. If you have FIGMA_ACCESS_TOKEN: Check your token permissions\n` +
1885
- `2. Install and run the Figma Desktop Bridge plugin\n` +
1886
- `3. Alternative: Use parseFromConsole=true with console snippet workflow`);
1823
+ `2. Install and run the Figma Desktop Bridge plugin and re-run this tool`);
1887
1824
  }
1888
1825
  catch (error) {
1889
1826
  logger.error({ error }, "Failed to get variables");
@@ -1898,11 +1835,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
1898
1835
  }
1899
1836
  catch (apiError) {
1900
1837
  const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
1901
- throw new Error(`Cannot retrieve variables or styles. REST API authentication required for both.\n` +
1902
- `Error: ${errorMessage}\n\n` +
1903
- `To fix:\n` +
1904
- `1. Local mode: Set FIGMA_ACCESS_TOKEN environment variable\n` +
1905
- `2. Cloud mode: Authenticate via OAuth`);
1838
+ throw restAuthError("Cannot retrieve variables or styles. REST API authentication required for both.", errorMessage);
1906
1839
  }
1907
1840
  // Use the Styles API directly - much faster than getFile!
1908
1841
  const stylesData = await api.getStyles(fileKey);
@@ -2006,26 +1939,10 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2006
1939
  }
2007
1940
  logger.info({ fileKey, nodeId, format, enrich }, "Fetching component data");
2008
1941
  // PRIORITY 1: Try Desktop Bridge plugin UI first (has reliable description field!)
2009
- if (getDesktopConnector || (getBrowserManager && ensureInitialized)) {
1942
+ if (getDesktopConnector) {
2010
1943
  try {
2011
1944
  logger.info({ nodeId }, "Attempting to get component via Desktop Bridge plugin UI");
2012
- let connector;
2013
- if (getDesktopConnector) {
2014
- connector = await getDesktopConnector();
2015
- }
2016
- else {
2017
- // Fallback: direct connector (legacy path)
2018
- if (ensureInitialized)
2019
- await ensureInitialized();
2020
- const browserManager = getBrowserManager?.();
2021
- if (!browserManager) {
2022
- throw new Error("Browser manager not available after initialization");
2023
- }
2024
- const { FigmaDesktopConnector } = await import('./figma-desktop-connector.js');
2025
- const page = await browserManager.getPage();
2026
- connector = new FigmaDesktopConnector(page);
2027
- await connector.initialize();
2028
- }
1945
+ const connector = await getDesktopConnector();
2029
1946
  const desktopResult = await connector.getComponentFromPluginUI(nodeId);
2030
1947
  if (desktopResult.success && desktopResult.component) {
2031
1948
  logger.info({
@@ -2132,13 +2049,10 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2132
2049
  }
2133
2050
  catch (apiError) {
2134
2051
  const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
2135
- throw new Error(`Cannot retrieve component data. Both Desktop Bridge and REST API are unavailable.\n` +
2136
- `Desktop Bridge: ${getDesktopConnector || (getBrowserManager && ensureInitialized) ? 'Failed (see logs above)' : 'Not available (local mode only)'}\n` +
2137
- `REST API: ${errorMessage}\n\n` +
2138
- `To fix:\n` +
2139
- `1. Local mode: Set FIGMA_ACCESS_TOKEN environment variable, OR ensure Figma Desktop Bridge plugin is running\n` +
2140
- `2. Cloud mode: Authenticate via OAuth\n` +
2141
- `3. Ensure the Desktop Bridge plugin is running in Figma Desktop`);
2052
+ const dbStatus = getDesktopConnector
2053
+ ? "Failed (see logs above)"
2054
+ : "Not available";
2055
+ throw restAuthError("Cannot retrieve component data. Both Desktop Bridge and REST API are unavailable.", `Desktop Bridge: ${dbStatus}; REST API: ${errorMessage}`, "Alternatively: open the Figma Desktop Bridge plugin in Figma Desktop to enable the plugin-based fallback.");
2142
2056
  }
2143
2057
  const componentData = await api.getComponentData(fileKey, nodeId);
2144
2058
  if (!componentData) {
@@ -2267,11 +2181,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2267
2181
  }
2268
2182
  catch (apiError) {
2269
2183
  const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
2270
- throw new Error(`Cannot retrieve styles. REST API authentication required.\n` +
2271
- `Error: ${errorMessage}\n\n` +
2272
- `To fix:\n` +
2273
- `1. Local mode: Set FIGMA_ACCESS_TOKEN environment variable\n` +
2274
- `2. Cloud mode: Authenticate via OAuth`);
2184
+ throw restAuthError("Cannot retrieve styles. REST API authentication required.", errorMessage);
2275
2185
  }
2276
2186
  const url = fileUrl || getCurrentUrl();
2277
2187
  if (!url) {
@@ -2406,13 +2316,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2406
2316
  }
2407
2317
  catch (apiError) {
2408
2318
  const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
2409
- throw new Error(`Cannot render component image. REST API authentication required.\n` +
2410
- `Error: ${errorMessage}\n\n` +
2411
- `To fix:\n` +
2412
- `1. Local mode: Set FIGMA_ACCESS_TOKEN environment variable\n` +
2413
- `2. Cloud mode: Authenticate via OAuth\n\n` +
2414
- `Note: For component screenshots, figma_capture_screenshot may work as an alternative ` +
2415
- `if the Desktop Bridge plugin is connected.`);
2319
+ throw restAuthError("Cannot render component image. REST API authentication required.", errorMessage, "Note: For component screenshots, figma_capture_screenshot may work as an alternative if the Desktop Bridge plugin is connected.");
2416
2320
  }
2417
2321
  const url = fileUrl || getCurrentUrl();
2418
2322
  if (!url) {
@@ -2525,13 +2429,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2525
2429
  }
2526
2430
  catch (apiError) {
2527
2431
  const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
2528
- throw new Error(`Cannot retrieve component for development. REST API authentication required.\n` +
2529
- `Error: ${errorMessage}\n\n` +
2530
- `To fix:\n` +
2531
- `1. Local mode: Set FIGMA_ACCESS_TOKEN environment variable\n` +
2532
- `2. Cloud mode: Authenticate via OAuth\n\n` +
2533
- `Note: For component metadata, figma_get_component has Desktop Bridge fallback ` +
2534
- `that works without token (requires the Desktop Bridge plugin to be connected).`);
2432
+ throw restAuthError("Cannot retrieve component for development. REST API authentication required.", errorMessage, "Note: For component metadata, figma_get_component has a Desktop Bridge fallback that works without a token (requires the Desktop Bridge plugin to be connected).");
2535
2433
  }
2536
2434
  const url = fileUrl || getCurrentUrl();
2537
2435
  if (!url) {
@@ -2971,11 +2869,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
2971
2869
  }
2972
2870
  catch (apiError) {
2973
2871
  const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
2974
- throw new Error(`Cannot retrieve file data for plugin development. REST API authentication required.\n` +
2975
- `Error: ${errorMessage}\n\n` +
2976
- `To fix:\n` +
2977
- `1. Local mode: Set FIGMA_ACCESS_TOKEN environment variable\n` +
2978
- `2. Cloud mode: Authenticate via OAuth`);
2872
+ throw restAuthError("Cannot retrieve file data for plugin development. REST API authentication required.", errorMessage);
2979
2873
  }
2980
2874
  const url = fileUrl || getCurrentUrl();
2981
2875
  if (!url) {
@@ -3166,33 +3060,6 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
3166
3060
  result = { success: true, image: result };
3167
3061
  }
3168
3062
  }
3169
- // Legacy CDP fallback (only when no connector factory is available)
3170
- if (!result && !getDesktopConnector) {
3171
- const browserManager = getBrowserManager?.();
3172
- if (!browserManager) {
3173
- throw new Error("Desktop Bridge not available. To capture screenshots:\n" +
3174
- "1. Open your Figma file in Figma Desktop\n" +
3175
- "2. Install and run the 'Figma Console MCP' plugin\n" +
3176
- "3. Ensure the plugin shows 'MCP ready' status");
3177
- }
3178
- if (ensureInitialized) {
3179
- await ensureInitialized();
3180
- }
3181
- const page = await browserManager.getPage();
3182
- const frames = page.frames();
3183
- for (const frame of frames) {
3184
- try {
3185
- const hasFunction = await frame.evaluate('typeof window.captureScreenshot === "function"');
3186
- if (hasFunction) {
3187
- result = await frame.evaluate(`window.captureScreenshot(${JSON.stringify(nodeId || '')}, ${JSON.stringify({ format, scale })})`);
3188
- break;
3189
- }
3190
- }
3191
- catch {
3192
- continue;
3193
- }
3194
- }
3195
- }
3196
3063
  if (!result) {
3197
3064
  throw new Error("Desktop Bridge plugin not found. Ensure the 'Figma Console MCP' plugin is running in Figma Desktop.");
3198
3065
  }
@@ -3271,33 +3138,6 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
3271
3138
  logger.info({ transport: connector.getTransportType?.() || 'unknown' }, "Instance properties via connector");
3272
3139
  result = await connector.setInstanceProperties(nodeId, properties);
3273
3140
  }
3274
- // Legacy CDP fallback (only when no connector factory is available)
3275
- if (!result && !getDesktopConnector) {
3276
- const browserManager = getBrowserManager?.();
3277
- if (!browserManager) {
3278
- throw new Error("Desktop Bridge not available. To set instance properties:\n" +
3279
- "1. Open your Figma file in Figma Desktop\n" +
3280
- "2. Install and run the 'Figma Console MCP' plugin\n" +
3281
- "3. Ensure the plugin shows 'MCP ready' status");
3282
- }
3283
- if (ensureInitialized) {
3284
- await ensureInitialized();
3285
- }
3286
- const page = await browserManager.getPage();
3287
- const frames = page.frames();
3288
- for (const frame of frames) {
3289
- try {
3290
- const hasFunction = await frame.evaluate('typeof window.setInstanceProperties === "function"');
3291
- if (hasFunction) {
3292
- result = await frame.evaluate(`window.setInstanceProperties(${JSON.stringify(nodeId)}, ${JSON.stringify(properties)})`);
3293
- break;
3294
- }
3295
- }
3296
- catch {
3297
- continue;
3298
- }
3299
- }
3300
- }
3301
3141
  if (!result) {
3302
3142
  throw new Error("Desktop Bridge plugin not found. Ensure the 'Figma Console MCP' plugin is running in Figma Desktop.");
3303
3143
  }