@liner-fe/figma-mcp 1.0.5 → 1.0.6
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/dist/server/server.cjs +164 -161
- package/package.json +1 -1
package/dist/server/server.cjs
CHANGED
|
@@ -24,104 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
// src/server.ts
|
|
25
25
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
26
26
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
27
|
-
var import_zod = require("zod");
|
|
28
|
-
|
|
29
|
-
// src/figmaClient.ts
|
|
30
27
|
var import_crypto = require("crypto");
|
|
31
28
|
var import_ws = __toESM(require("ws"), 1);
|
|
32
|
-
|
|
33
|
-
let ws = null;
|
|
34
|
-
const pendingRequests = /* @__PURE__ */ new Map();
|
|
35
|
-
function connectToFigma2(port = 3055) {
|
|
36
|
-
if (ws && ws.readyState === import_ws.default.OPEN) {
|
|
37
|
-
logger2.info("Already connected to Figma");
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const wsUrl = serverUrl2 === "localhost" ? `${wsUrlBase}:${port}` : wsUrlBase;
|
|
41
|
-
logger2.info(`Connecting to Figma socket server at ${wsUrl}...`);
|
|
42
|
-
ws = new import_ws.default(wsUrl);
|
|
43
|
-
ws.on("open", () => {
|
|
44
|
-
logger2.info("Connected to Figma socket server");
|
|
45
|
-
});
|
|
46
|
-
ws.on("message", (data) => {
|
|
47
|
-
try {
|
|
48
|
-
const json = JSON.parse(data);
|
|
49
|
-
const myResponse = json.message;
|
|
50
|
-
logger2.debug(`Received message: ${JSON.stringify(myResponse)}`);
|
|
51
|
-
logger2.log("myResponse" + JSON.stringify(myResponse));
|
|
52
|
-
if (myResponse.id && pendingRequests.has(myResponse.id) && myResponse.result) {
|
|
53
|
-
const request = pendingRequests.get(myResponse.id);
|
|
54
|
-
clearTimeout(request.timeout);
|
|
55
|
-
if (myResponse.error) {
|
|
56
|
-
logger2.error(`Error from Figma: ${myResponse.error}`);
|
|
57
|
-
request.reject(new Error(myResponse.error));
|
|
58
|
-
} else if (myResponse.result) {
|
|
59
|
-
request.resolve(myResponse.result);
|
|
60
|
-
}
|
|
61
|
-
pendingRequests.delete(myResponse.id);
|
|
62
|
-
} else {
|
|
63
|
-
logger2.info(`Received broadcast message: ${JSON.stringify(myResponse)}`);
|
|
64
|
-
}
|
|
65
|
-
} catch (error) {
|
|
66
|
-
logger2.error(
|
|
67
|
-
`Error parsing message: ${error instanceof Error ? error.message : String(error)}`
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
ws.on("error", (error) => {
|
|
72
|
-
logger2.error(`Socket error: ${error}`);
|
|
73
|
-
});
|
|
74
|
-
ws.on("close", () => {
|
|
75
|
-
logger2.info("Disconnected from Figma socket server");
|
|
76
|
-
ws = null;
|
|
77
|
-
for (const [id, request] of pendingRequests.entries()) {
|
|
78
|
-
clearTimeout(request.timeout);
|
|
79
|
-
request.reject(new Error("Connection closed"));
|
|
80
|
-
pendingRequests.delete(id);
|
|
81
|
-
}
|
|
82
|
-
logger2.info("Attempting to reconnect in 2 seconds...");
|
|
83
|
-
setTimeout(() => connectToFigma2(port), 2e3);
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
function sendCommandToFigma2(command, params = {}) {
|
|
87
|
-
return new Promise((resolve, reject) => {
|
|
88
|
-
if (!ws || ws.readyState !== import_ws.default.OPEN) {
|
|
89
|
-
connectToFigma2();
|
|
90
|
-
reject(new Error("Not connected to Figma. Attempting to connect..."));
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const id = (0, import_crypto.randomUUID)();
|
|
94
|
-
const request = {
|
|
95
|
-
id,
|
|
96
|
-
type: "message",
|
|
97
|
-
message: {
|
|
98
|
-
id,
|
|
99
|
-
command,
|
|
100
|
-
params: {
|
|
101
|
-
...params
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
const timeout = setTimeout(() => {
|
|
106
|
-
if (pendingRequests.has(id)) {
|
|
107
|
-
pendingRequests.delete(id);
|
|
108
|
-
logger2.error(`Request ${id} to Figma timed out after 30 seconds`);
|
|
109
|
-
reject(new Error("Request to Figma timed out"));
|
|
110
|
-
}
|
|
111
|
-
}, 3e4);
|
|
112
|
-
pendingRequests.set(id, { resolve, reject, timeout });
|
|
113
|
-
logger2.info(`Sending command to Figma: ${command}`);
|
|
114
|
-
logger2.debug(`Request details: ${JSON.stringify(request)}`);
|
|
115
|
-
ws.send(JSON.stringify(request));
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
return {
|
|
119
|
-
connectToFigma: connectToFigma2,
|
|
120
|
-
sendCommandToFigma: sendCommandToFigma2
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// src/server.ts
|
|
29
|
+
var import_zod = require("zod");
|
|
125
30
|
var logger = {
|
|
126
31
|
info: (message) => process.stderr.write(`[INFO] ${message}
|
|
127
32
|
`),
|
|
@@ -134,30 +39,16 @@ var logger = {
|
|
|
134
39
|
log: (message) => process.stderr.write(`[LOG] ${message}
|
|
135
40
|
`)
|
|
136
41
|
};
|
|
137
|
-
var
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
capabilities: {
|
|
144
|
-
logging: logger,
|
|
145
|
-
resources: {
|
|
146
|
-
subscribe: true,
|
|
147
|
-
listChanged: true
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
);
|
|
42
|
+
var ws = null;
|
|
43
|
+
var pendingRequests = /* @__PURE__ */ new Map();
|
|
44
|
+
var server = new import_mcp.McpServer({
|
|
45
|
+
name: "@liner-fe/figma-mcp",
|
|
46
|
+
version: "1.0.0"
|
|
47
|
+
});
|
|
152
48
|
var args = process.argv.slice(2);
|
|
153
49
|
var serverArg = args.find((arg) => arg.startsWith("--server="));
|
|
154
50
|
var serverUrl = serverArg ? serverArg.split("=")[1] : "localhost";
|
|
155
51
|
var WS_URL = serverUrl === "localhost" ? `ws://${serverUrl}` : `wss://${serverUrl}`;
|
|
156
|
-
var { connectToFigma, sendCommandToFigma } = createFigmaClient({
|
|
157
|
-
logger,
|
|
158
|
-
serverUrl,
|
|
159
|
-
wsUrlBase: WS_URL
|
|
160
|
-
});
|
|
161
52
|
server.tool(
|
|
162
53
|
"get_document_info",
|
|
163
54
|
"Get detailed information about the current Figma document",
|
|
@@ -186,14 +77,12 @@ server.tool(
|
|
|
186
77
|
}
|
|
187
78
|
);
|
|
188
79
|
server.tool(
|
|
189
|
-
"
|
|
190
|
-
"Get
|
|
191
|
-
{
|
|
192
|
-
|
|
193
|
-
},
|
|
194
|
-
async ({ nodeId }) => {
|
|
80
|
+
"get_selection",
|
|
81
|
+
"Get information about the current selection in Figma",
|
|
82
|
+
{},
|
|
83
|
+
async () => {
|
|
195
84
|
try {
|
|
196
|
-
const result = await sendCommandToFigma("
|
|
85
|
+
const result = await sendCommandToFigma("get_selection");
|
|
197
86
|
return {
|
|
198
87
|
content: [
|
|
199
88
|
{
|
|
@@ -207,7 +96,7 @@ server.tool(
|
|
|
207
96
|
content: [
|
|
208
97
|
{
|
|
209
98
|
type: "text",
|
|
210
|
-
text: `Error getting
|
|
99
|
+
text: `Error getting selection: ${error instanceof Error ? error.message : String(error)}`
|
|
211
100
|
}
|
|
212
101
|
]
|
|
213
102
|
};
|
|
@@ -215,24 +104,19 @@ server.tool(
|
|
|
215
104
|
}
|
|
216
105
|
);
|
|
217
106
|
server.tool(
|
|
218
|
-
"
|
|
219
|
-
"Get detailed information about
|
|
107
|
+
"get_node_info",
|
|
108
|
+
"Get detailed information about a specific node in Figma",
|
|
220
109
|
{
|
|
221
|
-
|
|
110
|
+
nodeId: import_zod.z.string().describe("The ID of the node to get information about")
|
|
222
111
|
},
|
|
223
|
-
async ({
|
|
112
|
+
async ({ nodeId }) => {
|
|
224
113
|
try {
|
|
225
|
-
const
|
|
226
|
-
nodeIds.map(async (nodeId) => {
|
|
227
|
-
const result = await sendCommandToFigma("get_node_info", { nodeId });
|
|
228
|
-
return { nodeId, info: result };
|
|
229
|
-
})
|
|
230
|
-
);
|
|
114
|
+
const result = await sendCommandToFigma("get_node_info", { nodeId });
|
|
231
115
|
return {
|
|
232
116
|
content: [
|
|
233
117
|
{
|
|
234
118
|
type: "text",
|
|
235
|
-
text: JSON.stringify(
|
|
119
|
+
text: JSON.stringify(result)
|
|
236
120
|
}
|
|
237
121
|
]
|
|
238
122
|
};
|
|
@@ -241,42 +125,161 @@ server.tool(
|
|
|
241
125
|
content: [
|
|
242
126
|
{
|
|
243
127
|
type: "text",
|
|
244
|
-
text: `Error getting
|
|
128
|
+
text: `Error getting node info: ${error instanceof Error ? error.message : String(error)}`
|
|
245
129
|
}
|
|
246
130
|
]
|
|
247
131
|
};
|
|
248
132
|
}
|
|
249
133
|
}
|
|
250
134
|
);
|
|
251
|
-
server.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
135
|
+
server.prompt("connection_troubleshooting", "Help users troubleshoot connection issues", () => {
|
|
136
|
+
return {
|
|
137
|
+
messages: [
|
|
138
|
+
{
|
|
139
|
+
role: "assistant",
|
|
140
|
+
content: {
|
|
141
|
+
type: "text",
|
|
142
|
+
text: `When users report connection issues, ask them to check:
|
|
143
|
+
|
|
144
|
+
1. Figma Plugin Connection:
|
|
145
|
+
- Did you click the "Connect" button in the Figma plugin?
|
|
146
|
+
- Is the Figma plugin running and showing a connected status?
|
|
147
|
+
- Try refreshing the plugin or restarting it if needed
|
|
148
|
+
|
|
149
|
+
2. MCP Connection in Cursor:
|
|
150
|
+
- Go to Cursor Settings/Preferences
|
|
151
|
+
- Check if the MCP server connection is properly configured(green light)
|
|
152
|
+
- Verify the MCP server is running and connected
|
|
153
|
+
- Look for any connection error messages in the settings
|
|
154
|
+
|
|
155
|
+
If both are properly connected and issues persist, try:
|
|
156
|
+
- Restarting both the Figma plugin and Cursor
|
|
157
|
+
- Checking if the WebSocket connection is blocked by firewall
|
|
158
|
+
- Verifying the correct server URL and port configuration`
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
description: "Troubleshooting guide for connection issues between Figma plugin and MCP server"
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
server.prompt("data_analysis_strategy", "Best practices for analyzing Figma design data", () => {
|
|
166
|
+
return {
|
|
167
|
+
messages: [
|
|
168
|
+
{
|
|
169
|
+
role: "assistant",
|
|
170
|
+
content: {
|
|
171
|
+
type: "text",
|
|
172
|
+
text: `When analyzing Figma design data, follow these strategies
|
|
173
|
+
|
|
174
|
+
1. Data Extraction Workflow:
|
|
175
|
+
- Use get_selection() to focus on specific areas of interest
|
|
176
|
+
- Use get_document_info() when connection is enabled
|
|
177
|
+
|
|
178
|
+
2. Component Analysis:
|
|
179
|
+
- Identify reusable components vs one-off elements
|
|
180
|
+
- Look for design system patterns (colors, typography, spacing)
|
|
181
|
+
- Note component variants and their properties
|
|
182
|
+
- Extract design tokens (colors, fonts, spacing values)
|
|
183
|
+
|
|
184
|
+
3. Layout Analysis:
|
|
185
|
+
- Analyze auto-layout settings and constraints
|
|
186
|
+
- Document spacing patterns and grid systems
|
|
187
|
+
- Identify responsive design patterns
|
|
188
|
+
- Note alignment and positioning strategies
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
`
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
description: "Best practices for working with Figma designs"
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
function connectToFigma(port = 3055) {
|
|
199
|
+
if (ws && ws.readyState === import_ws.default.OPEN) {
|
|
200
|
+
logger.info("Already connected to Figma");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const wsUrl = serverUrl === "localhost" ? `${WS_URL}:${port}` : WS_URL;
|
|
204
|
+
logger.info(`Connecting to Figma socket server at ${wsUrl}...`);
|
|
205
|
+
ws = new import_ws.default(wsUrl);
|
|
206
|
+
ws.on("open", () => {
|
|
207
|
+
logger.info("Connected to Figma socket server");
|
|
208
|
+
});
|
|
209
|
+
ws.on("message", (data) => {
|
|
258
210
|
try {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
211
|
+
const json = JSON.parse(data);
|
|
212
|
+
const myResponse = json.message;
|
|
213
|
+
logger.debug(`Received message: ${JSON.stringify(myResponse)}`);
|
|
214
|
+
logger.log("myResponse" + JSON.stringify(myResponse));
|
|
215
|
+
if (myResponse.id && pendingRequests.has(myResponse.id) && myResponse.result) {
|
|
216
|
+
const request = pendingRequests.get(myResponse.id);
|
|
217
|
+
clearTimeout(request.timeout);
|
|
218
|
+
if (myResponse.error) {
|
|
219
|
+
logger.error(`Error from Figma: ${myResponse.error}`);
|
|
220
|
+
request.reject(new Error(myResponse.error));
|
|
221
|
+
} else {
|
|
222
|
+
if (myResponse.result) {
|
|
223
|
+
request.resolve(myResponse.result);
|
|
265
224
|
}
|
|
266
|
-
|
|
267
|
-
|
|
225
|
+
}
|
|
226
|
+
pendingRequests.delete(myResponse.id);
|
|
227
|
+
} else {
|
|
228
|
+
logger.info(`Received broadcast message: ${JSON.stringify(myResponse)}`);
|
|
229
|
+
}
|
|
268
230
|
} catch (error) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
type: "text",
|
|
273
|
-
text: `Error getting selection: ${error instanceof Error ? error.message : String(error)}`
|
|
274
|
-
}
|
|
275
|
-
]
|
|
276
|
-
};
|
|
231
|
+
logger.error(
|
|
232
|
+
`Error parsing message: ${error instanceof Error ? error.message : String(error)}`
|
|
233
|
+
);
|
|
277
234
|
}
|
|
278
|
-
}
|
|
279
|
-
)
|
|
235
|
+
});
|
|
236
|
+
ws.on("error", (error) => {
|
|
237
|
+
logger.error(`Socket error: ${error}`);
|
|
238
|
+
});
|
|
239
|
+
ws.on("close", () => {
|
|
240
|
+
logger.info("Disconnected from Figma socket server");
|
|
241
|
+
ws = null;
|
|
242
|
+
for (const [id, request] of pendingRequests.entries()) {
|
|
243
|
+
clearTimeout(request.timeout);
|
|
244
|
+
request.reject(new Error("Connection closed"));
|
|
245
|
+
pendingRequests.delete(id);
|
|
246
|
+
}
|
|
247
|
+
logger.info("Attempting to reconnect in 2 seconds...");
|
|
248
|
+
setTimeout(() => connectToFigma(port), 2e3);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function sendCommandToFigma(command, params = {}) {
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
if (!ws || ws.readyState !== import_ws.default.OPEN) {
|
|
254
|
+
connectToFigma();
|
|
255
|
+
reject(new Error("Not connected to Figma. Attempting to connect..."));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const id = (0, import_crypto.randomUUID)();
|
|
259
|
+
const request = {
|
|
260
|
+
id,
|
|
261
|
+
type: "message",
|
|
262
|
+
message: {
|
|
263
|
+
id,
|
|
264
|
+
command,
|
|
265
|
+
params: {
|
|
266
|
+
...params
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
const timeout = setTimeout(() => {
|
|
271
|
+
if (pendingRequests.has(id)) {
|
|
272
|
+
pendingRequests.delete(id);
|
|
273
|
+
logger.error(`Request ${id} to Figma timed out after 30 seconds`);
|
|
274
|
+
reject(new Error("Request to Figma timed out"));
|
|
275
|
+
}
|
|
276
|
+
}, 3e4);
|
|
277
|
+
pendingRequests.set(id, { resolve, reject, timeout });
|
|
278
|
+
logger.info(`Sending command to Figma: ${command}`);
|
|
279
|
+
logger.debug(`Request details: ${JSON.stringify(request)}`);
|
|
280
|
+
ws.send(JSON.stringify(request));
|
|
281
|
+
});
|
|
282
|
+
}
|
|
280
283
|
async function main() {
|
|
281
284
|
try {
|
|
282
285
|
connectToFigma();
|