@liner-fe/figma-mcp 1.0.4 → 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.
Files changed (2) hide show
  1. package/dist/server/server.cjs +188 -121
  2. package/package.json +1 -1
@@ -24,103 +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
-
28
- // src/figmaClient.ts
29
27
  var import_crypto = require("crypto");
30
28
  var import_ws = __toESM(require("ws"), 1);
31
- function createFigmaClient({ logger: logger2, serverUrl: serverUrl2, wsUrlBase }) {
32
- let ws = null;
33
- const pendingRequests = /* @__PURE__ */ new Map();
34
- function connectToFigma2(port = 3055) {
35
- if (ws && ws.readyState === import_ws.default.OPEN) {
36
- logger2.info("Already connected to Figma");
37
- return;
38
- }
39
- const wsUrl = serverUrl2 === "localhost" ? `${wsUrlBase}:${port}` : wsUrlBase;
40
- logger2.info(`Connecting to Figma socket server at ${wsUrl}...`);
41
- ws = new import_ws.default(wsUrl);
42
- ws.on("open", () => {
43
- logger2.info("Connected to Figma socket server");
44
- });
45
- ws.on("message", (data) => {
46
- try {
47
- const json = JSON.parse(data);
48
- const myResponse = json.message;
49
- logger2.debug(`Received message: ${JSON.stringify(myResponse)}`);
50
- logger2.log("myResponse" + JSON.stringify(myResponse));
51
- if (myResponse.id && pendingRequests.has(myResponse.id) && myResponse.result) {
52
- const request = pendingRequests.get(myResponse.id);
53
- clearTimeout(request.timeout);
54
- if (myResponse.error) {
55
- logger2.error(`Error from Figma: ${myResponse.error}`);
56
- request.reject(new Error(myResponse.error));
57
- } else if (myResponse.result) {
58
- request.resolve(myResponse.result);
59
- }
60
- pendingRequests.delete(myResponse.id);
61
- } else {
62
- logger2.info(`Received broadcast message: ${JSON.stringify(myResponse)}`);
63
- }
64
- } catch (error) {
65
- logger2.error(
66
- `Error parsing message: ${error instanceof Error ? error.message : String(error)}`
67
- );
68
- }
69
- });
70
- ws.on("error", (error) => {
71
- logger2.error(`Socket error: ${error}`);
72
- });
73
- ws.on("close", () => {
74
- logger2.info("Disconnected from Figma socket server");
75
- ws = null;
76
- for (const [id, request] of pendingRequests.entries()) {
77
- clearTimeout(request.timeout);
78
- request.reject(new Error("Connection closed"));
79
- pendingRequests.delete(id);
80
- }
81
- logger2.info("Attempting to reconnect in 2 seconds...");
82
- setTimeout(() => connectToFigma2(port), 2e3);
83
- });
84
- }
85
- function sendCommandToFigma2(command, params = {}) {
86
- return new Promise((resolve, reject) => {
87
- if (!ws || ws.readyState !== import_ws.default.OPEN) {
88
- connectToFigma2();
89
- reject(new Error("Not connected to Figma. Attempting to connect..."));
90
- return;
91
- }
92
- const id = (0, import_crypto.randomUUID)();
93
- const request = {
94
- id,
95
- type: "message",
96
- message: {
97
- id,
98
- command,
99
- params: {
100
- ...params
101
- }
102
- }
103
- };
104
- const timeout = setTimeout(() => {
105
- if (pendingRequests.has(id)) {
106
- pendingRequests.delete(id);
107
- logger2.error(`Request ${id} to Figma timed out after 30 seconds`);
108
- reject(new Error("Request to Figma timed out"));
109
- }
110
- }, 3e4);
111
- pendingRequests.set(id, { resolve, reject, timeout });
112
- logger2.info(`Sending command to Figma: ${command}`);
113
- logger2.debug(`Request details: ${JSON.stringify(request)}`);
114
- ws.send(JSON.stringify(request));
115
- });
116
- }
117
- return {
118
- connectToFigma: connectToFigma2,
119
- sendCommandToFigma: sendCommandToFigma2
120
- };
121
- }
122
-
123
- // src/server.ts
29
+ var import_zod = require("zod");
124
30
  var logger = {
125
31
  info: (message) => process.stderr.write(`[INFO] ${message}
126
32
  `),
@@ -133,30 +39,16 @@ var logger = {
133
39
  log: (message) => process.stderr.write(`[LOG] ${message}
134
40
  `)
135
41
  };
136
- var server = new import_mcp.McpServer(
137
- {
138
- name: "@liner-fe/figma-mcp",
139
- version: "1.0.0"
140
- },
141
- {
142
- capabilities: {
143
- logging: logger,
144
- resources: {
145
- subscribe: true,
146
- listChanged: true
147
- }
148
- }
149
- }
150
- );
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
+ });
151
48
  var args = process.argv.slice(2);
