Figma UI MCP Bridge
Skill by ara.so — Design Skills collection
Bidirectional MCP bridge between AI assistants and Figma Desktop. Let Claude Code, Cursor, Windsurf, VS Code Copilot, or any MCP-compatible IDE draw UI directly on Figma canvas via JavaScript and read existing designs back as structured data, screenshots, or code-ready tokens. Works entirely over localhost — no Figma API key required.
Requires Figma Desktop (web app cannot access localhost).
Architecture
AI Agent ─figma_write─▶ MCP Server ─HTTP (localhost:38451)─▶ Figma Plugin ─▶ Figma Document
AI Agent ◀figma_read──── MCP Server ◀HTTP (localhost:38451)─ Figma Plugin ◀─ Figma Document
The MCP server starts an HTTP server on localhost:38451. The Figma plugin uses long polling (8s hold, <100ms latency). Multi-instance support — multiple Figma files can connect simultaneously via sessionId.
Installation
Step 1: Add MCP Server
Claude Code (CLI):
# Project scope
claude mcp add figma-ui-mcp -- npx figma-ui-mcp
# Global scope
claude mcp add --scope user figma-ui-mcp -- npx figma-ui-mcp
Claude Desktop:
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"figma": {
"command": "npx",
"args": ["-y", "figma-ui-mcp"]
}
}
}
Cursor:
Edit .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):
{
"mcpServers": {
"figma": {
"command": "npx",
"args": ["-y", "figma-ui-mcp"]
}
}
}
VS Code / Copilot:
Edit .vscode/mcp.json:
{
"mcp": {
"servers": {
"figma": {
"command": "npx",
"args": ["-y", "figma-ui-mcp"]
}
}
}
}
Windsurf:
Edit ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"figma": {
"command": "npx",
"args": ["-y", "figma-ui-mcp"]
}
}
}
⚠️ MUST restart IDE/AI client after adding MCP server (quit and reopen — saving config is not enough).
Step 2: Install Figma Plugin
- Download
plugin.zipfrom https://github.com/TranHoaiHung/figma-ui-mcp/raw/main/plugin.zip - Unzip anywhere on your machine
- Open Figma Desktop (required)
- Plugins → Development → Import plugin from manifest...
- Select
manifest.jsonfrom unzipped folder - Run Plugins → Development → Figma UI MCP Bridge
Green dot = connected. Orange = server not reachable.
Step 3: Verify Connection
// AI will call this automatically when you say "connect to Figma"
figma_status()
// Returns: { status: "ok", fileName, pageName, pluginVersion, sessions: [...] }
Core MCP Tools
figma_status
Check connection status and list active sessions.
figma_status()
// Response:
// {
// status: "ok",
// fileName: "My Project",
// pageName: "Page 1",
// pluginVersion: "2.5.12",
// sessions: [
// { sessionId: "abc123", fileName: "My Project", pageName: "Page 1" }
// ]
// }
figma_docs
Get full API reference and examples. Call once at session start to load capabilities.
figma_docs()
// Returns: markdown reference with all operations, params, examples
figma_rules
Generate design system rule sheet — color tokens, typography styles, variables (all modes), component catalog. Equivalent to official Figma MCP's create_design_system_rules. Call once per file.
figma_rules({ sessionId: "abc123" }) // optional sessionId
// Returns: markdown rule sheet with all design tokens
figma_write
Execute JavaScript operations on Figma canvas. Takes operation (string) and params (object).
Single operation:
figma_write({
operation: "create",
params: {
type: "FRAME",
name: "Login Screen",
width: 390,
height: 844,
fill: "#FFFFFF",
children: [
{
type: "TEXT",
name: "Title",
characters: "Welcome Back",
fontSize: 32,
fontFamily: "Inter",
fontWeight: 700,
fill: "#000000",
x: 40,
y: 100
}
]
}
})
Batch operations:
figma_write({
operations: [
{
operation: "create",
params: { type: "FRAME", name: "Container", width: 800, height: 600 }
},
{
operation: "modify",
params: { id: "result[0]", fill: "#F5F5F5" }
}
]
})
Use result[0], result[1] to reference previous operation results.
figma_read
Read data from Figma. Takes operation and params.
Get page structure:
figma_read({
operation: "get_page_nodes",
params: {}
})
// Returns: { nodes: [...], totalNodes: 42 }
Get selection:
figma_read({
operation: "get_selection",
params: {}
})
// Returns: [{ id, name, type, x, y, width, height, ... }]
Take screenshot:
figma_read({
operation: "screenshot",
params: { nodeId: "123:456", scale: 2 }
})
// Returns: { base64: "data:image/png;base64,..." }
Get design context (AI-optimized):
figma_read({
operation: "get_design_context",
params: { nodeId: "123:456" }
})
// Returns: flex layout, token-resolved colors, typography with style names,
// component instances with variant properties — best for code generation
Get component map:
figma_read({
operation: "get_component_map",
params: { frameId: "123:456" }
})
// Returns: { instances: [{ componentSetName, variantLabel, properties, suggestedImport }] }
Get CSS:
figma_read({
operation: "get_css",
params: { nodeId: "123:456" }
})
// Returns: { css: "display: flex; flex-direction: column; ..." }
Common Operations
Create Nodes
Frame with auto-layout:
figma_write({
operation: "create",
params: {
type: "FRAME",
name: "Card",
width: 320,
height: 240,
fill: "#FFFFFF",
cornerRadius: 16,
layoutMode: "VERTICAL",
paddingTop: 24,
paddingRight: 24,
paddingBottom: 24,
paddingLeft: 24,
itemSpacing: 16,
effects: [
{
type: "DROP_SHADOW",
color: "rgba(0,0,0,0.1)",
offsetX: 0,
offsetY: 4,
blur: 12
}
]
}
})
Text with typography:
figma_write({
operation: "create",
params: {
type: "TEXT",
name: "Heading",
characters: "Design System",
fontSize: 48,
fontFamily: "Inter",
fontWeight: 700,
lineHeight: { value: 120, unit: "PERCENT" },
letterSpacing: { value: -2, unit: "PERCENT" },
fill: "#1A1A1A"
}
})
Rectangle with gradient:
figma_write({
operation: "create",
params: {
type: "RECTANGLE",
name: "Gradient BG",
width: 800,
height: 600,
fill: {
type: "LINEAR_GRADIENT",
angle: 135,
stops: [
{ position: 0, color: "#667EEA" },
{ position: 1, color: "#764BA2" }
]
}
}
})
Icon from library:
figma_write({
operation: "createIcon",
params: {
name: "home",
library: "ionicons", // ionicons | fluent | bootstrap | phosphor | tabler-filled | tabler-outline | lucide
size: 24,
fill: "#000000",
x: 100,
y: 100
}
})
Component instance with overrides:
figma_write({
operation: "instantiate",
params: {
componentKey: "abc123",
overrides: {
"ButtonLabel": { text: "Submit", fill: "#FFFFFF" },
"Icon": { visible: false }
}
}
})
Modify Nodes
Change properties:
figma_write({
operation: "modify",
params: {
id: "123:456",
fill: "#FF5733",
width: 400,
opacity: 0.8
}
})
Apply design tokens:
figma_write({
operation: "applyVariable",
params: {
nodeId: "123:456",
field: "fills", // fills | strokes | width | height | cornerRadius | paddingTop | etc.
variableName: "color/primary"
}
})
Apply text style:
figma_write({
operation: "applyTextStyle",
params: {
nodeId: "123:456",
styleName: "Heading/H1"
}
})
Setup Design Tokens
Color tokens (all modes):
figma_write({
operation: "setupDesignTokens",
params: {
colors: {
"color/primary": {
Light: "#667EEA",
Dark: "#A5B4FC"
},
"color/background": {
Light: "#FFFFFF",
Dark: "#1A1A1A"
}
}
}
})
Typography tokens:
figma_write({
operation: "setupDesignTokens",
params: {
fontSizes: {
"size/xs": { Compact: 12, Comfortable: 14, Large: 16 },
"size/sm": { Compact: 14, Comfortable: 16, Large: 18 },
"size/md": { Compact: 16, Comfortable: 18, Large: 20 }
},
fonts: {
"font/primary": { Compact: "Inter", Comfortable: "Inter", Large: "SF Pro" }
},
textStyles: [
{
name: "Heading/H1",
fontFamily: "font/primary",
fontSize: "size/md",
fontWeight: 700,
lineHeightPercent: 120
}
]
}
})
Prototyping
Add click interaction:
figma_write({
operation: "setReactions",
params: {
nodeId: "123:456",
reactions: [
{
trigger: "ON_CLICK", // ON_CLICK | ON_HOVER | ON_PRESS | ON_DRAG
action: "NAVIGATE", // NAVIGATE | OVERLAY | SWAP | SCROLL_TO | CLOSE
destinationId: "789:012",
transition: "SMART_ANIMATE", // SMART_ANIMATE | DISSOLVE | SLIDE_IN | PUSH | etc.
duration: 300,
easing: "EASE_OUT"
}
]
}
})
Set scroll behavior:
figma_write({
operation: "setScrollBehavior",
params: {
nodeId: "123:456",
overflow: "VERTICAL" // HORIZONTAL | VERTICAL | BOTH | NONE
}
})
Component Variants
Set variant properties:
figma_write({
operation: "setComponentProperties",
params: {
instanceId: "123:456",
properties: {
"State": "Primary",
"Size": "Large"
}
}
})
Swap component:
figma_write({
operation: "swapComponent",
params: {
instanceId: "123:456",
newComponentKey: "def789"
}
})
Read Operations Reference
| Operation | Purpose | Key Params |
|---|---|---|
get_page_nodes |
List all top-level nodes | { maxDepth } |
get_selection |
Get selected nodes | {} |
get_node_detail |
Full node data with resolved variables/styles | { nodeId } |
get_design_context |
AI-optimized payload for code generation | { nodeId } |
get_component_map |
List component instances with variants | { frameId } |
get_unmapped_components |
Find components missing code mapping | { frameId } |
get_css |
Generate CSS from node | { nodeId } |
screenshot |
Capture PNG | { nodeId, scale } |
get_variables |
All variables (all modes) | {} |
get_styles |
All color/text/effect styles | {} |
Multi-Instance Sessions
When multiple Figma files are open, each plugin instance has a unique sessionId. Target specific files:
// List all sessions
figma_status()
// { sessions: [{ sessionId: "abc123", fileName: "App Design" }, ...] }
// Write to specific session
figma_write({
operation: "create",
params: { type: "FRAME", name: "Test" },
sessionId: "abc123"
})
// Read from specific session
figma_read({
operation: "get_page_nodes",
params: {},
sessionId: "abc123"
})
Omit sessionId to use the first connected session.
Common Patterns
Design System Bootstrap
// Step 1: Generate rule sheet
figma_rules()
// Step 2: Setup color tokens
figma_write({
operation: "setupDesignTokens",
params: {
colors: {
"color/primary": { Light: "#667EEA", Dark: "#A5B4FC" },
"color/text": { Light: "#1A1A1A", Dark: "#F5F5F5" }
}
}
})
// Step 3: Setup typography
figma_write({
operation: "setupDesignTokens",
params: {
fontSizes: {
"size/h1": { Compact: 32, Comfortable: 40, Large: 48 }
},
textStyles: [
{
name: "Heading/H1",
fontSize: "size/h1",
fontWeight: 700
}
]
}
})
Design-to-Code Workflow
// Step 1: Get design context
const context = await figma_read({
operation: "get_design_context",
params: { nodeId: "123:456" }
})
// Returns: flex layout, resolved color tokens, typography with style names
// Step 2: Get component map
const components = await figma_read({
operation: "get_component_map",
params: { frameId: "123:456" }
})
// Returns: { instances: [{ componentSetName: "Button", variantLabel: "State=Primary", suggestedImport: "@/components/Button" }] }
// Step 3: Get CSS
const css = await figma_read({
operation: "get_css",
params: { nodeId: "123:456" }
})
// Returns: { css: "display: flex; ..." }
Batch Create + Reference
figma_write({
operations: [
// Create container
{
operation: "create",
params: {
type: "FRAME",
name: "Container",
width: 800,
height: 600,
layoutMode: "VERTICAL",
itemSpacing: 24
}
},
// Create child, insert into container
{
operation: "create",
params: {
type: "RECTANGLE",
name: "Header",
width: 800,
height: 80,
fill: "#667EEA",
parentId: "result[0]" // Reference first operation's result
}
}
]
})
Troubleshooting
Plugin shows orange dot
- Cause: MCP server not running or wrong port
- Fix: Restart IDE/AI client (MCP server loads on startup). Check server logs for port conflicts.
"Plugin not connected" error
- Cause: Plugin not running in Figma Desktop
- Fix: Figma Desktop → Plugins → Development → Figma UI MCP Bridge. Must be Figma Desktop (web app cannot access localhost).
Operations fail silently
- Cause: Invalid node ID or operation params
- Fix: Call
figma_read get_page_nodesfirst to get valid node IDs. Checkfigma_docsfor param schema.
Font not found
- Cause: Font not installed on system
- Fix: Use
fontFamily: "Inter"(ships with Figma) or install custom fonts locally.
Multi-instance targeting wrong file
- Cause: No
sessionIdspecified with multiple files open - Fix: Call
figma_statusto list sessions, then passsessionIdtofigma_write/figma_read.
Variable binding fails
- Cause: Variable doesn't exist or wrong type
- Fix: Call
figma_read get_variablesfirst. EnsureFLOATvariables for dimensions,COLORfor fills.
Best Practices
- Always call
figma_docsat session start to load API reference into AI context - Call
figma_rulesonce per file to get design system tokens before generating code - Use
get_design_contextfor code generation — single call returns all layout/color/typography data - Use
get_component_mapfor import statements — returnssuggestedImportpaths - Batch operations when possible — reduces round-trips (single
figma_writewithoperationsarray) - Reference previous results with
result[0],result[1]in batch operations - Use design tokens (
applyVariable) instead of hardcoded colors for maintainability - Check
figma_statusfirst to verify connection and list sessions - Use
sessionIdwhen multiple Figma files are open to avoid ambiguity - Take screenshots to verify AI-generated designs match intent
Version Updates
# Get latest version + plugin path
npx figma-ui-mcp@latest --version
# Re-link Figma plugin (manual step required)
# Figma Desktop → Plugins → Development → Manage plugins in development
# Remove old → Import from manifest → Select new plugin path
Plugin does not auto-update — must re-link after npm update.