@liner-fe/figma-mcp 1.0.2 → 1.0.4
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 +27 -178
- package/package.json +2 -3
package/dist/server/server.cjs
CHANGED
|
@@ -24,7 +24,6 @@ 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
27
|
|
|
29
28
|
// src/figmaClient.ts
|
|
30
29
|
var import_crypto = require("crypto");
|
|
@@ -134,57 +133,9 @@ var logger = {
|
|
|
134
133
|
log: (message) => process.stderr.write(`[LOG] ${message}
|
|
135
134
|
`)
|
|
136
135
|
};
|
|
137
|
-
var RULES_FILES = Object.keys(__RULES__);
|
|
138
|
-
function levenshtein(a, b) {
|
|
139
|
-
const m = a.length;
|
|
140
|
-
const n = b.length;
|
|
141
|
-
if (m === 0) return n;
|
|
142
|
-
if (n === 0) return m;
|
|
143
|
-
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
144
|
-
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
145
|
-
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
146
|
-
for (let i = 1; i <= m; i++) {
|
|
147
|
-
for (let j = 1; j <= n; j++) {
|
|
148
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
149
|
-
dp[i][j] = Math.min(
|
|
150
|
-
dp[i - 1][j] + 1,
|
|
151
|
-
// 삭제
|
|
152
|
-
dp[i][j - 1] + 1,
|
|
153
|
-
// 삽입
|
|
154
|
-
dp[i - 1][j - 1] + cost
|
|
155
|
-
// 교체
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return dp[m][n];
|
|
160
|
-
}
|
|
161
|
-
function getSimilarityScore(target, candidate) {
|
|
162
|
-
if (target === candidate) return 100;
|
|
163
|
-
if (candidate.startsWith(target) || target.startsWith(candidate)) return 95;
|
|
164
|
-
if (candidate.includes(target) || target.includes(candidate)) return 85;
|
|
165
|
-
const distance = levenshtein(target, candidate);
|
|
166
|
-
const maxLen = Math.max(target.length, candidate.length);
|
|
167
|
-
const normalized = 1 - distance / maxLen;
|
|
168
|
-
const score = Math.floor(normalized * 80);
|
|
169
|
-
return score > 0 ? score : 0;
|
|
170
|
-
}
|
|
171
|
-
var STORIES_EXT = ".stories.mdx";
|
|
172
|
-
function findSimilarStoriesFiles(componentName) {
|
|
173
|
-
const compName = componentName.toLowerCase();
|
|
174
|
-
const files = RULES_FILES.filter((file) => file.toLowerCase().endsWith(STORIES_EXT));
|
|
175
|
-
const scored = files.map((file) => {
|
|
176
|
-
const baseName = file.toLowerCase().replace(new RegExp(`${STORIES_EXT.replace(".", "\\.")}$`), "");
|
|
177
|
-
return {
|
|
178
|
-
file,
|
|
179
|
-
baseName,
|
|
180
|
-
score: getSimilarityScore(compName, baseName)
|
|
181
|
-
};
|
|
182
|
-
}).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score);
|
|
183
|
-
return scored;
|
|
184
|
-
}
|
|
185
136
|
var server = new import_mcp.McpServer(
|
|
186
137
|
{
|
|
187
|
-
name: "@
|
|
138
|
+
name: "@liner-fe/figma-mcp",
|
|
188
139
|
version: "1.0.0"
|
|
189
140
|
},
|
|
190
141
|
{
|
|
@@ -206,20 +157,18 @@ var { connectToFigma, sendCommandToFigma } = createFigmaClient({
|
|
|
206
157
|
serverUrl,
|
|
207
158
|
wsUrlBase: WS_URL
|
|
208
159
|
});
|
|
209
|
-
server.
|
|
210
|
-
"
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
inputSchema: {}
|
|
214
|
-
},
|
|
160
|
+
server.tool(
|
|
161
|
+
"get_document_info",
|
|
162
|
+
"Get detailed information about the current Figma document",
|
|
163
|
+
{},
|
|
215
164
|
async () => {
|
|
216
165
|
try {
|
|
217
|
-
const result = await sendCommandToFigma("
|
|
166
|
+
const result = await sendCommandToFigma("get_document_info");
|
|
218
167
|
return {
|
|
219
168
|
content: [
|
|
220
169
|
{
|
|
221
170
|
type: "text",
|
|
222
|
-
text: JSON.stringify(result
|
|
171
|
+
text: JSON.stringify(result)
|
|
223
172
|
}
|
|
224
173
|
]
|
|
225
174
|
};
|
|
@@ -228,140 +177,40 @@ server.registerTool(
|
|
|
228
177
|
content: [
|
|
229
178
|
{
|
|
230
179
|
type: "text",
|
|
231
|
-
text: `Error getting
|
|
180
|
+
text: `Error getting document info: ${error instanceof Error ? error.message : String(error)}`
|
|
232
181
|
}
|
|
233
182
|
]
|
|
234
183
|
};
|
|
235
184
|
}
|
|
236
185
|
}
|
|
237
186
|
);
|
|
238
|
-
server.registerPrompt(
|
|
239
|
-
"design system components",
|
|
240
|
-
{
|
|
241
|
-
description: "help analyze design system common components"
|
|
242
|
-
},
|
|
243
|
-
() => {
|
|
244
|
-
return {
|
|
245
|
-
messages: [
|
|
246
|
-
{
|
|
247
|
-
role: "assistant",
|
|
248
|
-
content: {
|
|
249
|
-
type: "text",
|
|
250
|
-
text: `\uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAD6C\uD604 \uC815\uBCF4\uB97C \uD30C\uC545\uD558\uC5EC \uCF54\uB4DC \uC81C\uC791\uC5D0 \uD65C\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4. \uCF54\uB4DC \uC0AC\uC6A9 \uC608\uC2DC, \uC124\uBA85 \uB4F1\uC744 \uC815\uD655\uD558\uAC8C \uD30C\uC545\uD558\uC5EC \uC2E4\uC81C \uCF54\uB4DC \uC81C\uC791\uC5D0 \uC815\uD655\uD558\uAC8C \uD65C\uC6A9\uD560 \uC218 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4.
|
|
251
|
-
\uD2B9\uD788 Figma \uC778\uD130\uD398\uC774\uC2A4\uC640 \uCF54\uB4DC \uAC04\uC758 \uAD6C\uD604 \uCC28\uC774\uB97C \uC815\uD655\uD558\uAC8C \uD30C\uC545\uD558\uACE0 \uCF54\uB4DC\uC5D0 \uB9DE\uAC8C \uD65C\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4.`
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
],
|
|
255
|
-
description: "Troubleshooting guide for connection issues between Figma plugin and MCP server"
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
);
|
|
259
187
|
server.registerTool(
|
|
260
|
-
"
|
|
188
|
+
"get_selection",
|
|
261
189
|
{
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
"\uB514\uC790\uC778 \uC2DC\uC2A4\uD15C\uC5D0\uC11C \uC0AC\uC6A9\uD558\uB294 \uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAD6C\uD604 \uADDC\uCE59(\uAD6C\uC870, props, \uC2A4\uD0C0\uC77C \uB4F1)\uC744 \uC870\uD68C\uD558\uB294 MCP \uB3C4\uAD6C\uC785\uB2C8\uB2E4.",
|
|
265
|
-
"Figma \uB514\uC790\uC778\uC5D0\uC11C \uC0AC\uC6A9\uB41C \uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uC774\uB984\uC744 componentName \uC778\uC790\uB85C \uB118\uACA8 \uD638\uCD9C\uD558\uBA74,",
|
|
266
|
-
"\uD574\uB2F9 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAD6C\uD604 \uC815\uBCF4\uB97C \uBC18\uD658\uD558\uC5EC \u2018MCP\uB97C \uD1B5\uD574 \uC0AC\uC6A9\uB41C \uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAD6C\uD604 \uC815\uBCF4\uB97C \uD30C\uC545\uD55C \uB4A4\u2019 \uD398\uC774\uC9C0 \uCF54\uB4DC\uB97C \uC791\uC131\uD560 \uB54C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
267
|
-
"\uC608: Figma\uC5D0\uC11C Button \uCEF4\uD3EC\uB10C\uD2B8\uAC00 \uC4F0\uC600\uB2E4\uBA74 componentName\uC5D0 'button'\uC744 \uB123\uC5B4 \uC774 \uB3C4\uAD6C\uB97C \uD638\uCD9C\uD574 \uAD6C\uD604 \uADDC\uCE59\uC744 \uC870\uD68C\uD558\uC138\uC694."
|
|
268
|
-
].join(" "),
|
|
269
|
-
inputSchema: import_zod.z.object({
|
|
270
|
-
componentName: import_zod.z.string().describe(
|
|
271
|
-
"Figma/\uB514\uC790\uC778 \uC2DC\uC2A4\uD15C\uC5D0\uC11C \uC0AC\uC6A9\uB41C \uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 (\uC608: 'button', 'input', 'modal')"
|
|
272
|
-
)
|
|
273
|
-
})
|
|
190
|
+
description: "Get information about the current selection in Figma",
|
|
191
|
+
inputSchema: {}
|
|
274
192
|
},
|
|
275
|
-
async (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return {
|
|
279
|
-
content: [
|
|
280
|
-
{
|
|
281
|
-
type: "text",
|
|
282
|
-
text: `${texts.join("")}`
|
|
283
|
-
}
|
|
284
|
-
]
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
);
|
|
288
|
-
server.registerResource(
|
|
289
|
-
"file",
|
|
290
|
-
// 리소스 ID
|
|
291
|
-
new import_mcp.ResourceTemplate("file://{path}/", {
|
|
292
|
-
list: async () => {
|
|
193
|
+
async () => {
|
|
194
|
+
try {
|
|
195
|
+
const result = await sendCommandToFigma("get_selection");
|
|
293
196
|
return {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
description: "local file from design-rules",
|
|
299
|
-
mimeType: "text/plain",
|
|
300
|
-
_meta: {
|
|
301
|
-
filePath
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: JSON.stringify(result, null, 2)
|
|
302
201
|
}
|
|
303
|
-
|
|
202
|
+
]
|
|
304
203
|
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
description: "important rules and guidelines for resolving design data"
|
|
310
|
-
},
|
|
311
|
-
// resources/read 핸들러
|
|
312
|
-
async (uri, props) => {
|
|
313
|
-
const filePath = props.path;
|
|
314
|
-
const text = __RULES__[filePath] ?? "";
|
|
315
|
-
return {
|
|
316
|
-
contents: [
|
|
317
|
-
{
|
|
318
|
-
uri: uri.href,
|
|
319
|
-
mimeType: "text/plain",
|
|
320
|
-
text
|
|
321
|
-
}
|
|
322
|
-
]
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
);
|
|
326
|
-
server.registerPrompt(
|
|
327
|
-
"data_analysis_strategy",
|
|
328
|
-
{
|
|
329
|
-
description: "Best practices for analyzing Figma design data"
|
|
330
|
-
},
|
|
331
|
-
() => {
|
|
332
|
-
return {
|
|
333
|
-
messages: [
|
|
334
|
-
{
|
|
335
|
-
role: "assistant",
|
|
336
|
-
content: {
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return {
|
|
206
|
+
content: [
|
|
207
|
+
{
|
|
337
208
|
type: "text",
|
|
338
|
-
text: `
|
|
339
|
-
|
|
340
|
-
1. Data Extraction Workflow:
|
|
341
|
-
- Use get_selection() to focus on specific areas of interest
|
|
342
|
-
- Use get_document_info() when connection is enabled
|
|
343
|
-
|
|
344
|
-
2. Component Analysis:
|
|
345
|
-
- Identify reusable components vs one-off elements
|
|
346
|
-
- Look for design system patterns (colors, typography, spacing)
|
|
347
|
-
- Note component variants and their properties
|
|
348
|
-
- Extract design tokens (colors, fonts, spacing values)
|
|
349
|
-
|
|
350
|
-
3. Layout Analysis:
|
|
351
|
-
- Analyze auto-layout settings and constraints
|
|
352
|
-
- Document spacing patterns and grid systems
|
|
353
|
-
- Identify responsive design patterns
|
|
354
|
-
- Note alignment and positioning strategies
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
`
|
|
209
|
+
text: `Error getting selection: ${error instanceof Error ? error.message : String(error)}`
|
|
360
210
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
};
|
|
211
|
+
]
|
|
212
|
+
};
|
|
213
|
+
}
|
|
365
214
|
}
|
|
366
215
|
);
|
|
367
216
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liner-fe/figma-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "dist/server/server.cjs",
|
|
6
6
|
"files": [
|
|
@@ -65,7 +65,6 @@
|
|
|
65
65
|
"start": "concurrently \"pnpm run socket\" \"pnpm run dev\" --names \"SOCKET,MCP\" --prefix-colors \"blue,green\"",
|
|
66
66
|
"build:server": "pnpm tsup",
|
|
67
67
|
"build": "pnpm build:server",
|
|
68
|
-
"build:package": "pnpm build"
|
|
69
|
-
"generate-from-link": "tsx src/generate-from-link.ts"
|
|
68
|
+
"build:package": "pnpm build"
|
|
70
69
|
}
|
|
71
70
|
}
|