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.
- package/LICENSE +21 -0
- package/README.md +328 -0
- package/dist/browser/base.d.ts +50 -0
- package/dist/browser/base.d.ts.map +1 -0
- package/dist/browser/base.js +6 -0
- package/dist/browser/base.js.map +1 -0
- package/dist/browser/local.d.ts +66 -0
- package/dist/browser/local.d.ts.map +1 -0
- package/dist/browser/local.js +223 -0
- package/dist/browser/local.js.map +1 -0
- package/dist/cloudflare/browser/base.js +5 -0
- package/dist/cloudflare/browser/cloudflare.js +156 -0
- package/dist/cloudflare/browser-manager.js +157 -0
- package/dist/cloudflare/core/config.js +161 -0
- package/dist/cloudflare/core/console-monitor.js +382 -0
- package/dist/cloudflare/core/enrichment/enrichment-service.js +272 -0
- package/dist/cloudflare/core/enrichment/index.js +7 -0
- package/dist/cloudflare/core/enrichment/relationship-mapper.js +351 -0
- package/dist/cloudflare/core/enrichment/style-resolver.js +326 -0
- package/dist/cloudflare/core/figma-api.js +273 -0
- package/dist/cloudflare/core/figma-desktop-connector.js +383 -0
- package/dist/cloudflare/core/figma-style-extractor.js +311 -0
- package/dist/cloudflare/core/figma-tools.js +2299 -0
- package/dist/cloudflare/core/logger.js +53 -0
- package/dist/cloudflare/core/snippet-injector.js +96 -0
- package/dist/cloudflare/core/types/enriched.js +5 -0
- package/dist/cloudflare/core/types/index.js +4 -0
- package/dist/cloudflare/index.js +1059 -0
- package/dist/cloudflare/test-browser.js +88 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +141 -0
- package/dist/config.js.map +1 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +162 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/console-monitor.d.ts +81 -0
- package/dist/core/console-monitor.d.ts.map +1 -0
- package/dist/core/console-monitor.js +383 -0
- package/dist/core/console-monitor.js.map +1 -0
- package/dist/core/enrichment/enrichment-service.d.ts +52 -0
- package/dist/core/enrichment/enrichment-service.d.ts.map +1 -0
- package/dist/core/enrichment/enrichment-service.js +273 -0
- package/dist/core/enrichment/enrichment-service.js.map +1 -0
- package/dist/core/enrichment/index.d.ts +8 -0
- package/dist/core/enrichment/index.d.ts.map +1 -0
- package/dist/core/enrichment/index.js +8 -0
- package/dist/core/enrichment/index.js.map +1 -0
- package/dist/core/enrichment/relationship-mapper.d.ts +106 -0
- package/dist/core/enrichment/relationship-mapper.d.ts.map +1 -0
- package/dist/core/enrichment/relationship-mapper.js +352 -0
- package/dist/core/enrichment/relationship-mapper.js.map +1 -0
- package/dist/core/enrichment/style-resolver.d.ts +80 -0
- package/dist/core/enrichment/style-resolver.d.ts.map +1 -0
- package/dist/core/enrichment/style-resolver.js +327 -0
- package/dist/core/enrichment/style-resolver.js.map +1 -0
- package/dist/core/figma-api.d.ts +137 -0
- package/dist/core/figma-api.d.ts.map +1 -0
- package/dist/core/figma-api.js +274 -0
- package/dist/core/figma-api.js.map +1 -0
- package/dist/core/figma-desktop-connector.d.ts +52 -0
- package/dist/core/figma-desktop-connector.d.ts.map +1 -0
- package/dist/core/figma-desktop-connector.js +384 -0
- package/dist/core/figma-desktop-connector.js.map +1 -0
- package/dist/core/figma-style-extractor.d.ts +76 -0
- package/dist/core/figma-style-extractor.d.ts.map +1 -0
- package/dist/core/figma-style-extractor.js +312 -0
- package/dist/core/figma-style-extractor.js.map +1 -0
- package/dist/core/figma-tools.d.ts +15 -0
- package/dist/core/figma-tools.d.ts.map +1 -0
- package/dist/core/figma-tools.js +2300 -0
- package/dist/core/figma-tools.js.map +1 -0
- package/dist/core/logger.d.ts +22 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +54 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/snippet-injector.d.ts +24 -0
- package/dist/core/snippet-injector.d.ts.map +1 -0
- package/dist/core/snippet-injector.js +97 -0
- package/dist/core/snippet-injector.js.map +1 -0
- package/dist/core/types/enriched.d.ts +213 -0
- package/dist/core/types/enriched.d.ts.map +1 -0
- package/dist/core/types/enriched.js +6 -0
- package/dist/core/types/enriched.js.map +1 -0
- package/dist/core/types/index.d.ts +112 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +5 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/local.d.ts +57 -0
- package/dist/local.d.ts.map +1 -0
- package/dist/local.js +668 -0
- package/dist/local.js.map +1 -0
- package/dist/logger.d.ts +22 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +45 -0
- package/dist/logger.js.map +1 -0
- package/dist/server.d.ts +40 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +99 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +184 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +102 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/figma-desktop-bridge/README.md +232 -0
- package/figma-desktop-bridge/code.js +133 -0
- package/figma-desktop-bridge/manifest.json +13 -0
- package/figma-desktop-bridge/ui.html +200 -0
- 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
|
+
}
|