@ject-5-fe/figma-plugin 0.0.1

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 ADDED
@@ -0,0 +1,24 @@
1
+ ## Quickstart
2
+
3
+ This plugin was created with [Plugma](https://github.com/gavinmcfarland/plugma) using the [React](https://react.dev/) framework.
4
+
5
+ ### Requirements
6
+
7
+ - [Node.js](https://nodejs.org/en)
8
+ - [Figma desktop app](https://www.figma.com/downloads/)
9
+
10
+ ### 실행 방법
11
+
12
+ 1. **개발 서버 실행**
13
+
14
+ ```bash
15
+ cd shared/figma-plugin
16
+ yarn start
17
+ ```
18
+
19
+ 2. **Figma에서 플러그인 실행**
20
+ - Figma 데스크톱 앱에서 `Plugins` → `Development` → `Import plugin from manifest...` 클릭
21
+ - `shared/figma-plugin/dist/manifest.json` 파일 선택
22
+ - 플러그인이 설치되면 `Plugins` → `Development`에서 실행 가능
23
+ - 플러그인이 실행되면 `connect` 버튼을 클릭하여 mcp서버 사용 가능
24
+ - HMR이 적용되고 있음
@@ -0,0 +1,309 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __copyProps = (to, from, except, desc) => {
8
+ if (from && typeof from === "object" || typeof from === "function") {
9
+ for (let key of __getOwnPropNames(from))
10
+ if (!__hasOwnProp.call(to, key) && key !== except)
11
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
12
+ }
13
+ return to;
14
+ };
15
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
16
+ // If the importer is in node compatibility mode or this is not an ESM
17
+ // file that has been converted to a CommonJS file using a Babel-
18
+ // compatible transform (i.e. "__esModule" has not been set), then set
19
+ // "default" to the CommonJS "module.exports" for node compatibility.
20
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
21
+ mod
22
+ ));
23
+
24
+ // src/server.ts
25
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
26
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
27
+ var import_uuid = require("uuid");
28
+ var import_ws = __toESM(require("ws"), 1);
29
+ var import_zod = require("zod");
30
+ var logger = {
31
+ info: (message) => process.stderr.write(`[INFO] ${message}
32
+ `),
33
+ debug: (message) => process.stderr.write(`[DEBUG] ${message}
34
+ `),
35
+ warn: (message) => process.stderr.write(`[WARN] ${message}
36
+ `),
37
+ error: (message) => process.stderr.write(`[ERROR] ${message}
38
+ `),
39
+ log: (message) => process.stderr.write(`[LOG] ${message}
40
+ `)
41
+ };
42
+ var ws = null;
43
+ var pendingRequests = /* @__PURE__ */ new Map();
44
+ var server = new import_mcp.McpServer({
45
+ name: "@jongh/figma-plugin",
46
+ version: "1.0.0"
47
+ });
48
+ var args = process.argv.slice(2);
49
+ var serverArg = args.find((arg) => arg.startsWith("--server="));
50
+ var serverUrl = serverArg ? serverArg.split("=")[1] : "localhost";
51
+ var WS_URL = serverUrl === "localhost" ? `ws://${serverUrl}` : `wss://${serverUrl}`;
52
+ server.tool(
53
+ "get_document_info",
54
+ "Get detailed information about the current Figma document",
55
+ {},
56
+ async () => {
57
+ try {
58
+ const result = await sendCommandToFigma("get_document_info");
59
+ return {
60
+ content: [
61
+ {
62
+ type: "text",
63
+ text: JSON.stringify(result)
64
+ }
65
+ ]
66
+ };
67
+ } catch (error) {
68
+ return {
69
+ content: [
70
+ {
71
+ type: "text",
72
+ text: `Error getting document info: ${error instanceof Error ? error.message : String(error)}`
73
+ }
74
+ ]
75
+ };
76
+ }
77
+ }
78
+ );
79
+ server.tool(
80
+ "get_selection",
81
+ "Get information about the current selection in Figma",
82
+ {},
83
+ async () => {
84
+ try {
85
+ const result = await sendCommandToFigma("get_selection");
86
+ return {
87
+ content: [
88
+ {
89
+ type: "text",
90
+ text: JSON.stringify(result)
91
+ }
92
+ ]
93
+ };
94
+ } catch (error) {
95
+ return {
96
+ content: [
97
+ {
98
+ type: "text",
99
+ text: `Error getting selection: ${error instanceof Error ? error.message : String(error)}`
100
+ }
101
+ ]
102
+ };
103
+ }
104
+ }
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(
136
+ "connection_troubleshooting",
137
+ "Help users troubleshoot connection issues",
138
+ () => {
139
+ return {
140
+ messages: [
141
+ {
142
+ role: "assistant",
143
+ content: {
144
+ type: "text",
145
+ text: `When users report connection issues, ask them to check:
146
+
147
+ 1. Figma Plugin Connection:
148
+ - Did you click the "Connect" button in the Figma plugin?
149
+ - Is the Figma plugin running and showing a connected status?
150
+ - Try refreshing the plugin or restarting it if needed
151
+
152
+ 2. MCP Connection in Cursor:
153
+ - Go to Cursor Settings/Preferences
154
+ - Check if the MCP server connection is properly configured(green light)
155
+ - Verify the MCP server is running and connected
156
+ - Look for any connection error messages in the settings
157
+
158
+ If both are properly connected and issues persist, try:
159
+ - Restarting both the Figma plugin and Cursor
160
+ - Checking if the WebSocket connection is blocked by firewall
161
+ - Verifying the correct server URL and port configuration`
162
+ }
163
+ }
164
+ ],
165
+ description: "Troubleshooting guide for connection issues between Figma plugin and MCP server"
166
+ };
167
+ }
168
+ );
169
+ server.prompt(
170
+ "data_analysis_strategy",
171
+ "Best practices for analyzing Figma design data",
172
+ () => {
173
+ return {
174
+ messages: [
175
+ {
176
+ role: "assistant",
177
+ content: {
178
+ type: "text",
179
+ text: `When analyzing Figma design data, follow these strategies
180
+
181
+ 1. Data Extraction Workflow:
182
+ - Use get_selection() to focus on specific areas of interest
183
+ - Use get_document_info() when connection is enabled
184
+
185
+ 2. Component Analysis:
186
+ - Identify reusable components vs one-off elements
187
+ - Look for design system patterns (colors, typography, spacing)
188
+ - Note component variants and their properties
189
+ - Extract design tokens (colors, fonts, spacing values)
190
+
191
+ 3. Layout Analysis:
192
+ - Analyze auto-layout settings and constraints
193
+ - Document spacing patterns and grid systems
194
+ - Identify responsive design patterns
195
+ - Note alignment and positioning strategies
196
+
197
+
198
+ `
199
+ }
200
+ }
201
+ ],
202
+ description: "Best practices for working with Figma designs"
203
+ };
204
+ }
205
+ );
206
+ function connectToFigma(port = 3055) {
207
+ if (ws && ws.readyState === import_ws.default.OPEN) {
208
+ logger.info("Already connected to Figma");
209
+ return;
210
+ }
211
+ const wsUrl = serverUrl === "localhost" ? `${WS_URL}:${port}` : WS_URL;
212
+ logger.info(`Connecting to Figma socket server at ${wsUrl}...`);
213
+ ws = new import_ws.default(wsUrl);
214
+ ws.on("open", () => {
215
+ logger.info("Connected to Figma socket server");
216
+ });
217
+ ws.on("message", (data) => {
218
+ try {
219
+ const json = JSON.parse(data);
220
+ const myResponse = json.message;
221
+ logger.debug(`Received message: ${JSON.stringify(myResponse)}`);
222
+ logger.log("myResponse" + JSON.stringify(myResponse));
223
+ if (myResponse.id && pendingRequests.has(myResponse.id) && myResponse.result) {
224
+ const request = pendingRequests.get(myResponse.id);
225
+ clearTimeout(request.timeout);
226
+ if (myResponse.error) {
227
+ logger.error(`Error from Figma: ${myResponse.error}`);
228
+ request.reject(new Error(myResponse.error));
229
+ } else {
230
+ if (myResponse.result) {
231
+ request.resolve(myResponse.result);
232
+ }
233
+ }
234
+ pendingRequests.delete(myResponse.id);
235
+ } else {
236
+ logger.info(`Received broadcast message: ${JSON.stringify(myResponse)}`);
237
+ }
238
+ } catch (error) {
239
+ logger.error(
240
+ `Error parsing message: ${error instanceof Error ? error.message : String(error)}`
241
+ );
242
+ }
243
+ });
244
+ ws.on("error", (error) => {
245
+ logger.error(`Socket error: ${error}`);
246
+ });
247
+ ws.on("close", () => {
248
+ logger.info("Disconnected from Figma socket server");
249
+ ws = null;
250
+ for (const [id, request] of pendingRequests.entries()) {
251
+ clearTimeout(request.timeout);
252
+ request.reject(new Error("Connection closed"));
253
+ pendingRequests.delete(id);
254
+ }
255
+ logger.info("Attempting to reconnect in 2 seconds...");
256
+ setTimeout(() => connectToFigma(port), 2e3);
257
+ });
258
+ }
259
+ function sendCommandToFigma(command, params = {}) {
260
+ return new Promise((resolve, reject) => {
261
+ if (!ws || ws.readyState !== import_ws.default.OPEN) {
262
+ connectToFigma();
263
+ reject(new Error("Not connected to Figma. Attempting to connect..."));
264
+ return;
265
+ }
266
+ const id = (0, import_uuid.v4)();
267
+ const request = {
268
+ id,
269
+ type: "message",
270
+ message: {
271
+ id,
272
+ command,
273
+ params: {
274
+ ...params
275
+ }
276
+ }
277
+ };
278
+ const timeout = setTimeout(() => {
279
+ if (pendingRequests.has(id)) {
280
+ pendingRequests.delete(id);
281
+ logger.error(`Request ${id} to Figma timed out after 30 seconds`);
282
+ reject(new Error("Request to Figma timed out"));
283
+ }
284
+ }, 3e4);
285
+ pendingRequests.set(id, { resolve, reject, timeout });
286
+ logger.info(`Sending command to Figma: ${command}`);
287
+ logger.debug(`Request details: ${JSON.stringify(request)}`);
288
+ ws.send(JSON.stringify(request));
289
+ });
290
+ }
291
+ async function main() {
292
+ try {
293
+ connectToFigma();
294
+ } catch (error) {
295
+ logger.warn(
296
+ `Could not connect to Figma initially: ${error instanceof Error ? error.message : String(error)}`
297
+ );
298
+ logger.warn("Will try to connect when the first command is sent");
299
+ }
300
+ const transport = new import_stdio.StdioServerTransport();
301
+ await server.connect(transport);
302
+ logger.info("FigmaMCP server running on stdio");
303
+ }
304
+ main().catch((error) => {
305
+ logger.error(
306
+ `Error starting FigmaMCP server: ${error instanceof Error ? error.message : String(error)}`
307
+ );
308
+ process.exit(1);
309
+ });
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@ject-5-fe/figma-plugin",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "private": false,
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "scripts": {
10
+ "dev": "plugma dev",
11
+ "socket": "tsx src/socket.ts",
12
+ "start": "concurrently \"yarn run socket\" \"yarn run dev\" --names \"SOCKET,MCP\" --prefix-colors \"blue,green\"",
13
+ "build:server": "tsup"
14
+ },
15
+ "bin": "dist/server/server.cjs",
16
+ "files": [
17
+ "dist/server/server.cjs"
18
+ ],
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1",
21
+ "react": "^18.3.1",
22
+ "react-dom": "^18.3.1",
23
+ "uuid": "^11",
24
+ "ws": "^8",
25
+ "xml-js": "^1.6.11",
26
+ "zod": "^4"
27
+ },
28
+ "devDependencies": {
29
+ "@figma/plugin-typings": "^1.100.2",
30
+ "@ject-5-fe/eslint": "0.0.1",
31
+ "@types/react": "^18.3.12",
32
+ "@types/react-dom": "^18.3.1",
33
+ "@types/uuid": "^9.0.8",
34
+ "@types/ws": "^8.5.10",
35
+ "@vitejs/plugin-react": "^4.3.3",
36
+ "concurrently": "^8",
37
+ "plugma": "^1",
38
+ "tsup": "^8.5.0",
39
+ "tsx": "^4.7.0",
40
+ "vite": "^5.4.10",
41
+ "vite-plugin-generate-file": "^0.3.1"
42
+ },
43
+ "plugma": {
44
+ "manifest": {
45
+ "id": "e-replace",
46
+ "name": "e",
47
+ "main": "src/main.ts",
48
+ "ui": "src/ui.tsx",
49
+ "editorType": [
50
+ "figma",
51
+ "figjam",
52
+ "dev"
53
+ ],
54
+ "capabilities": [
55
+ "inspect"
56
+ ],
57
+ "networkAccess": {
58
+ "allowedDomains": [
59
+ "none"
60
+ ],
61
+ "devAllowedDomains": [
62
+ "http://localhost:*",
63
+ "ws://localhost:3000"
64
+ ]
65
+ }
66
+ }
67
+ }
68
+ }