figma-console-mcp 0.1.0

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 (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +328 -0
  3. package/dist/browser/base.d.ts +50 -0
  4. package/dist/browser/base.d.ts.map +1 -0
  5. package/dist/browser/base.js +6 -0
  6. package/dist/browser/base.js.map +1 -0
  7. package/dist/browser/local.d.ts +66 -0
  8. package/dist/browser/local.d.ts.map +1 -0
  9. package/dist/browser/local.js +223 -0
  10. package/dist/browser/local.js.map +1 -0
  11. package/dist/cloudflare/browser/base.js +5 -0
  12. package/dist/cloudflare/browser/cloudflare.js +156 -0
  13. package/dist/cloudflare/browser-manager.js +157 -0
  14. package/dist/cloudflare/core/config.js +161 -0
  15. package/dist/cloudflare/core/console-monitor.js +382 -0
  16. package/dist/cloudflare/core/enrichment/enrichment-service.js +272 -0
  17. package/dist/cloudflare/core/enrichment/index.js +7 -0
  18. package/dist/cloudflare/core/enrichment/relationship-mapper.js +351 -0
  19. package/dist/cloudflare/core/enrichment/style-resolver.js +326 -0
  20. package/dist/cloudflare/core/figma-api.js +273 -0
  21. package/dist/cloudflare/core/figma-desktop-connector.js +383 -0
  22. package/dist/cloudflare/core/figma-style-extractor.js +311 -0
  23. package/dist/cloudflare/core/figma-tools.js +2299 -0
  24. package/dist/cloudflare/core/logger.js +53 -0
  25. package/dist/cloudflare/core/snippet-injector.js +96 -0
  26. package/dist/cloudflare/core/types/enriched.js +5 -0
  27. package/dist/cloudflare/core/types/index.js +4 -0
  28. package/dist/cloudflare/index.js +1059 -0
  29. package/dist/cloudflare/test-browser.js +88 -0
  30. package/dist/config.d.ts +17 -0
  31. package/dist/config.d.ts.map +1 -0
  32. package/dist/config.js +141 -0
  33. package/dist/config.js.map +1 -0
  34. package/dist/core/config.d.ts +17 -0
  35. package/dist/core/config.d.ts.map +1 -0
  36. package/dist/core/config.js +162 -0
  37. package/dist/core/config.js.map +1 -0
  38. package/dist/core/console-monitor.d.ts +81 -0
  39. package/dist/core/console-monitor.d.ts.map +1 -0
  40. package/dist/core/console-monitor.js +383 -0
  41. package/dist/core/console-monitor.js.map +1 -0
  42. package/dist/core/enrichment/enrichment-service.d.ts +52 -0
  43. package/dist/core/enrichment/enrichment-service.d.ts.map +1 -0
  44. package/dist/core/enrichment/enrichment-service.js +273 -0
  45. package/dist/core/enrichment/enrichment-service.js.map +1 -0
  46. package/dist/core/enrichment/index.d.ts +8 -0
  47. package/dist/core/enrichment/index.d.ts.map +1 -0
  48. package/dist/core/enrichment/index.js +8 -0
  49. package/dist/core/enrichment/index.js.map +1 -0
  50. package/dist/core/enrichment/relationship-mapper.d.ts +106 -0
  51. package/dist/core/enrichment/relationship-mapper.d.ts.map +1 -0
  52. package/dist/core/enrichment/relationship-mapper.js +352 -0
  53. package/dist/core/enrichment/relationship-mapper.js.map +1 -0
  54. package/dist/core/enrichment/style-resolver.d.ts +80 -0
  55. package/dist/core/enrichment/style-resolver.d.ts.map +1 -0
  56. package/dist/core/enrichment/style-resolver.js +327 -0
  57. package/dist/core/enrichment/style-resolver.js.map +1 -0
  58. package/dist/core/figma-api.d.ts +137 -0
  59. package/dist/core/figma-api.d.ts.map +1 -0
  60. package/dist/core/figma-api.js +274 -0
  61. package/dist/core/figma-api.js.map +1 -0
  62. package/dist/core/figma-desktop-connector.d.ts +52 -0
  63. package/dist/core/figma-desktop-connector.d.ts.map +1 -0
  64. package/dist/core/figma-desktop-connector.js +384 -0
  65. package/dist/core/figma-desktop-connector.js.map +1 -0
  66. package/dist/core/figma-style-extractor.d.ts +76 -0
  67. package/dist/core/figma-style-extractor.d.ts.map +1 -0
  68. package/dist/core/figma-style-extractor.js +312 -0
  69. package/dist/core/figma-style-extractor.js.map +1 -0
  70. package/dist/core/figma-tools.d.ts +15 -0
  71. package/dist/core/figma-tools.d.ts.map +1 -0
  72. package/dist/core/figma-tools.js +2300 -0
  73. package/dist/core/figma-tools.js.map +1 -0
  74. package/dist/core/logger.d.ts +22 -0
  75. package/dist/core/logger.d.ts.map +1 -0
  76. package/dist/core/logger.js +54 -0
  77. package/dist/core/logger.js.map +1 -0
  78. package/dist/core/snippet-injector.d.ts +24 -0
  79. package/dist/core/snippet-injector.d.ts.map +1 -0
  80. package/dist/core/snippet-injector.js +97 -0
  81. package/dist/core/snippet-injector.js.map +1 -0
  82. package/dist/core/types/enriched.d.ts +213 -0
  83. package/dist/core/types/enriched.d.ts.map +1 -0
  84. package/dist/core/types/enriched.js +6 -0
  85. package/dist/core/types/enriched.js.map +1 -0
  86. package/dist/core/types/index.d.ts +112 -0
  87. package/dist/core/types/index.d.ts.map +1 -0
  88. package/dist/core/types/index.js +5 -0
  89. package/dist/core/types/index.js.map +1 -0
  90. package/dist/index.d.ts +8 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +72 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/local.d.ts +57 -0
  95. package/dist/local.d.ts.map +1 -0
  96. package/dist/local.js +668 -0
  97. package/dist/local.js.map +1 -0
  98. package/dist/logger.d.ts +22 -0
  99. package/dist/logger.d.ts.map +1 -0
  100. package/dist/logger.js +45 -0
  101. package/dist/logger.js.map +1 -0
  102. package/dist/server.d.ts +40 -0
  103. package/dist/server.d.ts.map +1 -0
  104. package/dist/server.js +99 -0
  105. package/dist/server.js.map +1 -0
  106. package/dist/tools/index.d.ts +15 -0
  107. package/dist/tools/index.d.ts.map +1 -0
  108. package/dist/tools/index.js +184 -0
  109. package/dist/tools/index.js.map +1 -0
  110. package/dist/types/index.d.ts +102 -0
  111. package/dist/types/index.d.ts.map +1 -0
  112. package/dist/types/index.js +6 -0
  113. package/dist/types/index.js.map +1 -0
  114. package/figma-desktop-bridge/README.md +232 -0
  115. package/figma-desktop-bridge/code.js +133 -0
  116. package/figma-desktop-bridge/manifest.json +13 -0
  117. package/figma-desktop-bridge/ui.html +200 -0
  118. package/package.json +77 -0
@@ -0,0 +1,232 @@
1
+ # Figma Desktop Bridge
2
+
3
+ A Figma plugin that bridges the Variables API and Component descriptions to MCP (Model Context Protocol) clients without requiring an Enterprise plan.
4
+
5
+ ## Overview
6
+
7
+ This plugin enables AI assistants like Claude Code and Claude Desktop to access your Figma variables AND component descriptions through the MCP protocol. It bypasses both Figma's plugin sandbox restrictions and the REST API's component description bug.
8
+
9
+ ## Architecture
10
+
11
+ **For Variables (pre-loaded):**
12
+ ```
13
+ Figma Plugin Worker → postMessage → Plugin UI Iframe → window object → Puppeteer → MCP Server
14
+ ```
15
+
16
+ **For Components (on-demand):**
17
+ ```
18
+ MCP Request → Plugin UI → postMessage → Plugin Worker → figma.getNodeByIdAsync() → Returns with description
19
+ ```
20
+
21
+ **Key Features:**
22
+ - ✅ No Enterprise plan required for variables
23
+ - ✅ Access all local variables and collections
24
+ - ✅ Reliable component descriptions (bypasses REST API bug)
25
+ - ✅ Supports multiple variable modes
26
+ - ✅ On-demand component data retrieval
27
+ - ✅ Persistent connection (stays open until closed)
28
+ - ✅ Clean, minimal UI
29
+ - ✅ Real-time data updates
30
+
31
+ ## Installation
32
+
33
+ ### Quick Install (Recommended)
34
+
35
+ 1. **Open Figma Desktop**
36
+ 2. **Go to Plugins → Development → Import plugin from manifest...**
37
+ 3. **Navigate to:** `/path/to/figma-console-mcp/figma-desktop-bridge/manifest.json`
38
+ 4. **Click "Open"**
39
+
40
+ The plugin will appear in your Development plugins list as "Figma Desktop Bridge".
41
+
42
+ ### Manual Installation
43
+
44
+ Alternatively, you can install from the plugin directory:
45
+
46
+ ```bash
47
+ # From the figma-console-mcp directory
48
+ cd figma-desktop-bridge
49
+
50
+ # Figma will use these files:
51
+ # - manifest.json (plugin configuration)
52
+ # - code.js (plugin worker logic)
53
+ # - ui.html (plugin UI interface)
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ ### Running the Plugin
59
+
60
+ 1. **Open your Figma file** with variables and/or components
61
+ 2. **Run the plugin:** Right-click → Plugins → Development → Figma Desktop Bridge
62
+ 3. **Wait for confirmation:** Plugin UI will show "✓ Desktop Bridge active"
63
+
64
+ The plugin will:
65
+ - Fetch all local variables and collections on startup
66
+ - Display counts in the UI (e.g., "Variables: 404 in 2 collections")
67
+ - Store variables in `window.__figmaVariablesData`
68
+ - Provide on-demand component data via `window.requestComponentData(nodeId)`
69
+ - Keep running until manually closed
70
+
71
+ ### Accessing Data via MCP
72
+
73
+ Once the plugin is running, MCP clients can access both variables and components:
74
+
75
+ **Variables (pre-loaded):**
76
+ ```typescript
77
+ // From Claude Code or Claude Desktop
78
+ figma_get_variables({
79
+ format: "summary" // or "filtered" or "full"
80
+ })
81
+ ```
82
+
83
+ **Components (on-demand):**
84
+ ```typescript
85
+ // Request component with description
86
+ figma_get_component({
87
+ fileUrl: "https://figma.com/design/YOUR_FILE_KEY",
88
+ nodeId: "279:2861"
89
+ })
90
+ ```
91
+
92
+ **Important:** Keep the plugin running while querying. Variables are pre-loaded, but component data is fetched on-demand when requested.
93
+
94
+ ## How It Works
95
+
96
+ ### Plugin Worker (code.js)
97
+
98
+ **On Startup (Variables):**
99
+ 1. Uses Figma's Variables API to fetch all local variables
100
+ 2. Formats data with full mode values
101
+ 3. Sends to UI via `postMessage`
102
+
103
+ **On Request (Components):**
104
+ 1. Listens for component requests via `figma.ui.onmessage`
105
+ 2. Uses `figma.getNodeByIdAsync(nodeId)` to fetch component
106
+ 3. Extracts description, descriptionMarkdown, and metadata
107
+ 4. Sends response back to UI via `postMessage` with requestId
108
+
109
+ ### Plugin UI (ui.html)
110
+
111
+ **Variables Flow:**
112
+ 1. Listens for `VARIABLES_DATA` message from worker
113
+ 2. Stores data on `window.__figmaVariablesData`
114
+ 3. Sets `window.__figmaVariablesReady = true`
115
+ 4. Displays status to user
116
+
117
+ **Components Flow:**
118
+ 1. Exposes `window.requestComponentData(nodeId)` function
119
+ 2. Returns a Promise that resolves when worker responds
120
+ 3. Sends request to worker via `parent.postMessage()`
121
+ 4. Resolves promise when `COMPONENT_DATA` message received
122
+ 5. Includes 10-second timeout and error handling
123
+
124
+ ### MCP Desktop Connector
125
+
126
+ **Variables (getVariablesFromPluginUI):**
127
+ 1. Connects to Figma Desktop via remote debugging port (9222)
128
+ 2. Enumerates plugin UI iframes
129
+ 3. Checks for `window.__figmaVariablesData` availability
130
+ 4. Retrieves pre-loaded variables data
131
+
132
+ **Components (getComponentFromPluginUI):**
133
+ 1. Connects to same Figma Desktop instance
134
+ 2. Finds iframe with `window.requestComponentData` function
135
+ 3. Calls function with nodeId parameter
136
+ 4. Returns Promise with component data including descriptions
137
+
138
+ ## Troubleshooting
139
+
140
+ ### Plugin doesn't appear in menu
141
+ - Make sure Figma Desktop is running (not browser)
142
+ - Check that manifest.json path is correct
143
+ - Try **Plugins → Development → Refresh plugin list**
144
+
145
+ ### "No plugin UI found with variables data" or "No plugin UI found with requestComponentData"
146
+ - Ensure plugin is running (check for open plugin window showing "✓ Desktop Bridge active")
147
+ - Try closing and reopening the plugin
148
+ - Check browser console for errors
149
+ - Verify Figma Desktop was launched with `--remote-debugging-port=9222`
150
+
151
+ ### Variables not updating
152
+ - Close and reopen the plugin to refresh data
153
+ - Use `refreshCache: true` parameter in MCP call
154
+ - Check that you're viewing the correct Figma file
155
+
156
+ ### Component descriptions are empty or missing
157
+ - **First, verify in Figma:** Check if the component actually has a description set
158
+ - If using REST API fallback (not Desktop Bridge), descriptions may be missing due to known Figma API bug
159
+ - Ensure the plugin is running - component data requires active plugin connection
160
+ - Check that the nodeId is correct (format: "123:456")
161
+
162
+ ### Component request times out
163
+ - Ensure plugin is running and shows "Desktop Bridge active"
164
+ - Check that the component exists in the current file
165
+ - Verify nodeId format is correct
166
+ - Timeout is set to 10 seconds - complex files may take longer
167
+
168
+ ### Empty or outdated data
169
+ - Plugin fetches variables on load - rerun plugin after making variable changes
170
+ - Component data is fetched on-demand - always returns current state
171
+ - Cache TTL is 5 minutes for variables - use `refreshCache: true` for immediate updates
172
+ - Ensure you're in the correct file (plugin reads current file's data)
173
+
174
+ ## Development
175
+
176
+ ### File Structure
177
+ ```
178
+ figma-desktop-bridge/
179
+ ├── manifest.json # Plugin configuration
180
+ ├── code.js # Plugin worker (accesses Figma API)
181
+ ├── ui.html # Plugin UI (stores/requests data for MCP access)
182
+ └── README.md # This file
183
+ ```
184
+
185
+ ### Console Logging
186
+
187
+ The plugin logs to Figma's console:
188
+
189
+ **Variables (startup):**
190
+ ```
191
+ 🌉 [Desktop Bridge] Plugin loaded and ready
192
+ 🌉 [Desktop Bridge] Fetching variables...
193
+ 🌉 [Desktop Bridge] Found 404 variables in 2 collections
194
+ 🌉 [Desktop Bridge] Variables data sent to UI successfully
195
+ 🌉 [Desktop Bridge] UI iframe now has variables data accessible via window.__figmaVariablesData
196
+ ```
197
+
198
+ **Components (on-demand):**
199
+ ```
200
+ 🌉 [Desktop Bridge] Fetching component: 279:2861
201
+ 🌉 [Desktop Bridge] Component data ready. Has description: true
202
+ ```
203
+
204
+ **Ready state:**
205
+ ```
206
+ 🌉 [Desktop Bridge] Ready to handle component requests
207
+ 🌉 [Desktop Bridge] Plugin will stay open until manually closed
208
+ ```
209
+
210
+ View logs: **Plugins → Development → Open Console** (Cmd+Option+I on Mac)
211
+
212
+ ## Security
213
+
214
+ - Plugin requires **no network access** (allowedDomains: ["none"])
215
+ - Data never leaves Figma Desktop
216
+ - Uses standard Figma Plugin API (no unofficial APIs)
217
+ - Read-only access (cannot modify variables or components)
218
+ - Component requests are scoped to current file only
219
+
220
+ ## Why Desktop Bridge for Components?
221
+
222
+ Figma's REST API has a known bug where component `description` and `descriptionMarkdown` fields are often missing or outdated. This is particularly problematic for:
223
+
224
+ - **Local project components** (not published to team libraries)
225
+ - **Unpublished components** in active development
226
+ - **Team collaboration** where descriptions contain important usage guidelines
227
+
228
+ The Desktop Bridge plugin bypasses this limitation by using the Figma Plugin API (`figma.getNodeByIdAsync()`), which has reliable, real-time access to all component fields including descriptions. This makes it ideal for teams working with local components in shared project files.
229
+
230
+ ## License
231
+
232
+ Part of the figma-console-mcp project.
@@ -0,0 +1,133 @@
1
+ // Figma Desktop Bridge - MCP Plugin
2
+ // Bridges Figma API to MCP clients via plugin UI window
3
+ // Supports: Variables, Components, Styles, and more
4
+ // Uses postMessage to communicate with UI, bypassing worker sandbox limitations
5
+ // Puppeteer can access UI iframe's window context to retrieve data
6
+
7
+ console.log('🌉 [Desktop Bridge] Plugin loaded and ready');
8
+
9
+ // Show UI to keep plugin running and receive data
10
+ figma.showUI(__html__, { width: 320, height: 240, visible: true });
11
+
12
+ // Immediately fetch and send variables data to UI
13
+ (async () => {
14
+ try {
15
+ console.log('🌉 [Desktop Bridge] Fetching variables...');
16
+
17
+ // Get all local variables and collections
18
+ const variables = await figma.variables.getLocalVariablesAsync();
19
+ const collections = await figma.variables.getLocalVariableCollectionsAsync();
20
+
21
+ console.log(`🌉 [Desktop Bridge] Found ${variables.length} variables in ${collections.length} collections`);
22
+
23
+ // Format the data
24
+ const variablesData = {
25
+ success: true,
26
+ timestamp: Date.now(),
27
+ fileKey: figma.fileKey || null,
28
+ variables: variables.map(v => ({
29
+ id: v.id,
30
+ name: v.name,
31
+ key: v.key,
32
+ resolvedType: v.resolvedType,
33
+ valuesByMode: v.valuesByMode,
34
+ variableCollectionId: v.variableCollectionId,
35
+ scopes: v.scopes,
36
+ description: v.description,
37
+ hiddenFromPublishing: v.hiddenFromPublishing
38
+ })),
39
+ variableCollections: collections.map(c => ({
40
+ id: c.id,
41
+ name: c.name,
42
+ key: c.key,
43
+ modes: c.modes,
44
+ defaultModeId: c.defaultModeId,
45
+ variableIds: c.variableIds
46
+ }))
47
+ };
48
+
49
+ // Send to UI via postMessage
50
+ figma.ui.postMessage({
51
+ type: 'VARIABLES_DATA',
52
+ data: variablesData
53
+ });
54
+
55
+ console.log('🌉 [Desktop Bridge] Variables data sent to UI successfully');
56
+ console.log('🌉 [Desktop Bridge] UI iframe now has variables data accessible via window.__figmaVariablesData');
57
+
58
+ } catch (error) {
59
+ console.error('🌉 [Desktop Bridge] Error fetching variables:', error);
60
+ figma.ui.postMessage({
61
+ type: 'ERROR',
62
+ error: error.message || String(error)
63
+ });
64
+ }
65
+ })();
66
+
67
+ // Listen for requests from UI (e.g., component data requests)
68
+ figma.ui.onmessage = async (msg) => {
69
+ if (msg.type === 'GET_COMPONENT') {
70
+ try {
71
+ console.log(`🌉 [Desktop Bridge] Fetching component: ${msg.nodeId}`);
72
+
73
+ const node = await figma.getNodeByIdAsync(msg.nodeId);
74
+
75
+ if (!node) {
76
+ throw new Error(`Node not found: ${msg.nodeId}`);
77
+ }
78
+
79
+ if (node.type !== 'COMPONENT' && node.type !== 'COMPONENT_SET' && node.type !== 'INSTANCE') {
80
+ throw new Error(`Node is not a component. Type: ${node.type}`);
81
+ }
82
+
83
+ // Extract component data including description fields
84
+ const componentData = {
85
+ success: true,
86
+ timestamp: Date.now(),
87
+ nodeId: msg.nodeId,
88
+ component: {
89
+ id: node.id,
90
+ name: node.name,
91
+ type: node.type,
92
+ description: node.description || null,
93
+ descriptionMarkdown: node.descriptionMarkdown || null,
94
+ visible: node.visible,
95
+ locked: node.locked,
96
+ // For components with properties
97
+ componentPropertyDefinitions: (node.type === 'COMPONENT' || node.type === 'COMPONENT_SET')
98
+ ? node.componentPropertyDefinitions
99
+ : undefined,
100
+ // Get children info (lightweight)
101
+ children: node.children ? node.children.map(child => ({
102
+ id: child.id,
103
+ name: child.name,
104
+ type: child.type
105
+ })) : undefined
106
+ }
107
+ };
108
+
109
+ console.log(`🌉 [Desktop Bridge] Component data ready. Has description: ${!!componentData.component.description}`);
110
+
111
+ // Send to UI
112
+ figma.ui.postMessage({
113
+ type: 'COMPONENT_DATA',
114
+ requestId: msg.requestId, // Echo back the request ID
115
+ data: componentData
116
+ });
117
+
118
+ } catch (error) {
119
+ console.error(`🌉 [Desktop Bridge] Error fetching component:`, error);
120
+ figma.ui.postMessage({
121
+ type: 'COMPONENT_ERROR',
122
+ requestId: msg.requestId,
123
+ error: error.message || String(error)
124
+ });
125
+ }
126
+ }
127
+ };
128
+
129
+ console.log('🌉 [Desktop Bridge] Ready to handle component requests');
130
+ console.log('🌉 [Desktop Bridge] Plugin will stay open until manually closed');
131
+
132
+ // Plugin stays open - no auto-close
133
+ // UI iframe remains accessible for Puppeteer to read data from window object
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "Figma Desktop Bridge",
3
+ "id": "figma-desktop-bridge-mcp",
4
+ "api": "1.0.0",
5
+ "main": "code.js",
6
+ "ui": "ui.html",
7
+ "editorType": ["figma", "dev"],
8
+ "capabilities": ["inspect"],
9
+ "documentAccess": "dynamic-page",
10
+ "networkAccess": {
11
+ "allowedDomains": ["none"]
12
+ }
13
+ }
@@ -0,0 +1,200 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <style>
6
+ * {
7
+ box-sizing: border-box;
8
+ margin: 0;
9
+ padding: 0;
10
+ }
11
+
12
+ body {
13
+ font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
14
+ background: #ffffff;
15
+ color: #1a1a1a;
16
+ padding: 24px;
17
+ min-height: 100vh;
18
+ display: flex;
19
+ flex-direction: column;
20
+ align-items: center;
21
+ justify-content: center;
22
+ }
23
+
24
+ .container {
25
+ max-width: 320px;
26
+ width: 100%;
27
+ }
28
+
29
+ .status {
30
+ font-size: 14px;
31
+ font-weight: 500;
32
+ margin-bottom: 8px;
33
+ display: flex;
34
+ align-items: center;
35
+ gap: 8px;
36
+ }
37
+
38
+ .icon {
39
+ font-size: 16px;
40
+ line-height: 1;
41
+ }
42
+
43
+ .details {
44
+ font-size: 12px;
45
+ color: #666;
46
+ line-height: 1.6;
47
+ margin-top: 8px;
48
+ }
49
+
50
+ .details strong {
51
+ color: #1a1a1a;
52
+ font-weight: 500;
53
+ }
54
+
55
+ .success {
56
+ color: #059669;
57
+ }
58
+
59
+ .error {
60
+ color: #dc2626;
61
+ }
62
+
63
+ .loading {
64
+ color: #6b7280;
65
+ }
66
+
67
+ .divider {
68
+ height: 1px;
69
+ background: #e5e7eb;
70
+ margin: 16px 0;
71
+ }
72
+
73
+ .footer {
74
+ font-size: 11px;
75
+ color: #9ca3af;
76
+ margin-top: 16px;
77
+ text-align: center;
78
+ }
79
+ </style>
80
+ </head>
81
+ <body>
82
+ <div class="container">
83
+ <div id="content">
84
+ <div class="status loading">
85
+ <span class="icon">⏳</span>
86
+ <span>Loading data...</span>
87
+ </div>
88
+ <div class="details">Connecting to Figma API</div>
89
+ </div>
90
+ <div class="divider"></div>
91
+ <div class="footer">Made with ❤️ by Southleft</div>
92
+ </div>
93
+
94
+ <script>
95
+ // Global variables to store data for Puppeteer access
96
+ window.__figmaVariablesData = null;
97
+ window.__figmaVariablesReady = false;
98
+ window.__figmaComponentData = null;
99
+ window.__figmaComponentRequests = new Map(); // Track pending requests
100
+
101
+ let requestIdCounter = 0;
102
+
103
+ // Function to request component data from plugin worker
104
+ window.requestComponentData = (nodeId) => {
105
+ return new Promise((resolve, reject) => {
106
+ const requestId = `component_${++requestIdCounter}_${Date.now()}`;
107
+
108
+ // Store the promise callbacks
109
+ window.__figmaComponentRequests.set(requestId, { resolve, reject });
110
+
111
+ // Send request to plugin worker
112
+ parent.postMessage({
113
+ pluginMessage: {
114
+ type: 'GET_COMPONENT',
115
+ requestId: requestId,
116
+ nodeId: nodeId
117
+ }
118
+ }, '*');
119
+
120
+ console.log('[Desktop Bridge] Requested component:', nodeId, 'requestId:', requestId);
121
+
122
+ // Timeout after 10 seconds
123
+ setTimeout(() => {
124
+ if (window.__figmaComponentRequests.has(requestId)) {
125
+ window.__figmaComponentRequests.delete(requestId);
126
+ reject(new Error('Component request timed out'));
127
+ }
128
+ }, 10000);
129
+ });
130
+ };
131
+
132
+ // Listen for messages from plugin worker
133
+ window.onmessage = (event) => {
134
+ const msg = event.data.pluginMessage;
135
+
136
+ if (msg.type === 'VARIABLES_DATA') {
137
+ // Store data on window object for Puppeteer
138
+ window.__figmaVariablesData = msg.data;
139
+ window.__figmaVariablesReady = true;
140
+
141
+ const varCount = msg.data.variables?.length || 0;
142
+ const collCount = msg.data.variableCollections?.length || 0;
143
+
144
+ // Update UI
145
+ document.getElementById('content').innerHTML = `
146
+ <div class="status success">
147
+ <span class="icon">✓</span>
148
+ <span>Desktop Bridge active</span>
149
+ </div>
150
+ <div class="details">
151
+ <strong>Variables:</strong> ${varCount} in ${collCount} ${collCount === 1 ? 'collection' : 'collections'}<br>
152
+ <strong>Components:</strong> On-demand via MCP<br>
153
+ <br>
154
+ <code style="font-size: 10px; background: #f3f4f6; padding: 2px 6px; border-radius: 4px; display: block; margin-top: 4px;">window.__figmaVariablesData</code>
155
+ <code style="font-size: 10px; background: #f3f4f6; padding: 2px 6px; border-radius: 4px; display: block; margin-top: 2px;">window.requestComponentData(id)</code>
156
+ </div>
157
+ `;
158
+
159
+ console.log('[Desktop Bridge] Variables ready:', { varCount, collCount });
160
+
161
+ } else if (msg.type === 'COMPONENT_DATA') {
162
+ // Store component data
163
+ window.__figmaComponentData = msg.data;
164
+
165
+ console.log('[Desktop Bridge] Component data received:', msg.data.component.name);
166
+
167
+ // Resolve the promise
168
+ const request = window.__figmaComponentRequests.get(msg.requestId);
169
+ if (request) {
170
+ request.resolve(msg.data);
171
+ window.__figmaComponentRequests.delete(msg.requestId);
172
+ }
173
+
174
+ } else if (msg.type === 'COMPONENT_ERROR') {
175
+ console.error('[Desktop Bridge] Component error:', msg.error);
176
+
177
+ // Reject the promise
178
+ const request = window.__figmaComponentRequests.get(msg.requestId);
179
+ if (request) {
180
+ request.reject(new Error(msg.error));
181
+ window.__figmaComponentRequests.delete(msg.requestId);
182
+ }
183
+
184
+ } else if (msg.type === 'ERROR') {
185
+ window.__figmaVariablesReady = false;
186
+
187
+ document.getElementById('content').innerHTML = `
188
+ <div class="status error">
189
+ <span class="icon">✕</span>
190
+ <span>Error loading data</span>
191
+ </div>
192
+ <div class="details">${msg.error}</div>
193
+ `;
194
+
195
+ console.error('[Desktop Bridge] Error:', msg.error);
196
+ }
197
+ };
198
+ </script>
199
+ </body>
200
+ </html>
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "figma-console-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for accessing Figma plugin console logs and screenshots via Cloudflare Workers or local mode",
5
+ "type": "module",
6
+ "main": "dist/local.js",
7
+ "types": "dist/local.d.ts",
8
+ "bin": {
9
+ "figma-console-mcp": "./dist/local.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "figma-desktop-bridge",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "prepublishOnly": "npm run build",
19
+ "deploy": "wrangler deploy",
20
+ "dev": "wrangler dev",
21
+ "dev:local": "tsx src/local.ts",
22
+ "build": "npm run build:local && npm run build:cloudflare",
23
+ "build:local": "tsc -p tsconfig.local.json",
24
+ "build:cloudflare": "tsc -p tsconfig.cloudflare.json",
25
+ "start": "wrangler dev",
26
+ "test": "jest",
27
+ "test:watch": "jest --watch",
28
+ "test:coverage": "jest --coverage",
29
+ "format": "biome format --write",
30
+ "lint:fix": "biome lint --fix",
31
+ "cf-typegen": "wrangler types",
32
+ "type-check": "tsc --noEmit"
33
+ },
34
+ "keywords": [
35
+ "mcp",
36
+ "figma",
37
+ "plugin",
38
+ "console",
39
+ "debugging",
40
+ "ai",
41
+ "anthropic",
42
+ "claude",
43
+ "cloudflare",
44
+ "workers"
45
+ ],
46
+ "author": "Your Name",
47
+ "license": "MIT",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/southleft/figma-console-mcp.git"
51
+ },
52
+ "engines": {
53
+ "node": ">=18.0.0"
54
+ },
55
+ "dependencies": {
56
+ "@cloudflare/puppeteer": "^1.0.4",
57
+ "@modelcontextprotocol/sdk": "1.19.1",
58
+ "agents": "^0.2.8",
59
+ "chrome-remote-interface": "^0.33.2",
60
+ "pino": "^9.5.0",
61
+ "pino-pretty": "^13.0.0",
62
+ "puppeteer-core": "^23.11.1",
63
+ "uuid": "^11.0.3",
64
+ "zod": "^3.25.76"
65
+ },
66
+ "devDependencies": {
67
+ "@biomejs/biome": "^2.2.5",
68
+ "@types/jest": "^29.5.14",
69
+ "@types/node": "^22.10.2",
70
+ "@types/uuid": "^10.0.0",
71
+ "jest": "^29.7.0",
72
+ "ts-jest": "^29.2.5",
73
+ "tsx": "^4.19.2",
74
+ "typescript": "5.9.3",
75
+ "wrangler": "^4.42.0"
76
+ }
77
+ }