# Figma MCP Plugin

A **Model Context Protocol (MCP) server with 72 tools** that connects AI assistants to Figma for creating and modifying designs programmatically.

Works with **Claude Code, Cursor, Windsurf, Aider** and any MCP-compatible tool. Also provides an **HTTP REST API** for non-MCP tools like Python scripts, curl, or custom integrations.

## Credits

Built from scratch, inspired by and with gratitude to:

- **[claude-talk-to-figma-mcp](https://github.com/arinspunk/claude-talk-to-figma-mcp)** by [arinspunk](https://github.com/arinspunk) - WebSocket bridge architecture concept
- **[figma-console-mcp](https://github.com/southleft/figma-console-mcp)** by [southleft](https://github.com/southleft) - Canvas editing tools and Figma Plugin API patterns

Their open-source work made this project possible. Thank you!

## Architecture

```
AI Assistant → MCP Server (stdio) ──→ WebSocket Broker (port 3055) ──→ Figma Plugin → Figma API
                                           ↑
              HTTP Bridge (port 3056) ─────┘  (for non-MCP tools)
```

### Channel System

The plugin uses a channel-based connection system for reliable, multi-session support:

1. Plugin connects to broker and generates a unique channel ID (e.g., `figma-a3f7k`)
2. User copies the channel ID from the plugin UI
3. AI tool joins the channel using `join_channel`
4. All messages are routed within the channel - multiple sessions can run simultaneously

## Quick Start

```bash
# 1. Install & Build
npm install
npm run build

# 2. Start the broker
npm run start:broker

# 3. Load plugin in Figma Desktop
#    Plugins → Development → Import plugin from manifest → select manifest.json

# 4. Click "Connect" in plugin UI → Copy the channel ID

# 5. Tell your AI tool to join the channel
#    "Join Figma channel figma-xxxxx"
```

### Using the HTTP Bridge (for non-MCP tools)

```bash
# Start both broker + HTTP bridge
# Windows: double-click start-http-bridge.bat
# Or manually:
npm run start:broker    # terminal 1
npm run start:http      # terminal 2

# Join a channel
curl -X POST http://localhost:3056/join-channel \
  -H "Content-Type: application/json" \
  -d '{"channel":"figma-xxxxx"}'

# Send commands
curl -X POST http://localhost:3056/command \
  -H "Content-Type: application/json" \
  -d '{"command":"get_document_info"}'

curl -X POST http://localhost:3056/command \
  -H "Content-Type: application/json" \
  -d '{"command":"create_rectangle","params":{"x":100,"y":100,"width":200,"height":100,"fillColor":{"r":0.2,"g":0.4,"b":0.9,"a":1}}}'
```

## Available Tools (72)

| Category | Count | Tools |
|----------|-------|-------|
| **Connection** | 1 | `join_channel` |
| **Document & Pages** | 6 | `get_document_info`, `get_selection`, `get_pages`, `set_current_page`, `create_page`, `delete_page` |
| **Creating Nodes** | 12 | `create_frame`, `create_rectangle`, `create_ellipse`, `create_line`, `create_polygon`, `create_star`, `create_text`, `create_component`, `create_component_set`, `create_instance`, `create_boolean_operation`, `create_section` |
| **Reading Nodes** | 8 | `get_node_by_id`, `get_nodes_in_frame`, `get_local_components`, `get_local_styles`, `get_local_variables`, `get_variable_collections`, `scan_nodes`, `export_node` |
| **Transform** | 8 | `move_node`, `resize_node`, `set_rotation`, `rename_node`, `clone_node`, `delete_node`, `set_visible`, `set_locked` |
| **Fill & Stroke** | 6 | `set_fill_color`, `set_gradient_fill`, `set_image_fill`, `set_stroke_color`, `set_stroke_weight`, `remove_fill` |
| **Effects** | 6 | `set_opacity`, `set_corner_radius`, `set_blend_mode`, `add_drop_shadow`, `add_inner_shadow`, `add_blur` |
| **Text** | 8 | `set_font_family`, `set_font_size`, `set_font_weight`, `set_font_style`, `set_text_content`, `set_text_align`, `set_line_height`, `set_letter_spacing` |
| **Auto-Layout** | 8 | `set_auto_layout`, `remove_auto_layout`, `set_layout_sizing`, `set_layout_padding`, `set_layout_spacing`, `set_layout_align`, `insert_in_auto_layout`, `set_layout_wrap` |
| **Organizing** | 5 | `group_nodes`, `ungroup_nodes`, `flatten_node`, `set_parent`, `set_constraints` |
| **Variables** | 3 | `create_variable`, `set_variable_value`, `bind_variable` |
| **Navigation** | 1 | `zoom_to_node` |

## MCP Configuration

### Claude Code / Cursor (project-level)

Already configured in `.claude/mcp.json`. Just reload your editor after building.

For Claude Code, you also need **user-level permissions** - see [Permissions Guide](docs/PERMISSIONS-GUIDE.md).

### Claude Desktop (requires absolute path)

Edit `claude_desktop_config.json`:
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Mac**: `~/Library/Application Support/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "figma": {
      "command": "node",
      "args": ["/full/path/to/figma-mcp-plugin/dist/server/index.js"]
    }
  }
}
```

Get your absolute path by running in the project directory:
- **Windows**: `echo %cd%\dist\server\index.js`
- **Mac/Linux**: `echo $(pwd)/dist/server/index.js`

### Other Tools

See [MCP Connection Guide](docs/MCP-CONNECTION-GUIDE.md) for Cursor, Aider, Windsurf, etc.

## Project Structure

```
server/
  index.ts          - MCP server with 72 tool definitions
  broker.ts         - Channel-aware WebSocket message broker
  websocket.ts      - WebSocket client with channel support
  http-bridge.ts    - HTTP REST API (port 3056)
  types.ts          - Shared TypeScript types
plugin/
  code.ts           - Figma plugin with 71 command handlers
  ui.html           - Plugin UI (Connect, channel ID, Copy button)
docs/               - Setup guides and documentation
```

## Build Commands

| Command | Description |
|---------|-------------|
| `npm run build` | Build everything |
| `npm run build:server` | Build MCP server only |
| `npm run build:plugin` | Build Figma plugin only |
| `npm run start:broker` | Start WebSocket broker (port 3055) |
| `npm run start:http` | Start HTTP bridge (port 3056) |
| `npm run watch:plugin` | Watch mode for plugin development |
| `npm run watch:server` | Watch mode for server development |

## Cross-Platform

All config files use **relative paths** - works on Windows, macOS, and Linux without modification.

## Troubleshooting

### Plugin won't connect
- Make sure the broker is running first: `npm run start:broker`
- Click "Connect" in the plugin UI

### Tools don't appear in AI assistant
- Rebuild: `npm run build`
- For Claude Code: add `mcp__figma-custom` to `~/.claude/settings.json` permissions
- Reload your editor

### Port 3055 already in use
```bash
# Windows
netstat -ano | findstr :3055
taskkill /PID <number> /F

# Mac/Linux
lsof -ti:3055 | xargs kill -9
```

## Documentation

- [Quick Start](docs/QUICK-START.md) - 5-minute setup
- [Permissions Guide](docs/PERMISSIONS-GUIDE.md) - User-level permissions for Claude Code
- [MCP Connection Guide](docs/MCP-CONNECTION-GUIDE.md) - Configure any AI tool
- [For Other AI Tools](docs/FOR-OTHER-AI-TOOLS.md) - Integration guide for AI assistants

## License

ISC
