Talk to Figma MCP uses a three-component pipeline architecture to enable seamless communication between AI agents (like Claude Code or Cursor) and Figma designs. This architecture ensures reliable, real-time interaction with Figma while maintaining the MCP protocol standard.
The MCP server (src/talk_to_figma_mcp/server.ts) is the protocol adapter between AI agents and Figma. It implements the Model Context Protocol using @modelcontextprotocol/sdk.
The MCP server uses stdio transport to communicate with AI agents:
server.ts
// All logs go to stderr to avoid interfering with MCP protocolconst logger = { info: (message: string) => process.stderr.write(`[INFO] ${message}\n`), debug: (message: string) => process.stderr.write(`[DEBUG] ${message}\n`), error: (message: string) => process.stderr.write(`[ERROR] ${message}\n`),};// Start server with stdio transportconst transport = new StdioServerTransport();await server.connect(transport);
stdout is reserved for MCP protocol messages. All logging must go to stderr to prevent protocol corruption.
The WebSocket relay (src/socket.ts) is a lightweight Bun-powered router that enables channel-based message isolation between multiple MCP servers and Figma plugins.
Clients must join a channel before sending messages:
socket.ts
if (data.type === "join") { const channelName = data.channel; // Create channel if it doesn't exist if (!channels.has(channelName)) { channels.set(channelName, new Set()); } // Add client to channel const channelClients = channels.get(channelName)!; channelClients.add(ws); console.log(`Client joined channel "${channelName}" (${channelClients.size} total clients)`);}
Messages are broadcast only within the same channel:
socket.ts
if (data.type === "message") { const channelClients = channels.get(channelName); // Broadcast to all OTHER clients (not the sender) channelClients.forEach((client) => { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(JSON.stringify({ type: "broadcast", message: data.message, sender: "peer", channel: channelName })); } });}
The relay runs on port 3055 by default (configurable via PORT env):
socket.ts
const server = Bun.serve({ port: 3055, websocket: { open: handleConnection, message: handleMessage, close: handleClose }});console.log(`WebSocket server running on port ${server.port}`);
Start the relay with bun socket before connecting MCP servers or Figma plugins.
The plugin main thread handles 30+ commands via a dispatcher pattern. It processes requests like create_frame, set_text_content, export_node_as_image, etc.No build step required — code.js is written directly as the runtime artifact.
ui.html - Plugin UI
The plugin UI provides WebSocket connection management, allowing users to:
Connect to the relay server
Join channels
Monitor connection status
manifest.json - Permissions
Declares required permissions:
dynamic-page access for document manipulation
localhost network access for WebSocket connections