@mp3wizard/figma-console-mcp 1.20.1 → 1.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/dist/cloudflare/core/cloud-websocket-connector.js +3 -0
- package/dist/cloudflare/core/figjam-tools.js +91 -11
- package/dist/cloudflare/core/figma-api.js +1 -10
- package/dist/cloudflare/core/figma-desktop-connector.js +1 -0
- package/dist/cloudflare/core/websocket-connector.js +3 -0
- package/dist/cloudflare/core/websocket-server.js +61 -3
- package/dist/cloudflare/index.js +8 -10
- package/dist/core/figjam-tools.d.ts.map +1 -1
- package/dist/core/figjam-tools.js +91 -11
- package/dist/core/figjam-tools.js.map +1 -1
- package/dist/core/figma-api.d.ts.map +1 -1
- package/dist/core/figma-api.js +1 -10
- package/dist/core/figma-api.js.map +1 -1
- package/dist/core/figma-connector.d.ts +16 -0
- package/dist/core/figma-connector.d.ts.map +1 -1
- package/dist/core/figma-desktop-connector.d.ts +1 -0
- package/dist/core/figma-desktop-connector.d.ts.map +1 -1
- package/dist/core/figma-desktop-connector.js +1 -0
- package/dist/core/figma-desktop-connector.js.map +1 -1
- package/dist/core/websocket-connector.d.ts +16 -0
- package/dist/core/websocket-connector.d.ts.map +1 -1
- package/dist/core/websocket-connector.js +3 -0
- package/dist/core/websocket-connector.js.map +1 -1
- package/dist/core/websocket-server.d.ts +18 -1
- package/dist/core/websocket-server.d.ts.map +1 -1
- package/dist/core/websocket-server.js +61 -3
- package/dist/core/websocket-server.js.map +1 -1
- package/dist/local.d.ts +13 -0
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +129 -19
- package/dist/local.js.map +1 -1
- package/figma-desktop-bridge/code.js +94 -7
- package/figma-desktop-bridge/ui-full.html +24 -0
- package/figma-desktop-bridge/ui.html +8 -2
- package/package.json +103 -2
|
@@ -3864,11 +3864,11 @@ figma.ui.onmessage = async (msg) => {
|
|
|
3864
3864
|
|
|
3865
3865
|
connector.connectorStart = {
|
|
3866
3866
|
endpointNodeId: msg.startNodeId,
|
|
3867
|
-
magnet: 'AUTO'
|
|
3867
|
+
magnet: msg.startMagnet || 'AUTO'
|
|
3868
3868
|
};
|
|
3869
3869
|
connector.connectorEnd = {
|
|
3870
3870
|
endpointNodeId: msg.endNodeId,
|
|
3871
|
-
magnet: 'AUTO'
|
|
3871
|
+
magnet: msg.endMagnet || 'AUTO'
|
|
3872
3872
|
};
|
|
3873
3873
|
|
|
3874
3874
|
// Set label text if provided
|
|
@@ -3901,6 +3901,55 @@ figma.ui.onmessage = async (msg) => {
|
|
|
3901
3901
|
}
|
|
3902
3902
|
}
|
|
3903
3903
|
|
|
3904
|
+
// CREATE_SECTION - Create a section on FigJam board
|
|
3905
|
+
else if (msg.type === 'CREATE_SECTION') {
|
|
3906
|
+
try {
|
|
3907
|
+
if (__editorType !== 'figjam') {
|
|
3908
|
+
throw new Error('CREATE_SECTION is only available in FigJam files');
|
|
3909
|
+
}
|
|
3910
|
+
console.log('🌉 [Desktop Bridge] Creating section');
|
|
3911
|
+
|
|
3912
|
+
var section = figma.createSection();
|
|
3913
|
+
if (msg.name) section.name = msg.name;
|
|
3914
|
+
if (typeof msg.x === 'number') section.x = msg.x;
|
|
3915
|
+
if (typeof msg.y === 'number') section.y = msg.y;
|
|
3916
|
+
if (typeof msg.width === 'number' && typeof msg.height === 'number') {
|
|
3917
|
+
section.resizeWithoutConstraints(msg.width, msg.height);
|
|
3918
|
+
}
|
|
3919
|
+
if (msg.fillColor) {
|
|
3920
|
+
var scHex = msg.fillColor.replace('#', '');
|
|
3921
|
+
var scR = parseInt(scHex.substring(0, 2), 16) / 255;
|
|
3922
|
+
var scG = parseInt(scHex.substring(2, 4), 16) / 255;
|
|
3923
|
+
var scB = parseInt(scHex.substring(4, 6), 16) / 255;
|
|
3924
|
+
section.fills = [{ type: 'SOLID', color: { r: scR, g: scG, b: scB } }];
|
|
3925
|
+
}
|
|
3926
|
+
|
|
3927
|
+
figma.ui.postMessage({
|
|
3928
|
+
type: 'CREATE_SECTION_RESULT',
|
|
3929
|
+
requestId: msg.requestId,
|
|
3930
|
+
success: true,
|
|
3931
|
+
data: {
|
|
3932
|
+
id: section.id,
|
|
3933
|
+
type: section.type,
|
|
3934
|
+
name: section.name,
|
|
3935
|
+
x: section.x,
|
|
3936
|
+
y: section.y,
|
|
3937
|
+
width: section.width,
|
|
3938
|
+
height: section.height
|
|
3939
|
+
}
|
|
3940
|
+
});
|
|
3941
|
+
|
|
3942
|
+
} catch (error) {
|
|
3943
|
+
console.error('🌉 [Desktop Bridge] Create section error:', error);
|
|
3944
|
+
figma.ui.postMessage({
|
|
3945
|
+
type: 'CREATE_SECTION_RESULT',
|
|
3946
|
+
requestId: msg.requestId,
|
|
3947
|
+
success: false,
|
|
3948
|
+
error: error.message || String(error)
|
|
3949
|
+
});
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
|
|
3904
3953
|
// CREATE_SHAPE_WITH_TEXT - Create a labeled shape
|
|
3905
3954
|
else if (msg.type === 'CREATE_SHAPE_WITH_TEXT') {
|
|
3906
3955
|
try {
|
|
@@ -3916,7 +3965,45 @@ figma.ui.onmessage = async (msg) => {
|
|
|
3916
3965
|
shape.shapeType = msg.shapeType;
|
|
3917
3966
|
}
|
|
3918
3967
|
|
|
3919
|
-
// Set
|
|
3968
|
+
// Set position
|
|
3969
|
+
if (typeof msg.x === 'number') shape.x = msg.x;
|
|
3970
|
+
if (typeof msg.y === 'number') shape.y = msg.y;
|
|
3971
|
+
|
|
3972
|
+
// Resize (before text so text reflows to fit)
|
|
3973
|
+
if (typeof msg.width === 'number' && typeof msg.height === 'number') {
|
|
3974
|
+
shape.resize(msg.width, msg.height);
|
|
3975
|
+
} else if (typeof msg.width === 'number') {
|
|
3976
|
+
shape.resize(msg.width, shape.height);
|
|
3977
|
+
} else if (typeof msg.height === 'number') {
|
|
3978
|
+
shape.resize(shape.width, msg.height);
|
|
3979
|
+
}
|
|
3980
|
+
|
|
3981
|
+
// Fill color
|
|
3982
|
+
if (msg.fillColor) {
|
|
3983
|
+
var fHex = msg.fillColor.replace('#', '');
|
|
3984
|
+
var fR = parseInt(fHex.substring(0, 2), 16) / 255;
|
|
3985
|
+
var fG = parseInt(fHex.substring(2, 4), 16) / 255;
|
|
3986
|
+
var fB = parseInt(fHex.substring(4, 6), 16) / 255;
|
|
3987
|
+
shape.fills = [{ type: 'SOLID', color: { r: fR, g: fG, b: fB } }];
|
|
3988
|
+
}
|
|
3989
|
+
|
|
3990
|
+
// Stroke color
|
|
3991
|
+
if (msg.strokeColor) {
|
|
3992
|
+
var sHex = msg.strokeColor.replace('#', '');
|
|
3993
|
+
var sR = parseInt(sHex.substring(0, 2), 16) / 255;
|
|
3994
|
+
var sG = parseInt(sHex.substring(2, 4), 16) / 255;
|
|
3995
|
+
var sB = parseInt(sHex.substring(4, 6), 16) / 255;
|
|
3996
|
+
shape.strokes = [{ type: 'SOLID', color: { r: sR, g: sG, b: sB } }];
|
|
3997
|
+
shape.strokeWeight = 1;
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
// Dash pattern
|
|
4001
|
+
if (msg.strokeDashPattern) {
|
|
4002
|
+
var parts = msg.strokeDashPattern.split(',').map(function(p) { return parseFloat(p.trim()); });
|
|
4003
|
+
shape.dashPattern = parts;
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
// Set text (after resize so text reflows to fit new size)
|
|
3920
4007
|
if (msg.text) {
|
|
3921
4008
|
try {
|
|
3922
4009
|
await figma.loadFontAsync(shape.text.fontName);
|
|
@@ -3925,16 +4012,16 @@ figma.ui.onmessage = async (msg) => {
|
|
|
3925
4012
|
shape.text.fontName = { family: 'Inter', style: 'Medium' };
|
|
3926
4013
|
}
|
|
3927
4014
|
shape.text.characters = msg.text;
|
|
4015
|
+
if (typeof msg.fontSize === 'number') {
|
|
4016
|
+
shape.text.fontSize = msg.fontSize;
|
|
4017
|
+
}
|
|
3928
4018
|
}
|
|
3929
4019
|
|
|
3930
|
-
if (typeof msg.x === 'number') shape.x = msg.x;
|
|
3931
|
-
if (typeof msg.y === 'number') shape.y = msg.y;
|
|
3932
|
-
|
|
3933
4020
|
figma.ui.postMessage({
|
|
3934
4021
|
type: 'CREATE_SHAPE_WITH_TEXT_RESULT',
|
|
3935
4022
|
requestId: msg.requestId,
|
|
3936
4023
|
success: true,
|
|
3937
|
-
data: { id: shape.id, type: shape.type, name: shape.name, x: shape.x, y: shape.y }
|
|
4024
|
+
data: { id: shape.id, type: shape.type, name: shape.name, x: shape.x, y: shape.y, width: shape.width, height: shape.height }
|
|
3938
4025
|
});
|
|
3939
4026
|
|
|
3940
4027
|
} catch (error) {
|
|
@@ -828,6 +828,9 @@
|
|
|
828
828
|
* Tries the same port first (server may have just restarted),
|
|
829
829
|
* then does a full rescan to pick up any new servers.
|
|
830
830
|
*/
|
|
831
|
+
// Long-interval reconnect timers — keyed by port to avoid duplicates
|
|
832
|
+
var longIntervalTimers = {};
|
|
833
|
+
|
|
831
834
|
function wsReconnectPort(port) {
|
|
832
835
|
try {
|
|
833
836
|
var testWs = new WebSocket('ws://localhost:' + port);
|
|
@@ -839,6 +842,13 @@
|
|
|
839
842
|
clearTimeout(timeout);
|
|
840
843
|
activeConnections.push({ port: port, ws: testWs });
|
|
841
844
|
updateCompatState();
|
|
845
|
+
// Reset reconnect counter on ANY successful connect (fixes global counter bug)
|
|
846
|
+
wsReconnectAttempts = 0;
|
|
847
|
+
// Cancel long-interval retry for this port
|
|
848
|
+
if (longIntervalTimers[port]) {
|
|
849
|
+
clearInterval(longIntervalTimers[port]);
|
|
850
|
+
delete longIntervalTimers[port];
|
|
851
|
+
}
|
|
842
852
|
console.log('[MCP Bridge] Reconnected to port ' + port + ' (' + activeConnections.length + ' server(s) total)');
|
|
843
853
|
attachWsHandlers(testWs, port);
|
|
844
854
|
initializeConnection(testWs, port);
|
|
@@ -915,6 +925,20 @@
|
|
|
915
925
|
if (wsReconnectAttempts <= 5) {
|
|
916
926
|
var delay = Math.min(1000 * wsReconnectAttempts, 5000);
|
|
917
927
|
setTimeout(function() { wsReconnectPort(port); }, delay);
|
|
928
|
+
} else if (!longIntervalTimers[port]) {
|
|
929
|
+
// After rapid retries exhausted, start a low-frequency background retry.
|
|
930
|
+
// One attempt every 30s — if the MCP server restarts, we'll find it
|
|
931
|
+
// without requiring the user to manually reopen the plugin.
|
|
932
|
+
console.log('[MCP Bridge] Rapid retries exhausted for port ' + port + '. Starting 30s background retry.');
|
|
933
|
+
longIntervalTimers[port] = setInterval(function() {
|
|
934
|
+
if (isPortConnected(port)) {
|
|
935
|
+
// Already reconnected (e.g., via another path) — stop retrying
|
|
936
|
+
clearInterval(longIntervalTimers[port]);
|
|
937
|
+
delete longIntervalTimers[port];
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
wsReconnectPort(port);
|
|
941
|
+
}, 30000);
|
|
918
942
|
}
|
|
919
943
|
};
|
|
920
944
|
|
|
@@ -675,6 +675,9 @@
|
|
|
675
675
|
'CREATE_SHAPE_WITH_TEXT': function(params) {
|
|
676
676
|
return window.sendPluginCommand('CREATE_SHAPE_WITH_TEXT', params);
|
|
677
677
|
},
|
|
678
|
+
'CREATE_SECTION': function(params) {
|
|
679
|
+
return window.sendPluginCommand('CREATE_SECTION', params);
|
|
680
|
+
},
|
|
678
681
|
'CREATE_TABLE': function(params) {
|
|
679
682
|
return window.sendPluginCommand('CREATE_TABLE', params, 30000);
|
|
680
683
|
},
|
|
@@ -1114,8 +1117,8 @@
|
|
|
1114
1117
|
var statusEl = document.getElementById('cloud-status');
|
|
1115
1118
|
var code = (codeInput.value || '').trim().toUpperCase();
|
|
1116
1119
|
|
|
1117
|
-
if (!code || code.length <
|
|
1118
|
-
statusEl.textContent = '
|
|
1120
|
+
if (!code || code.length < 6) {
|
|
1121
|
+
statusEl.textContent = 'Pairing code must be 6 characters';
|
|
1119
1122
|
statusEl.className = 'cloud-status error';
|
|
1120
1123
|
return;
|
|
1121
1124
|
}
|
|
@@ -1351,6 +1354,9 @@
|
|
|
1351
1354
|
case 'CREATE_SHAPE_WITH_TEXT_RESULT':
|
|
1352
1355
|
handleResult('CREATE_SHAPE_WITH_TEXT', 'data');
|
|
1353
1356
|
break;
|
|
1357
|
+
case 'CREATE_SECTION_RESULT':
|
|
1358
|
+
handleResult('CREATE_SECTION', 'data');
|
|
1359
|
+
break;
|
|
1354
1360
|
case 'CREATE_TABLE_RESULT':
|
|
1355
1361
|
handleResult('CREATE_TABLE', 'data');
|
|
1356
1362
|
break;
|
package/package.json
CHANGED
|
@@ -1,97 +1,198 @@
|
|
|
1
1
|
{
|
|
2
|
+
|
|
2
3
|
"name": "@mp3wizard/figma-console-mcp",
|
|
3
|
-
|
|
4
|
+
|
|
5
|
+
"version": "1.21.2",
|
|
6
|
+
|
|
4
7
|
"description": "MCP server for accessing Figma plugin console logs and screenshots via Cloudflare Workers or local mode",
|
|
8
|
+
|
|
5
9
|
"type": "module",
|
|
10
|
+
|
|
6
11
|
"main": "dist/local.js",
|
|
12
|
+
|
|
7
13
|
"types": "dist/local.d.ts",
|
|
14
|
+
|
|
8
15
|
"bin": {
|
|
16
|
+
|
|
9
17
|
"figma-console-mcp": "./dist/local.js"
|
|
18
|
+
|
|
10
19
|
},
|
|
20
|
+
|
|
11
21
|
"files": [
|
|
22
|
+
|
|
12
23
|
"dist",
|
|
24
|
+
|
|
13
25
|
"figma-desktop-bridge",
|
|
26
|
+
|
|
14
27
|
"README.md",
|
|
28
|
+
|
|
15
29
|
"LICENSE"
|
|
30
|
+
|
|
16
31
|
],
|
|
32
|
+
|
|
17
33
|
"scripts": {
|
|
34
|
+
|
|
18
35
|
"prepublishOnly": "npm run build",
|
|
36
|
+
|
|
19
37
|
"deploy": "wrangler deploy",
|
|
38
|
+
|
|
20
39
|
"dev": "wrangler dev",
|
|
40
|
+
|
|
21
41
|
"dev:local": "tsx src/local.ts",
|
|
42
|
+
|
|
22
43
|
"build": "npm run build:local && npm run build:cloudflare && npm run build:apps",
|
|
44
|
+
|
|
23
45
|
"build:apps": "cross-env APP_NAME=token-browser vite build && cross-env APP_NAME=design-system-dashboard vite build",
|
|
46
|
+
|
|
24
47
|
"dev:apps": "vite build --watch",
|
|
48
|
+
|
|
25
49
|
"build:local": "tsc -p tsconfig.local.json",
|
|
50
|
+
|
|
26
51
|
"build:cloudflare": "tsc -p tsconfig.cloudflare.json",
|
|
52
|
+
|
|
27
53
|
"start": "wrangler dev",
|
|
54
|
+
|
|
28
55
|
"test": "jest",
|
|
56
|
+
|
|
29
57
|
"test:watch": "jest --watch",
|
|
58
|
+
|
|
30
59
|
"test:coverage": "jest --coverage",
|
|
60
|
+
|
|
31
61
|
"format": "biome format --write",
|
|
62
|
+
|
|
32
63
|
"lint:fix": "biome lint --fix",
|
|
64
|
+
|
|
33
65
|
"cf-typegen": "wrangler types",
|
|
66
|
+
|
|
34
67
|
"type-check": "tsc --noEmit"
|
|
68
|
+
|
|
35
69
|
},
|
|
70
|
+
|
|
36
71
|
"keywords": [
|
|
72
|
+
|
|
37
73
|
"mcp",
|
|
74
|
+
|
|
38
75
|
"figma",
|
|
76
|
+
|
|
39
77
|
"plugin",
|
|
78
|
+
|
|
40
79
|
"console",
|
|
80
|
+
|
|
41
81
|
"debugging",
|
|
82
|
+
|
|
42
83
|
"ai",
|
|
84
|
+
|
|
43
85
|
"anthropic",
|
|
86
|
+
|
|
44
87
|
"claude",
|
|
88
|
+
|
|
45
89
|
"cloudflare",
|
|
90
|
+
|
|
46
91
|
"workers"
|
|
92
|
+
|
|
47
93
|
],
|
|
94
|
+
|
|
48
95
|
"author": "Your Name",
|
|
96
|
+
|
|
49
97
|
"license": "MIT",
|
|
98
|
+
|
|
50
99
|
"repository": {
|
|
100
|
+
|
|
51
101
|
"type": "git",
|
|
102
|
+
|
|
52
103
|
"url": "https://github.com/mp3wizard/figma-console-mcp.git"
|
|
104
|
+
|
|
53
105
|
},
|
|
106
|
+
|
|
54
107
|
"engines": {
|
|
108
|
+
|
|
55
109
|
"node": ">=18.0.0"
|
|
110
|
+
|
|
56
111
|
},
|
|
112
|
+
|
|
57
113
|
"overrides": {
|
|
114
|
+
|
|
58
115
|
"path-to-regexp": ">=8.4.1",
|
|
116
|
+
|
|
59
117
|
"vite": {
|
|
118
|
+
|
|
60
119
|
"picomatch": ">=4.0.4"
|
|
120
|
+
|
|
61
121
|
},
|
|
122
|
+
|
|
62
123
|
"miniflare": {
|
|
124
|
+
|
|
63
125
|
"undici": ">=7.24.0"
|
|
126
|
+
|
|
64
127
|
},
|
|
128
|
+
|
|
65
129
|
"handlebars": ">=4.7.9",
|
|
66
|
-
|
|
130
|
+
|
|
131
|
+
"brace-expansion": ">=1.1.13",
|
|
132
|
+
|
|
133
|
+
"lodash": ">=4.18.0",
|
|
134
|
+
|
|
135
|
+
"picomatch": ">=4.0.4"
|
|
136
|
+
|
|
67
137
|
},
|
|
138
|
+
|
|
68
139
|
"dependencies": {
|
|
140
|
+
|
|
69
141
|
"@cloudflare/puppeteer": "^1.0.4",
|
|
142
|
+
|
|
70
143
|
"@modelcontextprotocol/ext-apps": "^1.0.1",
|
|
144
|
+
|
|
71
145
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
146
|
+
|
|
72
147
|
"agents": "^0.7.1",
|
|
148
|
+
|
|
73
149
|
"chrome-remote-interface": "^0.33.2",
|
|
150
|
+
|
|
74
151
|
"pino": "^9.5.0",
|
|
152
|
+
|
|
75
153
|
"pino-pretty": "^13.0.0",
|
|
154
|
+
|
|
76
155
|
"puppeteer-core": "^23.11.1",
|
|
156
|
+
|
|
77
157
|
"uuid": "^11.0.3",
|
|
158
|
+
|
|
78
159
|
"ws": "^8.19.0",
|
|
160
|
+
|
|
79
161
|
"zod": "^3.25.76"
|
|
162
|
+
|
|
80
163
|
},
|
|
164
|
+
|
|
81
165
|
"devDependencies": {
|
|
166
|
+
|
|
82
167
|
"@biomejs/biome": "^2.2.5",
|
|
168
|
+
|
|
83
169
|
"@types/jest": "^29.5.14",
|
|
170
|
+
|
|
84
171
|
"@types/node": "^22.10.2",
|
|
172
|
+
|
|
85
173
|
"@types/uuid": "^10.0.0",
|
|
174
|
+
|
|
86
175
|
"@types/ws": "^8.18.1",
|
|
176
|
+
|
|
87
177
|
"cross-env": "^7.0.3",
|
|
178
|
+
|
|
88
179
|
"jest": "^29.7.0",
|
|
180
|
+
|
|
89
181
|
"ts-jest": "^29.2.5",
|
|
182
|
+
|
|
90
183
|
"tsx": "^4.19.2",
|
|
184
|
+
|
|
91
185
|
"typescript": "5.9.3",
|
|
186
|
+
|
|
92
187
|
"vite": "^6.0.0",
|
|
188
|
+
|
|
93
189
|
"vite-plugin-singlefile": "^2.0.0",
|
|
190
|
+
|
|
94
191
|
"wrangler": "^4.42.0",
|
|
192
|
+
|
|
95
193
|
"zod-to-json-schema": "^3.25.1"
|
|
194
|
+
|
|
96
195
|
}
|
|
196
|
+
|
|
97
197
|
}
|
|
198
|
+
|