zapcode-figma-mcp 1.0.4 → 1.1.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 +1 -1
- package/build/index.js +66 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ Generated markdown
|
|
|
3
3
|
# Zapcode Figma MCP Server
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/zapcode-figma-mcp)
|
|
6
|
-
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
<!-- [](https://opensource.org/licenses/MIT) -->
|
|
7
7
|
|
|
8
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
9
|
|
package/build/index.js
CHANGED
|
@@ -117,12 +117,17 @@ async function startMcpServer() {
|
|
|
117
117
|
const serverInfo = {
|
|
118
118
|
name: "zapcode-figma-mcp-server",
|
|
119
119
|
title: "Zapcode Figma MCP",
|
|
120
|
-
version: "1.0
|
|
120
|
+
version: "1.1.0",
|
|
121
121
|
};
|
|
122
122
|
const mcpServerInstance = new McpServer(serverInfo, {
|
|
123
123
|
capabilities: { tools: {} },
|
|
124
124
|
});
|
|
125
|
-
mcpServerInstance.tool("get_figma_context", "Retrieves the selected Figma frame context (HTML, CSS, prompt, image, assets). Saves any provided
|
|
125
|
+
mcpServerInstance.tool("get_figma_context", "Retrieves the selected Figma frame context (HTML, CSS, prompt, image, assets). Saves any provided assets to the local directory. Accepts an optional 'assets_path' parameter to specify where assets should be saved.", {
|
|
126
|
+
assets_path: {
|
|
127
|
+
type: "string",
|
|
128
|
+
description: "Optional absolute or relative path where assets should be saved. Defaults to 'assets' in the current working directory.",
|
|
129
|
+
},
|
|
130
|
+
}, async (params) => {
|
|
126
131
|
console.error("Zapcode: MCP Tool 'get_figma_context' invoked.");
|
|
127
132
|
if (!connectedFigmaClient?.connected) {
|
|
128
133
|
console.error("Zapcode: Error - Figma plugin not connected.");
|
|
@@ -150,9 +155,14 @@ async function startMcpServer() {
|
|
|
150
155
|
}, 30000);
|
|
151
156
|
});
|
|
152
157
|
console.error("Zapcode: Successfully received context from Figma.");
|
|
153
|
-
//
|
|
158
|
+
// Determine the base path for assets
|
|
154
159
|
const workspacePath = process.cwd();
|
|
155
|
-
const
|
|
160
|
+
const assetsBasePath = params.assets_path
|
|
161
|
+
? path.isAbsolute(params.assets_path)
|
|
162
|
+
? params.assets_path
|
|
163
|
+
: path.join(workspacePath, params.assets_path)
|
|
164
|
+
: path.join(workspacePath, "assets");
|
|
165
|
+
const assetResults = await saveAssets(context.assets, assetsBasePath);
|
|
156
166
|
const mcpContent = [];
|
|
157
167
|
if (context.image) {
|
|
158
168
|
mcpContent.push({
|
|
@@ -161,11 +171,21 @@ async function startMcpServer() {
|
|
|
161
171
|
mimeType: "image/png",
|
|
162
172
|
});
|
|
163
173
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
.
|
|
174
|
+
// Format assets information by type
|
|
175
|
+
let assetsInfo = "";
|
|
176
|
+
if (assetResults.savedFiles.length > 0) {
|
|
177
|
+
assetsInfo = `\n\nAssets (Saved to ${assetsBasePath}):\n`;
|
|
178
|
+
assetResults.assetsByType.forEach((files, type) => {
|
|
179
|
+
assetsInfo += `\n${type.toUpperCase()} Files:\n`;
|
|
180
|
+
files.forEach((filePath) => {
|
|
181
|
+
assetsInfo += `- ${path.basename(filePath)}\n`;
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
assetsInfo = "\n\nAssets: (None saved)";
|
|
187
|
+
}
|
|
188
|
+
const textDetails = `Figma Context Details:\n\nPrompt Suggestion:\n${context.prompt}\n\nTechnology Configuration:\n\`\`\`json\n${JSON.stringify(context.tech_config, null, 2)}\n\`\`\`${assetsInfo}\n\nHTML:\n\`\`\`html\n${context.HTML}\n\`\`\`\n\nCSS:\n\`\`\`css\n${context.CSS}\n\`\`\``;
|
|
169
189
|
mcpContent.push({ type: "text", text: textDetails });
|
|
170
190
|
return { content: mcpContent };
|
|
171
191
|
}
|
|
@@ -188,32 +208,55 @@ async function startMcpServer() {
|
|
|
188
208
|
console.error("Zapcode: ✅ MCP server connected and listening on stdio.");
|
|
189
209
|
}
|
|
190
210
|
// --- Utility Functions ---
|
|
191
|
-
async function
|
|
211
|
+
async function saveAssets(assets, basePath) {
|
|
192
212
|
const savedFiles = [];
|
|
193
213
|
const errors = [];
|
|
214
|
+
const assetsByType = new Map();
|
|
194
215
|
if (!Array.isArray(assets))
|
|
195
|
-
return { savedFiles, errors: ["No assets provided"] };
|
|
196
|
-
const assetsDir = path.join(basePath, "assets", "svg");
|
|
216
|
+
return { savedFiles, errors: ["No assets provided"], assetsByType };
|
|
197
217
|
try {
|
|
198
|
-
if (!fs.existsSync(assetsDir)) {
|
|
199
|
-
fs.mkdirSync(assetsDir, { recursive: true });
|
|
200
|
-
}
|
|
201
218
|
for (const asset of assets) {
|
|
202
|
-
if (asset.type
|
|
219
|
+
if (!asset.type || !asset.name || !asset.data) {
|
|
220
|
+
console.error(`Zapcode: Skipping invalid asset: ${JSON.stringify(asset)}`);
|
|
203
221
|
continue;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
:
|
|
209
|
-
|
|
222
|
+
}
|
|
223
|
+
// Create subdirectory for this asset type
|
|
224
|
+
const typeDir = path.join(basePath, asset.type);
|
|
225
|
+
if (!fs.existsSync(typeDir)) {
|
|
226
|
+
fs.mkdirSync(typeDir, { recursive: true });
|
|
227
|
+
}
|
|
228
|
+
// Sanitize filename and add appropriate extension
|
|
229
|
+
const sanitizedName = asset.name.replace(/[^a-z0-9]/gi, "_").toLowerCase();
|
|
230
|
+
const fileName = `${sanitizedName}.${asset.type}`;
|
|
231
|
+
const filePath = path.join(typeDir, fileName);
|
|
232
|
+
// Handle base64-encoded data
|
|
233
|
+
let fileContent;
|
|
234
|
+
if (asset.data.startsWith("data:")) {
|
|
235
|
+
// Extract base64 content from data URL
|
|
236
|
+
const base64Data = asset.data.split(",")[1];
|
|
237
|
+
fileContent = Buffer.from(base64Data, "base64");
|
|
238
|
+
}
|
|
239
|
+
else if (asset.type === "svg") {
|
|
240
|
+
// SVG might be plain text
|
|
241
|
+
fileContent = asset.data;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// Assume it's already base64 string
|
|
245
|
+
fileContent = Buffer.from(asset.data, "base64");
|
|
246
|
+
}
|
|
247
|
+
fs.writeFileSync(filePath, fileContent);
|
|
210
248
|
savedFiles.push(filePath);
|
|
249
|
+
// Track assets by type
|
|
250
|
+
if (!assetsByType.has(asset.type)) {
|
|
251
|
+
assetsByType.set(asset.type, []);
|
|
252
|
+
}
|
|
253
|
+
assetsByType.get(asset.type).push(filePath);
|
|
211
254
|
}
|
|
212
255
|
}
|
|
213
256
|
catch (err) {
|
|
214
257
|
errors.push(`Failed to save assets: ${err.message}`);
|
|
215
258
|
}
|
|
216
|
-
return { savedFiles, errors };
|
|
259
|
+
return { savedFiles, errors, assetsByType };
|
|
217
260
|
}
|
|
218
261
|
// --- Main Execution Logic ---
|
|
219
262
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zapcode-figma-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
|
-
"start": "node build/index.
|
|
11
|
+
"start": "node build/index.js",
|
|
12
12
|
"prepublishOnly": "npm run build"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|