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 CHANGED
@@ -3,7 +3,7 @@ Generated markdown
3
3
  # Zapcode Figma MCP Server
4
4
 
5
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)
6
+ <!-- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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.3", // It's good practice to increment version on significant changes
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 SVG assets to the local 'assets/svg' directory.", {}, async () => {
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
- // Save assets to the current working directory
158
+ // Determine the base path for assets
154
159
  const workspacePath = process.cwd();
155
- const assetResults = await saveSvgAssets(context.assets, workspacePath);
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
- 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\`\`\`\n\nSVG Assets ${assetResults.savedFiles.length > 0
165
- ? "(Saved to " + path.join(workspacePath, "assets", "svg") + "):"
166
- : "(None saved)"}\n${assetResults.savedFiles
167
- .map((p) => `- ${path.basename(p)}`)
168
- .join("\n")}\n\nHTML:\n\`\`\`html\n${context.HTML}\n\`\`\`\n\nCSS:\n\`\`\`css\n${context.CSS}\n\`\`\``;
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 saveSvgAssets(assets, basePath) {
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 !== "svg" || !asset.name || !asset.data)
219
+ if (!asset.type || !asset.name || !asset.data) {
220
+ console.error(`Zapcode: Skipping invalid asset: ${JSON.stringify(asset)}`);
203
221
  continue;
204
- const fileName = asset.name.replace(/[^a-z0-9]/gi, "_").toLowerCase() + ".svg";
205
- const filePath = path.join(assetsDir, fileName);
206
- const svgContent = asset.data.startsWith("data:image/svg+xml;base64,")
207
- ? Buffer.from(asset.data.split(",")[1], "base64").toString()
208
- : asset.data;
209
- fs.writeFileSync(filePath, svgContent);
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.0.4",
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.j npm publishs",
11
+ "start": "node build/index.js",
12
12
  "prepublishOnly": "npm run build"
13
13
  },
14
14
  "keywords": [