figma-mcp-lightweight 1.0.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/talk_to_figma_mcp/server.ts","../../src/talk_to_figma_mcp/config/config.ts","../../src/talk_to_figma_mcp/utils/logger.ts","../../src/talk_to_figma_mcp/utils/websocket.ts","../../src/talk_to_figma_mcp/tools/execution-tools.ts","../../src/talk_to_figma_mcp/tools/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Main entry point for the Figma MCP Server\n * This file initializes the server, connects to Figma,\n * and registers all tools and prompts.\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\n// Import configuration\nimport { SERVER_CONFIG } from \"./config/config\";\n\n// Import utilities\nimport { logger } from \"./utils/logger\";\nimport { connectToFigma } from \"./utils/websocket\";\n\n// Import tools registration function from tools/index.ts\nimport { registerTools } from \"./tools\";\n\n/**\n * Initialize and start the MCP server\n */\nasync function main() {\n try {\n // Create MCP server instance with configuration\n const server = new McpServer(SERVER_CONFIG);\n \n // Register all tools with the server\n registerTools(server);\n \n // Try to connect to Figma socket server\n try {\n connectToFigma();\n } catch (error) {\n logger.warn(`Could not connect to Figma initially: ${error instanceof Error ? error.message : String(error)}`);\n logger.warn('Will try to connect when the first command is sent');\n }\n\n // Start the MCP server with stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info('FigmaMCP server running on stdio');\n } catch (error) {\n logger.error(`Error starting FigmaMCP server: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n}\n\n// Run the server\nmain().catch(error => {\n logger.error(`Error starting FigmaMCP server: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n});\n\n","// Argumentos de línea de comandos\nconst args = process.argv.slice(2);\nconst serverArg = args.find(arg => arg.startsWith('--server='));\nconst portArg = args.find(arg => arg.startsWith('--port='));\nconst reconnectArg = args.find(arg => arg.startsWith('--reconnect-interval='));\n\n// Configuración de conexión extraída de argumentos CLI\nexport const serverUrl = serverArg ? serverArg.split('=')[1] : 'localhost';\nexport const defaultPort = portArg ? parseInt(portArg.split('=')[1], 10) : 3055;\nexport const reconnectInterval = reconnectArg ? parseInt(reconnectArg.split('=')[1], 10) : 2000;\n\n// URL de WebSocket basada en el servidor (WS para localhost, WSS para remoto)\nexport const WS_URL = serverUrl === 'localhost' ? `ws://${serverUrl}` : `wss://${serverUrl}`;\n\n// Configuración del servidor MCP\nexport const SERVER_CONFIG = {\n name: \"ClaudeTalkToFigmaMCP\",\n description: \"Claude MCP Plugin for Figma\",\n version: \"0.4.0\",\n};","// Custom logging functions that write to stderr instead of stdout to avoid being captured\nexport const logger = {\n info: (message: string) => process.stderr.write(`[INFO] ${message}\\n`),\n debug: (message: string) => process.stderr.write(`[DEBUG] ${message}\\n`),\n warn: (message: string) => process.stderr.write(`[WARN] ${message}\\n`),\n error: (message: string) => process.stderr.write(`[ERROR] ${message}\\n`),\n log: (message: string) => process.stderr.write(`[LOG] ${message}\\n`)\n};","import WebSocket from \"ws\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { logger } from \"./logger\";\nimport { serverUrl, defaultPort, WS_URL, reconnectInterval } from \"../config/config\";\nimport { FigmaCommand, FigmaResponse, CommandProgressUpdate, PendingRequest, ProgressMessage } from \"../types\";\n\n// WebSocket connection and request tracking\nlet ws: WebSocket | null = null;\nlet currentChannel: string | null = null;\n\n// Map of pending requests for promise tracking\nconst pendingRequests = new Map<string, PendingRequest>();\n\n/**\n * Connects to the Figma server via WebSocket.\n * @param port - Optional port for the connection (defaults to defaultPort from config)\n */\nexport function connectToFigma(port: number = defaultPort) {\n // If already connected, do nothing\n if (ws && ws.readyState === WebSocket.OPEN) {\n logger.info('Already connected to Figma');\n return;\n }\n\n // If connection is in progress (CONNECTING state), wait\n if (ws && ws.readyState === WebSocket.CONNECTING) {\n logger.info('Connection to Figma is already in progress');\n return;\n }\n\n // If there's an existing socket in a closing state, clean it up\n if (ws && (ws.readyState === WebSocket.CLOSING || ws.readyState === WebSocket.CLOSED)) {\n ws.removeAllListeners();\n ws = null;\n }\n\n const wsUrl = serverUrl === 'localhost' ? `${WS_URL}:${port}` : WS_URL;\n logger.info(`Connecting to Figma socket server at ${wsUrl}...`);\n \n try {\n ws = new WebSocket(wsUrl);\n \n // Add connection timeout\n const connectionTimeout = setTimeout(() => {\n if (ws && ws.readyState === WebSocket.CONNECTING) {\n logger.error('Connection to Figma timed out');\n ws.terminate();\n }\n }, 10000); // 10 second connection timeout\n \n ws.on('open', () => {\n clearTimeout(connectionTimeout);\n logger.info('Connected to Figma socket server');\n // Reset channel on new connection\n currentChannel = null;\n });\n\n ws.on(\"message\", (data: any) => {\n try {\n const json = JSON.parse(data) as ProgressMessage;\n\n // Handle progress updates\n if (json.type === 'progress_update') {\n const progressData = json.message.data as CommandProgressUpdate;\n const requestId = json.id || '';\n\n if (requestId && pendingRequests.has(requestId)) {\n const request = pendingRequests.get(requestId)!;\n\n // Update last activity timestamp\n request.lastActivity = Date.now();\n\n // Reset the timeout to prevent timeouts during long-running operations\n clearTimeout(request.timeout);\n\n // Create a new timeout\n request.timeout = setTimeout(() => {\n if (pendingRequests.has(requestId)) {\n logger.error(`Request ${requestId} timed out after extended period of inactivity`);\n pendingRequests.delete(requestId);\n request.reject(new Error('Request to Figma timed out'));\n }\n }, 60000); // 60 second timeout for inactivity\n\n // Log progress\n logger.info(`Progress update for ${progressData.commandType}: ${progressData.progress}% - ${progressData.message}`);\n\n // For completed updates, we could resolve the request early if desired\n if (progressData.status === 'completed' && progressData.progress === 100) {\n // Optionally resolve early with partial data\n // request.resolve(progressData.payload);\n // pendingRequests.delete(requestId);\n\n // Instead, just log the completion, wait for final result from Figma\n logger.info(`Operation ${progressData.commandType} completed, waiting for final result`);\n }\n }\n return;\n }\n\n // Handle regular responses\n const myResponse = json.message;\n logger.debug(`Received message: ${JSON.stringify(myResponse)}`);\n logger.log('myResponse' + JSON.stringify(myResponse));\n\n // Handle response to a request\n if (\n myResponse.id &&\n pendingRequests.has(myResponse.id) &&\n (myResponse.result !== undefined || myResponse.error)\n ) {\n const request = pendingRequests.get(myResponse.id)!;\n clearTimeout(request.timeout);\n\n if (myResponse.error) {\n logger.error(`Error from Figma: ${myResponse.error}`);\n request.reject(new Error(myResponse.error));\n } else {\n request.resolve(myResponse.result);\n }\n\n pendingRequests.delete(myResponse.id);\n } else {\n // Handle broadcast messages or events\n logger.info(`Received broadcast message: ${JSON.stringify(myResponse)}`);\n }\n } catch (error) {\n logger.error(`Error parsing message: ${error instanceof Error ? error.message : String(error)}`);\n }\n });\n\n ws.on('error', (error) => {\n logger.error(`Socket error: ${error}`);\n // Don't attempt to reconnect here, let the close handler do it\n });\n\n ws.on('close', (code, reason) => {\n clearTimeout(connectionTimeout);\n logger.info(`Disconnected from Figma socket server with code ${code} and reason: ${reason || 'No reason provided'}`);\n ws = null;\n\n // Reject all pending requests\n for (const [id, request] of pendingRequests.entries()) {\n clearTimeout(request.timeout);\n request.reject(new Error(`Connection closed with code ${code}: ${reason || 'No reason provided'}`));\n pendingRequests.delete(id);\n }\n\n // Attempt to reconnect with exponential backoff\n const backoff = Math.min(30000, reconnectInterval * Math.pow(1.5, Math.floor(Math.random() * 5))); // Max 30s\n logger.info(`Attempting to reconnect in ${backoff/1000} seconds...`);\n setTimeout(() => connectToFigma(port), backoff);\n });\n \n } catch (error) {\n logger.error(`Failed to create WebSocket connection: ${error instanceof Error ? error.message : String(error)}`);\n // Attempt to reconnect after a delay\n setTimeout(() => connectToFigma(port), reconnectInterval);\n }\n}\n\n/**\n * Join a specific channel in Figma.\n * @param channelName - Name of the channel to join\n * @returns Promise that resolves when successfully joined the channel\n */\nexport async function joinChannel(channelName: string): Promise<void> {\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n throw new Error(\"Not connected to Figma\");\n }\n\n try {\n await sendCommandToFigma(\"join\", { channel: channelName });\n currentChannel = channelName;\n logger.info(`Joined channel: ${channelName}`);\n } catch (error) {\n logger.error(`Failed to join channel: ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n}\n\n/**\n * Send a command to Figma via WebSocket.\n * @param command - The command to send\n * @param params - Additional parameters for the command\n * @param timeoutMs - Timeout in milliseconds before failing\n * @returns A promise that resolves with the Figma response\n */\nexport function sendCommandToFigma(\n command: FigmaCommand,\n params: unknown = {},\n timeoutMs: number = 30000\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n // If not connected, try to connect first\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n connectToFigma();\n reject(new Error(\"Not connected to Figma. Attempting to connect...\"));\n return;\n }\n\n // Check if we need a channel for this command\n const requiresChannel = command !== \"join\";\n if (requiresChannel && !currentChannel) {\n reject(new Error(\"Must join a channel before sending commands\"));\n return;\n }\n\n const id = uuidv4();\n const request = {\n id,\n type: command === \"join\" ? \"join\" : \"message\",\n ...(command === \"join\"\n ? { channel: (params as any).channel }\n : { channel: currentChannel }),\n message: {\n id,\n command,\n params: {\n ...(params as any),\n commandId: id, // Include the command ID in params\n },\n },\n };\n\n // Set timeout for request\n const timeout = setTimeout(() => {\n if (pendingRequests.has(id)) {\n pendingRequests.delete(id);\n logger.error(`Request ${id} to Figma timed out after ${timeoutMs / 1000} seconds`);\n reject(new Error('Request to Figma timed out'));\n }\n }, timeoutMs);\n\n // Store the promise callbacks to resolve/reject later\n pendingRequests.set(id, {\n resolve,\n reject,\n timeout,\n lastActivity: Date.now()\n });\n\n // Send the request\n logger.info(`Sending command to Figma: ${command}`);\n logger.debug(`Request details: ${JSON.stringify(request)}`);\n ws.send(JSON.stringify(request));\n });\n}","import { z } from \"zod\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { sendCommandToFigma, joinChannel } from \"../utils/websocket\";\n\n/**\n * Register execution tools to the MCP server\n * This module contains the generic code execution tool for running arbitrary Figma Plugin API code\n * @param server - The MCP server instance\n */\nexport function registerExecutionTools(server: McpServer): void {\n // Join channel tool - required before any other commands\n server.tool(\n \"join_channel\",\n \"Join a Figma channel to establish communication. Must be called before execute_code.\",\n {\n channel: z.string().min(1).describe(\"Channel name from Figma plugin\"),\n },\n async ({ channel }) => {\n try {\n await joinChannel(channel);\n return {\n content: [{\n type: \"text\",\n text: `Joined channel: ${channel}`\n }]\n };\n } catch (error) {\n return {\n content: [{\n type: \"text\",\n text: `Failed to join channel: ${error instanceof Error ? error.message : String(error)}`\n }]\n };\n }\n }\n );\n\n server.tool(\n \"execute_code\",\n \"Execute arbitrary Figma Plugin API code. Returns serialized result or error. Use for complex operations not covered by existing tools.\",\n {\n code: z.string().min(1).describe(\n \"JavaScript code to execute. Has access to 'figma' global object. Supports async/await. Return only JSON-serializable data.\"\n ),\n },\n async ({ code }) => {\n try {\n const result = await sendCommandToFigma(\"execute_code\", { code });\n\n // Check if execution failed\n if (result && typeof result === 'object' && 'success' in result) {\n if (!result.success) {\n return {\n content: [{\n type: \"text\",\n text: `Execution failed:\\n${result.error.type}: ${result.error.message}\\n\\nStack:\\n${result.error.stack}`\n }]\n };\n }\n\n // Success - parse result string back to display nicely\n const parsed = JSON.parse(result.result);\n return {\n content: [{\n type: \"text\",\n text: JSON.stringify(parsed, null, 2)\n }]\n };\n }\n\n // Fallback - return as-is\n return {\n content: [{\n type: \"text\",\n text: JSON.stringify(result, null, 2)\n }]\n };\n } catch (error) {\n return {\n content: [{\n type: \"text\",\n text: `Error: ${error instanceof Error ? error.message : String(error)}`\n }]\n };\n }\n }\n );\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { registerExecutionTools } from \"./execution-tools\";\n\n/**\n * Register all Figma tools to the MCP server\n * @param server - The MCP server instance\n */\nexport function registerTools(server: McpServer): void {\n registerExecutionTools(server);\n}\n"],"mappings":";;;AAQA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;;;ACRrC,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,YAAY,KAAK,KAAK,SAAO,IAAI,WAAW,WAAW,CAAC;AAC9D,IAAM,UAAU,KAAK,KAAK,SAAO,IAAI,WAAW,SAAS,CAAC;AAC1D,IAAM,eAAe,KAAK,KAAK,SAAO,IAAI,WAAW,uBAAuB,CAAC;AAGtE,IAAM,YAAY,YAAY,UAAU,MAAM,GAAG,EAAE,CAAC,IAAI;AACxD,IAAM,cAAc,UAAU,SAAS,QAAQ,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI;AACpE,IAAM,oBAAoB,eAAe,SAAS,aAAa,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI;AAGpF,IAAM,SAAS,cAAc,cAAc,QAAQ,SAAS,KAAK,SAAS,SAAS;AAGnF,IAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,aAAa;AAAA,EACb,SAAS;AACX;;;AClBO,IAAM,SAAS;AAAA,EACpB,MAAM,CAAC,YAAoB,QAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAAA,EACrE,OAAO,CAAC,YAAoB,QAAQ,OAAO,MAAM,WAAW,OAAO;AAAA,CAAI;AAAA,EACvE,MAAM,CAAC,YAAoB,QAAQ,OAAO,MAAM,UAAU,OAAO;AAAA,CAAI;AAAA,EACrE,OAAO,CAAC,YAAoB,QAAQ,OAAO,MAAM,WAAW,OAAO;AAAA,CAAI;AAAA,EACvE,KAAK,CAAC,YAAoB,QAAQ,OAAO,MAAM,SAAS,OAAO;AAAA,CAAI;AACrE;;;ACPA,OAAO,eAAe;AACtB,SAAS,MAAM,cAAc;AAM7B,IAAI,KAAuB;AAC3B,IAAI,iBAAgC;AAGpC,IAAM,kBAAkB,oBAAI,IAA4B;AAMjD,SAAS,eAAe,OAAe,aAAa;AAEzD,MAAI,MAAM,GAAG,eAAe,UAAU,MAAM;AAC1C,WAAO,KAAK,4BAA4B;AACxC;AAAA,EACF;AAGA,MAAI,MAAM,GAAG,eAAe,UAAU,YAAY;AAChD,WAAO,KAAK,4CAA4C;AACxD;AAAA,EACF;AAGA,MAAI,OAAO,GAAG,eAAe,UAAU,WAAW,GAAG,eAAe,UAAU,SAAS;AACrF,OAAG,mBAAmB;AACtB,SAAK;AAAA,EACP;AAEA,QAAM,QAAQ,cAAc,cAAc,GAAG,MAAM,IAAI,IAAI,KAAK;AAChE,SAAO,KAAK,wCAAwC,KAAK,KAAK;AAE9D,MAAI;AACF,SAAK,IAAI,UAAU,KAAK;AAGxB,UAAM,oBAAoB,WAAW,MAAM;AACzC,UAAI,MAAM,GAAG,eAAe,UAAU,YAAY;AAChD,eAAO,MAAM,+BAA+B;AAC5C,WAAG,UAAU;AAAA,MACf;AAAA,IACF,GAAG,GAAK;AAER,OAAG,GAAG,QAAQ,MAAM;AAClB,mBAAa,iBAAiB;AAC9B,aAAO,KAAK,kCAAkC;AAE9C,uBAAiB;AAAA,IACnB,CAAC;AAED,OAAG,GAAG,WAAW,CAAC,SAAc;AAC9B,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,IAAI;AAG5B,YAAI,KAAK,SAAS,mBAAmB;AACnC,gBAAM,eAAe,KAAK,QAAQ;AAClC,gBAAM,YAAY,KAAK,MAAM;AAE7B,cAAI,aAAa,gBAAgB,IAAI,SAAS,GAAG;AAC/C,kBAAM,UAAU,gBAAgB,IAAI,SAAS;AAG7C,oBAAQ,eAAe,KAAK,IAAI;AAGhC,yBAAa,QAAQ,OAAO;AAG5B,oBAAQ,UAAU,WAAW,MAAM;AACjC,kBAAI,gBAAgB,IAAI,SAAS,GAAG;AAClC,uBAAO,MAAM,WAAW,SAAS,gDAAgD;AACjF,gCAAgB,OAAO,SAAS;AAChC,wBAAQ,OAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,cACxD;AAAA,YACF,GAAG,GAAK;AAGR,mBAAO,KAAK,uBAAuB,aAAa,WAAW,KAAK,aAAa,QAAQ,OAAO,aAAa,OAAO,EAAE;AAGlH,gBAAI,aAAa,WAAW,eAAe,aAAa,aAAa,KAAK;AAMxE,qBAAO,KAAK,aAAa,aAAa,WAAW,sCAAsC;AAAA,YACzF;AAAA,UACF;AACA;AAAA,QACF;AAGA,cAAM,aAAa,KAAK;AACxB,eAAO,MAAM,qBAAqB,KAAK,UAAU,UAAU,CAAC,EAAE;AAC9D,eAAO,IAAI,eAAe,KAAK,UAAU,UAAU,CAAC;AAGpD,YACE,WAAW,MACX,gBAAgB,IAAI,WAAW,EAAE,MAChC,WAAW,WAAW,UAAa,WAAW,QAC/C;AACA,gBAAM,UAAU,gBAAgB,IAAI,WAAW,EAAE;AACjD,uBAAa,QAAQ,OAAO;AAE5B,cAAI,WAAW,OAAO;AACpB,mBAAO,MAAM,qBAAqB,WAAW,KAAK,EAAE;AACpD,oBAAQ,OAAO,IAAI,MAAM,WAAW,KAAK,CAAC;AAAA,UAC5C,OAAO;AACL,oBAAQ,QAAQ,WAAW,MAAM;AAAA,UACnC;AAEA,0BAAgB,OAAO,WAAW,EAAE;AAAA,QACtC,OAAO;AAEL,iBAAO,KAAK,+BAA+B,KAAK,UAAU,UAAU,CAAC,EAAE;AAAA,QACzE;AAAA,MACF,SAAS,OAAO;AACd,eAAO,MAAM,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,MACjG;AAAA,IACF,CAAC;AAED,OAAG,GAAG,SAAS,CAAC,UAAU;AACxB,aAAO,MAAM,iBAAiB,KAAK,EAAE;AAAA,IAEvC,CAAC;AAED,OAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AAC/B,mBAAa,iBAAiB;AAC9B,aAAO,KAAK,mDAAmD,IAAI,gBAAgB,UAAU,oBAAoB,EAAE;AACnH,WAAK;AAGL,iBAAW,CAAC,IAAI,OAAO,KAAK,gBAAgB,QAAQ,GAAG;AACrD,qBAAa,QAAQ,OAAO;AAC5B,gBAAQ,OAAO,IAAI,MAAM,+BAA+B,IAAI,KAAK,UAAU,oBAAoB,EAAE,CAAC;AAClG,wBAAgB,OAAO,EAAE;AAAA,MAC3B;AAGA,YAAM,UAAU,KAAK,IAAI,KAAO,oBAAoB,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC;AAChG,aAAO,KAAK,8BAA8B,UAAQ,GAAI,aAAa;AACnE,iBAAW,MAAM,eAAe,IAAI,GAAG,OAAO;AAAA,IAChD,CAAC;AAAA,EAEH,SAAS,OAAO;AACd,WAAO,MAAM,0CAA0C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAE/G,eAAW,MAAM,eAAe,IAAI,GAAG,iBAAiB;AAAA,EAC1D;AACF;AAOA,eAAsB,YAAY,aAAoC;AACpE,MAAI,CAAC,MAAM,GAAG,eAAe,UAAU,MAAM;AAC3C,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,MAAI;AACF,UAAM,mBAAmB,QAAQ,EAAE,SAAS,YAAY,CAAC;AACzD,qBAAiB;AACjB,WAAO,KAAK,mBAAmB,WAAW,EAAE;AAAA,EAC9C,SAAS,OAAO;AACd,WAAO,MAAM,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAChG,UAAM;AAAA,EACR;AACF;AASO,SAAS,mBACd,SACA,SAAkB,CAAC,GACnB,YAAoB,KACF;AAClB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAEtC,QAAI,CAAC,MAAM,GAAG,eAAe,UAAU,MAAM;AAC3C,qBAAe;AACf,aAAO,IAAI,MAAM,kDAAkD,CAAC;AACpE;AAAA,IACF;AAGA,UAAM,kBAAkB,YAAY;AACpC,QAAI,mBAAmB,CAAC,gBAAgB;AACtC,aAAO,IAAI,MAAM,6CAA6C,CAAC;AAC/D;AAAA,IACF;AAEA,UAAM,KAAK,OAAO;AAClB,UAAM,UAAU;AAAA,MACd;AAAA,MACA,MAAM,YAAY,SAAS,SAAS;AAAA,MACpC,GAAI,YAAY,SACZ,EAAE,SAAU,OAAe,QAAQ,IACnC,EAAE,SAAS,eAAe;AAAA,MAC9B,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,UACN,GAAI;AAAA,UACJ,WAAW;AAAA;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAGA,UAAM,UAAU,WAAW,MAAM;AAC/B,UAAI,gBAAgB,IAAI,EAAE,GAAG;AAC3B,wBAAgB,OAAO,EAAE;AACzB,eAAO,MAAM,WAAW,EAAE,6BAA6B,YAAY,GAAI,UAAU;AACjF,eAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,MAChD;AAAA,IACF,GAAG,SAAS;AAGZ,oBAAgB,IAAI,IAAI;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,KAAK,IAAI;AAAA,IACzB,CAAC;AAGD,WAAO,KAAK,6BAA6B,OAAO,EAAE;AAClD,WAAO,MAAM,oBAAoB,KAAK,UAAU,OAAO,CAAC,EAAE;AAC1D,OAAG,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,EACjC,CAAC;AACH;;;ACvPA,SAAS,SAAS;AASX,SAAS,uBAAuB,QAAyB;AAE9D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,gCAAgC;AAAA,IACtE;AAAA,IACA,OAAO,EAAE,QAAQ,MAAM;AACrB,UAAI;AACF,cAAM,YAAY,OAAO;AACzB,eAAO;AAAA,UACL,SAAS,CAAC;AAAA,YACR,MAAM;AAAA,YACN,MAAM,mBAAmB,OAAO;AAAA,UAClC,CAAC;AAAA,QACH;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC;AAAA,YACR,MAAM;AAAA,YACN,MAAM,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UACzF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,IACA,OAAO,EAAE,KAAK,MAAM;AAClB,UAAI;AACF,cAAM,SAAS,MAAM,mBAAmB,gBAAgB,EAAE,KAAK,CAAC;AAGhE,YAAI,UAAU,OAAO,WAAW,YAAY,aAAa,QAAQ;AAC/D,cAAI,CAAC,OAAO,SAAS;AACnB,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,EAAsB,OAAO,MAAM,IAAI,KAAK,OAAO,MAAM,OAAO;AAAA;AAAA;AAAA,EAAe,OAAO,MAAM,KAAK;AAAA,cACzG,CAAC;AAAA,YACH;AAAA,UACF;AAGA,gBAAM,SAAS,KAAK,MAAM,OAAO,MAAM;AACvC,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,YACtC,CAAC;AAAA,UACH;AAAA,QACF;AAGA,eAAO;AAAA,UACL,SAAS,CAAC;AAAA,YACR,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,UACtC,CAAC;AAAA,QACH;AAAA,MACF,SAAS,OAAO;AACd,eAAO;AAAA,UACL,SAAS,CAAC;AAAA,YACR,MAAM;AAAA,YACN,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UACxE,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AChFO,SAAS,cAAc,QAAyB;AACrD,yBAAuB,MAAM;AAC/B;;;ALeA,eAAe,OAAO;AACpB,MAAI;AAEF,UAAM,SAAS,IAAI,UAAU,aAAa;AAG1C,kBAAc,MAAM;AAGpB,QAAI;AACF,qBAAe;AAAA,IACjB,SAAS,OAAO;AACd,aAAO,KAAK,yCAAyC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAC7G,aAAO,KAAK,oDAAoD;AAAA,IAClE;AAGA,UAAM,YAAY,IAAI,qBAAqB;AAC3C,UAAM,OAAO,QAAQ,SAAS;AAC9B,WAAO,KAAK,kCAAkC;AAAA,EAChD,SAAS,OAAO;AACd,WAAO,MAAM,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AACxG,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAGA,KAAK,EAAE,MAAM,WAAS;AACpB,SAAO,MAAM,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AACxG,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "figma-mcp-lightweight",
3
+ "description": "Lightweight MCP server with full Figma Plugin API access",
4
+ "version": "1.0.0",
5
+ "module": "dist/talk_to_figma_mcp/server.js",
6
+ "main": "dist/talk_to_figma_mcp/server.js",
7
+ "bin": {
8
+ "figma-mcp-lightweight": "dist/talk_to_figma_mcp/server.js",
9
+ "figma-mcp-socket": "dist/socket.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "readme.md",
14
+ "LICENSE",
15
+ "TESTING.md",
16
+ "CHANGELOG.md"
17
+ ],
18
+ "type": "module",
19
+ "scripts": {
20
+ "start": "bun run dist/talk_to_figma_mcp/server.js",
21
+ "socket": "bun run dist/socket.js",
22
+ "setup": "./scripts/setup.sh",
23
+ "build": "tsup && chmod +x dist/talk_to_figma_mcp/server.js dist/socket.js",
24
+ "build:win": "tsup",
25
+ "build:watch": "tsup --watch",
26
+ "dev": "bun run build:watch",
27
+ "pack": "dxt pack",
28
+ "sync-version": "VERSION=$(jq -r '.version' package.json) && jq --arg version \"$VERSION\" '.version = $version' manifest.json > manifest.tmp && mv manifest.tmp manifest.json",
29
+ "build:dxt": "npm run sync-version && npm run build && npm run pack",
30
+ "pub:release": "bun run build && npm publish",
31
+ "configure-claude": "node scripts/configure-claude.js",
32
+ "test": "echo 'No tests configured'"
33
+ },
34
+ "devDependencies": {
35
+ "@anthropic-ai/dxt": "^0.2.0",
36
+ "@types/bun": "latest",
37
+ "bun-types": "^1.2.9",
38
+ "tsup": "^8.4.0",
39
+ "typescript": "^5.8.3"
40
+ },
41
+ "dependencies": {
42
+ "@modelcontextprotocol/sdk": "latest",
43
+ "uuid": "latest",
44
+ "ws": "latest",
45
+ "zod": "latest"
46
+ },
47
+ "keywords": [
48
+ "claude",
49
+ "figma",
50
+ "mcp",
51
+ "plugin",
52
+ "ai",
53
+ "design",
54
+ "automation"
55
+ ],
56
+ "author": "Igor Halilovic",
57
+ "license": "MIT",
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "git+https://github.com/halilc4/figma-mcp-lightweight.git"
61
+ },
62
+ "bugs": {
63
+ "url": "https://github.com/halilc4/figma-mcp-lightweight/issues"
64
+ },
65
+ "homepage": "https://github.com/halilc4/figma-mcp-lightweight#readme"
66
+ }
package/readme.md ADDED
@@ -0,0 +1,94 @@
1
+ # Figma MCP - Lightweight Full Access
2
+
3
+ A minimal MCP server providing full Figma Plugin API access through a single `execute_code` tool.
4
+
5
+ > **Fork Notice**: This is a lightweight fork of [claude-talk-to-figma-mcp](https://github.com/arinspunk/claude-talk-to-figma-mcp) by Xúlio Zé, which is based on [cursor-talk-to-figma-mcp](https://github.com/sonnylazuardi/cursor-talk-to-figma-mcp) by Sonny Lazuardi.
6
+
7
+ ## Why This Fork?
8
+
9
+ The original MCP has 40+ specialized tools. This version has **2 tools**:
10
+
11
+ | Tool | Purpose |
12
+ |------|---------|
13
+ | `join_channel` | Connect to Figma plugin |
14
+ | `execute_code` | Run any Figma Plugin API code |
15
+
16
+ ### Benefits
17
+
18
+ - **Context-efficient**: Minimal tool definitions = more context for your actual work
19
+ - **Full access**: Execute any valid Figma Plugin API code
20
+ - **No limitations**: Not restricted to predefined operations
21
+ - **Simpler maintenance**: Less code to maintain
22
+
23
+ ## Setup
24
+
25
+ ### 1. Install & Build
26
+
27
+ ```bash
28
+ git clone https://github.com/halilc4/figma-mcp-lightweight.git
29
+ cd figma-mcp-lightweight
30
+ bun install
31
+ bun run build # macOS/Linux
32
+ bun run build:win # Windows
33
+ ```
34
+
35
+ ### 2. Configure Claude Desktop
36
+
37
+ Add to your Claude Desktop config (`claude_desktop_config.json`):
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "FigmaMCP": {
43
+ "command": "bun",
44
+ "args": ["run", "/path/to/figma-mcp-lightweight/dist/talk_to_figma_mcp/server.js"]
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### 3. Setup Figma Plugin
51
+
52
+ Import `src/claude_mcp_plugin/manifest.json` in Figma:
53
+ Menu > Plugins > Development > Import plugin from manifest
54
+
55
+ ### 4. Connect
56
+
57
+ 1. Start WebSocket server: `bun socket`
58
+ 2. Open plugin in Figma, copy channel ID
59
+ 3. In Claude: "Connect to Figma channel {channel-ID}"
60
+
61
+ ## Usage
62
+
63
+ Once connected, Claude can execute any Figma Plugin API code:
64
+
65
+ ```javascript
66
+ // Create a frame
67
+ const frame = figma.createFrame();
68
+ frame.resize(400, 300);
69
+ frame.name = "My Frame";
70
+
71
+ // Access selection
72
+ const selected = figma.currentPage.selection;
73
+
74
+ // Modify nodes
75
+ node.fills = [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 } }];
76
+ ```
77
+
78
+ See [Figma Plugin API docs](https://www.figma.com/plugin-docs/) for full reference.
79
+
80
+ ## Architecture
81
+
82
+ ```
83
+ Claude Desktop <-> MCP Server <-> WebSocket Server <-> Figma Plugin
84
+ ```
85
+
86
+ ## License
87
+
88
+ MIT License - see [LICENSE](LICENSE)
89
+
90
+ ## Credits
91
+
92
+ - [Sonny Lazuardi](https://github.com/sonnylazuardi) - Original implementation
93
+ - [Xúlio Zé](https://github.com/arinspunk) - Claude adaptation
94
+ - [Igor Halilovic](https://github.com/halilc4) - Lightweight fork