zapcode-figma-mcp 1.0.0 → 1.0.3

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 (3) hide show
  1. package/README.md +112 -0
  2. package/build/index.js +53 -79
  3. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ Generated markdown
2
+
3
+ # Zapcode Figma MCP Server
4
+
5
+ [![npm version](https://img.shields.io/npm/v/zapcode-figma-mcp.svg)](https://www.npmjs.com/package/zapcode-figma-mcp)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ This package provides a Model Context Protocol (MCP) server that acts as a bridge between your **Figma designs** and any **MCP-compatible AI client** (like Claude for Desktop, Open WebUI, etc.).
9
+
10
+ When invoked by an AI model, this server connects to the Zapcode Figma plugin to retrieve real-time context from your selected Figma frame, including its HTML/CSS structure, a generated image, and any exportable SVG assets.
11
+
12
+ ## ✨ Features
13
+
14
+ - **Real-time Context**: Fetches live data directly from your currently selected frame in Figma.
15
+ - **Rich Information**: Provides the LLM with rich context of the frame.
16
+ - **Automatic Asset Handling**: Automatically extracts any SVG assets from the selection, saves them to your local workspace, and informs the LLM of their location.
17
+ - **Easy Integration**: Runs as a simple command-line tool and can be easily configured with any MCP client.
18
+
19
+ ## 📋 Prerequisites
20
+
21
+ 1. **Node.js**: You must have Node.js (v16 or higher) installed to use `npx`. You can download it from [nodejs.org](https://nodejs.org/).
22
+ 2. [**Zapcode Figma Plugin**](https://www.figma.com/community/plugin/1454956820198178710/zapcode): You must have the Zapcode plugin installed and running in your Figma desktop application.
23
+
24
+ ## 🚀 Installation & Usage
25
+
26
+ You can run this server directly from the npm registry without a manual installation using `npx`.
27
+
28
+ ## Integration with an MCP Client
29
+
30
+ This is the most common use case. You configure your client to launch the server automatically. Below are examples for common clients.
31
+
32
+ ### Generic MCP Client Configuration
33
+
34
+ For clients that use a similar JSON configuration, the structure is the same. This server does not require any environment variables.
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "zapcode-figma-mcp": {
40
+ "command": "npx",
41
+ "args": ["zapcode-figma-mcp"]
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### GitHub Copilot
48
+
49
+ ```json
50
+ {
51
+ "servers": {
52
+ "Zapcode stdio mcp": {
53
+ "type": "stdio",
54
+ "command": "npx",
55
+ "args": ["zapcode-figma-mcp"]
56
+ }
57
+ },
58
+ "inputs": []
59
+ }
60
+ ```
61
+
62
+ ### Claude for Desktop
63
+
64
+ Open your claude_desktop_config.json file and add the following entry to mcpServers.
65
+
66
+ macOS/Linux: ~/Library/Application Support/Claude/claude_desktop_config.json
67
+ Windows: %APPDATA%\Claude\claude_desktop_config.json
68
+
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "zapcode-figma-mcp": {
73
+ "command": "npx",
74
+ "args": ["zapcode-figma-mcp"]
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ **Note:** Restart Claude for Desktop for the changes to take effect.
81
+
82
+ The server will start and wait for connections from both the Figma plugin and an MCP client.
83
+
84
+ ## 🛠️ Tool Details
85
+
86
+ This server exposes a single, powerful tool to the LLM.
87
+
88
+ get_figma_context
89
+
90
+ - **Description**: Retrieves the selected Figma frame context. Saves any provided SVG assets to the local 'assets/svg' directory.
91
+ - **Input Schema**: {} (No input arguments are required).
92
+ - **Output**: A combination of prompts and Figma context
93
+
94
+ ## ⚠️ Troubleshooting
95
+
96
+ ```error
97
+ Error: Figma plugin is not connected.
98
+ ```
99
+
100
+ This is the most common error. It means the MCP server is running, but it cannot find the Zapcode Figma plugin to talk to.
101
+ Solution: Make sure you have opened Figma and are running the Zapcode plugin on the design file you wish to query.
102
+
103
+ ```error
104
+ Port Conflict / EADDRINUSE Error
105
+ ```
106
+
107
+ If you see an error that the port is already in use, it means another application is using port 3001 or 32896.
108
+ Solution: You need to stop the other application.
109
+
110
+ ## 📄 License
111
+
112
+ This project is licensed under the MIT License. See the LICENSE.md file for details.
package/build/index.js CHANGED
@@ -1,43 +1,34 @@
1
1
  #!/usr/bin/env node
2
- import { createServer as createHttpServer, } from "http";
2
+ import { createServer as createHttpServer } from "http";
3
3
  import { Server as SocketIOServer } from "socket.io";
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
6
- import { URL } from "url";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
6
  import * as fs from "fs";
8
7
  import * as path from "path";
9
- // --- Default Ports ---
8
+ // --- Default Port for Figma Connection ---
10
9
  const DEFAULT_FIGMA_SOCKET_IO_PORT = 32896;
11
- const DEFAULT_MCP_HTTP_PORT = 3001;
12
- // --- Paths ---
13
- const MCP_SSE_PATH = "/zapcode-mcp-sse";
14
- const MCP_MESSAGE_PATH = "/mcp-messages";
15
- // --- Global Variables ---
10
+ // --- Global Variables for Figma Connection ---
16
11
  let figmaHttpServer = null;
17
12
  let io = null;
18
13
  let connectedFigmaClient = null;
19
14
  const figmaContextRequests = new Map();
20
- const figmaHttpConnections = new Set();
21
- let mcpHttpServer = null;
22
- let mcpServerInstance = null;
23
- const mcpSseTransports = new Map();
24
15
  // --- Argument Parsing ---
25
16
  function parseArgs() {
26
17
  const args = process.argv.slice(2);
27
- let mcpPort = DEFAULT_MCP_HTTP_PORT;
28
- const portArgIndex = args.findIndex((arg) => arg === "--port" || arg === "-p");
29
- if (portArgIndex !== -1 && args[portArgIndex + 1]) {
30
- const port = parseInt(args[portArgIndex + 1], 10);
31
- if (!isNaN(port)) {
32
- mcpPort = port;
33
- }
18
+ let figmaPort = DEFAULT_FIGMA_SOCKET_IO_PORT;
19
+ const figmaPortArgIndex = args.findIndex((arg) => arg === "--figma-port");
20
+ if (figmaPortArgIndex !== -1 && args[figmaPortArgIndex + 1]) {
21
+ const port = parseInt(args[figmaPortArgIndex + 1], 10);
22
+ if (!isNaN(port))
23
+ figmaPort = port;
34
24
  }
35
- return { mcpPort };
25
+ return { figmaPort };
36
26
  }
37
27
  // --- Socket.IO Server Logic ---
38
28
  function startSocketIOServer(port) {
39
29
  return new Promise((resolve, reject) => {
40
- console.log(`Attempting to start Socket.IO server for Figma on port ${port}...`);
30
+ // IMPORTANT: All logging must go to stderr to not interfere with MCP on stdout.
31
+ console.error(`Zapcode: Attempting to start Socket.IO server for Figma on port ${port}...`);
41
32
  try {
42
33
  figmaHttpServer = createHttpServer();
43
34
  io = new SocketIOServer(figmaHttpServer, {
@@ -45,7 +36,7 @@ function startSocketIOServer(port) {
45
36
  cors: { origin: "*" },
46
37
  });
47
38
  io.on("connection", (socket) => {
48
- console.log(`Figma plugin connected: ${socket.id}`);
39
+ console.error(`Zapcode: Figma plugin connected: ${socket.id}`);
49
40
  if (connectedFigmaClient) {
50
41
  connectedFigmaClient.disconnect(true);
51
42
  }
@@ -65,38 +56,43 @@ function startSocketIOServer(port) {
65
56
  }
66
57
  });
67
58
  socket.on("disconnect", (reason) => {
68
- console.log(`Figma plugin disconnected: ${socket.id}. Reason: ${reason}`);
59
+ console.error(`Zapcode: Figma plugin disconnected: ${socket.id}. Reason: ${reason}`);
69
60
  if (connectedFigmaClient === socket) {
70
61
  connectedFigmaClient = null;
71
62
  }
72
63
  });
73
- socket.on("error", (err) => console.error(`Figma Socket.IO error: ${err.message}`));
64
+ socket.on("error", (err) => console.error(`Zapcode: Figma Socket.IO error: ${err.message}`));
65
+ });
66
+ figmaHttpServer.on("error", (err) => {
67
+ console.error(`Zapcode: Figma HTTP server error: ${err.message}`);
68
+ reject(err);
74
69
  });
75
70
  figmaHttpServer.listen(port, () => {
76
- console.log(`✅ Figma Socket.IO server listening on port ${port}`);
71
+ console.error(`Zapcode: Figma Socket.IO server is listening on port ${port}`);
77
72
  resolve();
78
73
  });
79
74
  }
80
75
  catch (error) {
81
- console.error(`Error creating Figma Socket.IO server: ${error}`);
76
+ console.error(`Zapcode: Error creating Figma Socket.IO server: ${error}`);
82
77
  reject(error);
83
78
  }
84
79
  });
85
80
  }
86
- // --- MCP Server Logic ---
87
- async function startMcpServer(port) {
88
- console.log(`Attempting to start MCP HTTP/SSE server on port ${port}...`);
81
+ // --- MCP Server Logic for Stdio ---
82
+ async function startMcpServer() {
83
+ console.error("Zapcode: Initializing MCP server for Stdio transport.");
89
84
  const serverInfo = {
90
85
  name: "zapcode-figma-mcp-server",
91
86
  title: "Zapcode Figma MCP",
92
- version: "1.0.2",
87
+ version: "1.0.3", // It's good practice to increment version on significant changes
93
88
  };
94
- const serverOptions = { capabilities: { tools: {} } };
95
- mcpServerInstance = new McpServer(serverInfo, serverOptions);
89
+ const mcpServerInstance = new McpServer(serverInfo, {
90
+ capabilities: { tools: {} },
91
+ });
96
92
  mcpServerInstance.tool("get_figma_context", "Retrieves the selected Figma frame context (HTML, CSS, prompt, image, assets). Saves any provided SVG assets to the local 'assets/svg' directory.", {}, async () => {
97
- console.log("MCP Tool 'get_figma_context' invoked.");
93
+ console.error("Zapcode: MCP Tool 'get_figma_context' invoked.");
98
94
  if (!connectedFigmaClient?.connected) {
99
- console.error("Error: Figma plugin not connected.");
95
+ console.error("Zapcode: Error - Figma plugin not connected.");
100
96
  return {
101
97
  isError: true,
102
98
  content: [
@@ -109,7 +105,7 @@ async function startMcpServer(port) {
109
105
  }
110
106
  const requestId = `figma-ctx-${Date.now()}`;
111
107
  try {
112
- console.log(`Emitting 'request_context' to Figma (ID: ${requestId})`);
108
+ console.error(`Zapcode: Emitting 'request_context' to Figma (ID: ${requestId})`);
113
109
  connectedFigmaClient.emit("request_context", { requestId });
114
110
  const context = await new Promise((resolve, reject) => {
115
111
  figmaContextRequests.set(requestId, { resolve, reject });
@@ -120,7 +116,7 @@ async function startMcpServer(port) {
120
116
  }
121
117
  }, 30000);
122
118
  });
123
- console.log("Successfully received context from Figma.");
119
+ console.error("Zapcode: Successfully received context from Figma.");
124
120
  // Save assets to the current working directory
125
121
  const workspacePath = process.cwd();
126
122
  const assetResults = await saveSvgAssets(context.assets, workspacePath);
@@ -141,7 +137,7 @@ async function startMcpServer(port) {
141
137
  return { content: mcpContent };
142
138
  }
143
139
  catch (error) {
144
- console.error(`Error during Figma context request: ${error.message}`);
140
+ console.error(`Zapcode: Error during Figma context request: ${error.message}`);
145
141
  return {
146
142
  isError: true,
147
143
  content: [
@@ -153,38 +149,12 @@ async function startMcpServer(port) {
153
149
  };
154
150
  }
155
151
  });
156
- return new Promise((resolve, reject) => {
157
- mcpHttpServer = createHttpServer(async (req, res) => {
158
- const requestUrl = new URL(req.url || "", `http://${req.headers.host}`);
159
- if (req.method === "GET" && requestUrl.pathname === MCP_SSE_PATH) {
160
- const transport = new SSEServerTransport(MCP_MESSAGE_PATH, res);
161
- mcpSseTransports.set(transport.sessionId, transport);
162
- console.log(`New MCP client connected. SessionID: ${transport.sessionId}`);
163
- req.socket.on("close", () => mcpSseTransports.delete(transport.sessionId));
164
- await mcpServerInstance.connect(transport);
165
- }
166
- else if (req.method === "POST" &&
167
- requestUrl.pathname === MCP_MESSAGE_PATH) {
168
- const sessionId = requestUrl.searchParams.get("sessionId");
169
- const transport = sessionId
170
- ? mcpSseTransports.get(sessionId)
171
- : undefined;
172
- if (transport)
173
- await transport.handlePostMessage(req, res);
174
- else
175
- res.writeHead(404).end("Session not found");
176
- }
177
- else {
178
- res.writeHead(404).end("Not Found");
179
- }
180
- });
181
- mcpHttpServer.listen(port, () => {
182
- console.log(`✅ MCP HTTP/SSE server listening on http://localhost:${port}${MCP_SSE_PATH}`);
183
- resolve();
184
- });
185
- });
152
+ // Use the Stdio transport
153
+ const transport = new StdioServerTransport();
154
+ await mcpServerInstance.connect(transport);
155
+ console.error("Zapcode: MCP server connected and listening on stdio.");
186
156
  }
187
- // --- Utility Functions (modified to remove vscode dependency) ---
157
+ // --- Utility Functions ---
188
158
  async function saveSvgAssets(assets, basePath) {
189
159
  const savedFiles = [];
190
160
  const errors = [];
@@ -214,24 +184,28 @@ async function saveSvgAssets(assets, basePath) {
214
184
  }
215
185
  // --- Main Execution Logic ---
216
186
  async function main() {
217
- const { mcpPort } = parseArgs();
218
- console.log("Starting Figma MCP Server...");
187
+ const { figmaPort } = parseArgs();
219
188
  try {
220
- await startSocketIOServer(DEFAULT_FIGMA_SOCKET_IO_PORT);
221
- await startMcpServer(mcpPort);
222
- console.log("🚀 All servers are up and running!");
223
- console.log("Connect your Figma plugin and an MCP client to begin.");
189
+ await startSocketIOServer(figmaPort);
190
+ await startMcpServer();
224
191
  }
225
192
  catch (error) {
226
- console.error("💥 Failed to start servers:", error);
193
+ console.error("Zapcode: 💥 Failed to start servers:", error);
227
194
  process.exit(1);
228
195
  }
229
196
  }
230
197
  // --- Graceful Shutdown ---
231
198
  async function shutdown() {
232
- console.log("\nGracefully shutting down servers...");
233
- // Implement stop functions if needed, or just exit for this simple case
234
- process.exit(0);
199
+ console.error("\nZapcode: Gracefully shutting down...");
200
+ if (figmaHttpServer) {
201
+ figmaHttpServer.close(() => {
202
+ console.error("Zapcode: Figma HTTP server closed.");
203
+ process.exit(0);
204
+ });
205
+ }
206
+ else {
207
+ process.exit(0);
208
+ }
235
209
  }
236
210
  process.on("SIGINT", shutdown);
237
211
  process.on("SIGTERM", shutdown);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zapcode-figma-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "An MCP server that exposes Figma context via a plugin and saves assets, from Zapcode.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,7 @@
19
19
  "zapcode"
20
20
  ],
21
21
  "author": "Zapcode",
22
- "license": "SEE LICENSE IN LICENSE.md",
22
+ "license": "MIT",
23
23
  "files": [
24
24
  "build/"
25
25
  ],