152
49
  var serverArg = args.find((arg) => arg.startsWith("--server="));
153
50
  var serverUrl = serverArg ? serverArg.split("=")[1] : "localhost";
154
51
  var WS_URL = serverUrl === "localhost" ? `ws://${serverUrl}` : `wss://${serverUrl}`;
155
- var { connectToFigma, sendCommandToFigma } = createFigmaClient({
156
- logger,
157
- serverUrl,
158
- wsUrlBase: WS_URL
159
- });
160
52
  server.tool(
161
53
  "get_document_info",
162
54
  "Get detailed information about the current Figma document",
@@ -184,12 +76,10 @@ server.tool(
184
76
  }
185
77
  }
186
78
  );
187
- server.registerTool(
79
+ server.tool(
188
80
  "get_selection",
189
- {
190
- description: "Get information about the current selection in Figma",
191
- inputSchema: {}
192
- },
81
+ "Get information about the current selection in Figma",
82
+ {},
193
83
  async () => {
194
84
  try {
195
85
  const result = await sendCommandToFigma("get_selection");
@@ -197,7 +87,7 @@ server.registerTool(
197
87
  content: [
198
88
  {
199
89
  type: "text",
200
- text: JSON.stringify(result, null, 2)
90
+ text: JSON.stringify(result)
201
91
  }
202
92
  ]
203
93
  };
@@ -213,6 +103,183 @@ server.registerTool(
213
103
  }
214
104
  }
215
105
  );
106
+ server.tool(
107
+ "get_node_info",
108
+ "Get detailed information about a specific node in Figma",
109
+ {
110
+ nodeId: import_zod.z.string().describe("The ID of the node to get information about")
111
+ },
112
+ async ({ nodeId }) => {
113
+ try {
114
+ const result = await sendCommandToFigma("get_node_info", { nodeId });
115
+ return {
116
+ content: [
117
+ {
118
+ type: "text",
119
+ text: JSON.stringify(result)
120
+ }
121
+ ]
122
+ };
123
+ } catch (error) {
124
+ return {
125
+ content: [
126
+ {
127
+ type: "text",
128
+ text: `Error getting node info: ${error instanceof Error ? error.message : String(error)}`
129
+ }
130
+ ]
131
+ };
132
+ }
133
+ }
134
+ );
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) => {
210
+ try {
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);
224
+ }
225
+ }
226
+ pendingRequests.delete(myResponse.id);
227
+ } else {
228
+ logger.info(`Received broadcast message: ${JSON.stringify(myResponse)}`);
229
+ }
230
+ } catch (error) {
231
+ logger.error(
232
+ `Error parsing message: ${error instanceof Error ? error.message : String(error)}`
233
+ );
234
+ }
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
+ }
216
283
  async function main() {
217
284
  try {
218
285
  connectToFigma();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liner-fe/figma-mcp",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "bin": "dist/server/server.cjs",
6
6
  "files": [