This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
TalkToFigma Desktop is an Electron-based desktop application that bridges Figma with AI tools (Cursor, Claude Code, VS Code) using the Model Context Protocol (MCP). It enables AI assistants to read and manipulate Figma designs through a structured protocol.
Current Version: 2.0.0 Bundle ID: com.grabtaxi.klever Architecture: Electron + React + TypeScript
This project was migrated from Kotlin Compose Desktop to Electron in January 2026 (v2.0.0). The Electron version maintains feature parity while adding cross-platform support (macOS, Windows, Linux) and better developer experience with modern JavaScript tooling.
npm start
# Launches Electron app with hot reload
# Main process: src/main.ts
# Renderer process: src/renderer.tsx
npm run package # Package for current platform
npm run make # Create distributable installers
npm run lint # Run ESLint on TypeScript files
The project uses Vite with multiple entry points configured in forge.config.ts:
- Main process:
src/main.ts→vite.main.config.ts - Preload script:
src/preload.ts→vite.preload.config.ts - Renderer: React app →
vite.renderer.config.ts - MCP stdio server:
src/main/server/mcp-stdio-server.ts→vite.stdio.config.ts
MCP Clients (Cursor, Claude Code, etc.)
│ spawn independent process per client
▼
stdio MCP Servers (one per client)
│ WebSocket connection (ws://127.0.0.1:3055)
▼
WebSocket Server (Electron App - Port 3055)
│ Channel-based routing
▼
Figma Plugin
- Each MCP client (Cursor, Claude Code) spawns its own independent stdio server process
- stdio servers are NOT managed by the Electron app - they're spawned by the MCP clients themselves
- All stdio servers connect to the same WebSocket server (port 3055) in the Electron app
- This allows multiple AI tools to work with Figma simultaneously
The MCP stdio server is automatically installed to platform-specific locations:
- macOS:
~/Library/Application Support/TalkToFigma/mcp-server.cjs - Windows:
%APPDATA%\TalkToFigma\mcp-server.cjs
Installation happens on first app launch via src/main/utils/stdio-installer.ts. These paths are sandbox-safe for App Store distribution.
- The WebSocket server uses a channel system to isolate different Figma files
- Each MCP client must call
join_channeltool with a Figma file ID before sending commands - Messages are routed only to clients and plugins in the same channel
- This prevents command collisions when multiple Figma files are open
- User sends command in MCP client (e.g., Cursor)
- MCP client → stdin → MCP stdio server process
- stdio server → WebSocket (port 3055) → Electron app
- Electron app → WebSocket → Figma plugin
- Figma executes command and returns result
- Result flows back through the same chain via message ID matching
- Creates BrowserWindow (900x650, sidebar layout)
- Initializes singleton services:
TalkToFigmaService- Unified service orchestratorTalkToFigmaServerManager- WebSocket server lifecycle managerTalkToFigmaTray- System tray with quick actions
- Handles OAuth authentication flow
- Installs stdio server on startup
- Manages IPC communication with renderer
- React 19 application (
src/renderer.tsx→src/App.tsx) - Three-page sidebar interface:
- Terminal: Real-time log viewer with log streaming
- Settings: MCP client configuration manager (generates config snippets)
- Help: Documentation and troubleshooting
- Real-time status updates via IPC events
- Dark/light theme support
- Security bridge with context isolation
- Exposes typed API surface via
window.electron:server.*- Server control (start/stop/status)figma.*- Figma operationsauth.*- OAuth authenticationmcp.*- MCP configuration utilitieslog.*- Log streaming
All services in src/main/server/services/ extend BaseFigmaService:
document-service.ts- Document operations (get nodes, search)creation-service.ts- Node creation (frames, rectangles, text, etc.)channel-service.ts- Channel management (join/leave/list)rest-api-service.ts- Figma REST API operations (comments, reactions)
Services use adapter pattern to decouple from UI layer - see main.ts for adapter implementations.
- Listens on port 3055
- Tracks two types of connections:
- MCP clients (stdio servers spawned by Cursor/Claude Code)
- Figma plugins (running in Figma)
- Channel-based message routing
- Message types:
join,message,progress_update - Connection count displayed in UI
- Standalone process (not part of Electron app)
- Communicates via stdin/stdout using JSON-RPC
- Contains embedded WebSocket client (
src/main/server/shared/websocket-client.ts) - Supports 50 MCP tools (defined in
src/main/server/tools.ts) - Tool and prompt registries shared across all instances
- Full OAuth 2.0 PKCE flow
- Local callback server on port 8888
- Token storage in electron-store
- Automatic refresh with 5-minute buffer before expiration
- Tokens used for Figma REST API access
- Authenticated requests to Figma API
- Operations: comments, reactions, file metadata, user info
- Token refresh handling
- Real-time bidirectional communication via WebSocket
- Plugin runs in Figma and executes design operations
- Commands sent from MCP clients → Electron → Plugin
- Results returned via request/response matching with timeouts
src/main/- Main process codeserver/- MCP servers, WebSocket server, service layerfigma/- OAuth and REST API integrationutils/- Utilities (logger, store, installer)
src/lib/- Shared librariesmcp/- MCP client configuration detection and generation
src/components/- React componentsui/- Reusable UI components (shadcn/ui)mcp/- MCP-specific components
src/pages/- Page components (Terminal, Settings, Help)src/shared/- Code shared between main and renderertypes/- TypeScript type definitionsconstants.ts- Shared constants (ports, channels, store keys)
TypeScript path alias @/* maps to ./src/* (configured in tsconfig.json).
Example:
import { logger } from '@/main/utils/logger';
import { ServerStatus } from '@/shared/types/server';
The codebase recently migrated from SSE transport to stdio transport (Jan 2026). Key changes:
- ❌ Removed HTTP server (port 3056)
- ❌ Removed SSE session management
- ✅ Added stdio-based spawning by clients
- ✅ Multi-client support with independent processes
- ✅ Better isolation and resource management
Legacy code: SSE server code in src/main/server/TalkToFigmaMcpServer.ts is marked @deprecated but kept for backward compatibility.
All IPC handlers are centralized in src/main/ipc-handlers.ts. The pattern:
- Renderer calls via
window.electron.server.startAll() - Preload exposes via
contextBridge - Main process handles via
ipcMain.handle(IPC_CHANNELS.SERVER_START_ALL) - Service method called via adapter
- Status emitted back to renderer via
ipcMain.emit(IPC_CHANNELS.SERVER_STATUS_CHANGED)
These services MUST be singletons (initialized once in main.ts):
TalkToFigmaService- Orchestrator for all operationsTalkToFigmaServerManager- WebSocket server managerTalkToFigmaTray- System tray manager
Access via static getInstance() method.
- Tokens stored in electron-store with key
STORE_KEYS.FIGMA_AUTH - Never log tokens or expose in UI
- Auto-refresh happens 5 minutes before expiration
- Check
FigmaOAuthService.isAuthenticated()before REST API calls
When adding new MCP tools:
- Define tool schema in
src/main/server/tools.tsusing Zod - Add handler logic in appropriate service (
src/main/server/services/) - Register in tool registry (
src/main/server/shared/tool-registry.ts) - Tool automatically available to all stdio server instances
Tool naming convention: {category}_{action} (e.g., document_get_node, node_create_frame)
Before any MCP client can send commands to Figma:
- Client must call
join_channeltool with Figma file ID - File ID format:
{file_key}:{page_id}:{view_id} - All subsequent commands routed to that channel
- Multiple clients can join same channel for collaboration
- 3055: WebSocket server (managed by Electron app, MUST be running)
- 8888: OAuth callback server (temporary, only during auth flow)
The project uses multiple Vite configs:
vite.main.config.ts- Main process bundlingvite.renderer.config.ts- Renderer process (React)vite.preload.config.ts- Preload scriptvite.stdio.config.ts- MCP stdio server (standalone executable)
The stdio server is bundled separately and copied to Application Support directory on installation.
Bundle ID Configuration:
- Bundle ID is hardcoded in
forge.config.tsascom.grabtaxi.klever - IMPORTANT: Never modify bundle ID via scripts or build processes
- This differs from the previous Kotlin version which used temporary bundle ID swapping for Grab Taxi builds
- TypeScript strict mode enabled
- Shared types in
src/shared/types/ - IPC message types defined in
src/shared/types/ipc.ts - All IPC channels use constants from
src/shared/constants.ts
- Electron app must be running for stdio servers to connect
- Check if port 3055 is available:
lsof -i :3055(macOS) ornetstat -ano | findstr :3055(Windows) - Look for startup errors in app logs (Terminal page)
- Check installation path exists:
ls -la ~/Library/Application\ Support/TalkToFigma/mcp-server.cjs - Reinstall by deleting the file and restarting the app
- Installation logs in main process console
- Ensure
join_channelwas called with correct file ID - Verify Figma plugin is running in the target file
- Check WebSocket connection count in UI (Settings page)
- Review connection logs in Terminal page
- Clear Vite cache:
rm -rf .vite - Clear node_modules:
rm -rf node_modules && npm install - Check for TypeScript errors:
npx tsc --noEmit
- Electron 39 - Desktop app framework
- React 19 - UI framework
- TypeScript 4.5 - Type safety
- Vite 5 - Build tool and bundler
- @modelcontextprotocol/sdk - MCP protocol implementation
- WebSocket (ws) - Real-time communication
- shadcn/ui + Radix UI - Component library
- Tailwind CSS 4 - Styling
- electron-store - Persistent storage
- winston - Structured logging
- Aptabase - Privacy-friendly analytics