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.
- package/README.md +112 -0
- package/build/index.js +53 -79
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Generated markdown
|
|
2
|
+
|
|
3
|
+
# Zapcode Figma MCP Server
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/zapcode-figma-mcp)
|
|
6
|
+
[](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
|
|
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 {
|
|
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
|
|
8
|
+
// --- Default Port for Figma Connection ---
|
|
10
9
|
const DEFAULT_FIGMA_SOCKET_IO_PORT = 32896;
|
|
11
|
-
|
|
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
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
const port = parseInt(args[
|
|
31
|
-
if (!isNaN(port))
|
|
32
|
-
|
|
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 {
|
|
25
|
+
return { figmaPort };
|
|
36
26
|
}
|
|
37
27
|
// --- Socket.IO Server Logic ---
|
|
38
28
|
function startSocketIOServer(port) {
|
|
39
29
|
return new Promise((resolve, reject) => {
|
|
40
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
88
|
-
console.
|
|
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.
|
|
87
|
+
version: "1.0.3", // It's good practice to increment version on significant changes
|
|
93
88
|
};
|
|
94
|
-
const
|
|
95
|
-
|
|
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.
|
|
93
|
+
console.error("Zapcode: MCP Tool 'get_figma_context' invoked.");
|
|
98
94
|
if (!connectedFigmaClient?.connected) {
|
|
99
|
-
console.error("
|
|
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.
|
|
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.
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
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 {
|
|
218
|
-
console.log("Starting Figma MCP Server...");
|
|
187
|
+
const { figmaPort } = parseArgs();
|
|
219
188
|
try {
|
|
220
|
-
await startSocketIOServer(
|
|
221
|
-
await startMcpServer(
|
|
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.
|
|
233
|
-
|
|
234
|
-
|
|
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.
|
|
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": "
|
|
22
|
+
"license": "MIT",
|
|
23
23
|
"files": [
|
|
24
24
|
"build/"
|
|
25
25
|
],
|