Install
$ agentstack add mcp-tranhoaihung-figma-ui-mcp Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
About
figma-ui-mcp
Claude Code to Figma · Antigravity to Figma · Cursor to Figma · Any MCP IDE to Figma
✅ Tested: Claude Code, Antigravity | 🔧 Compatible: Cursor, VS Code, Windsurf, Zed (any MCP stdio client)
Bidirectional Figma MCP bridge — let AI assistants (Claude Code, Cursor, Windsurf, Antigravity, VS Code Copilot, or any MCP-compatible IDE) draw UI directly on Figma canvas and read existing designs back as structured data, screenshots, or code-ready tokens. No Figma API key needed — works entirely over localhost.
> Requires Figma Desktop — the plugin communicates with the MCP server over localhost HTTP polling. Figma's web app does not allow localhost network access, so Figma Desktop is required.
Claude ──figma_write──▶ MCP Server ──HTTP (localhost:38451)──▶ Figma Plugin ──▶ Figma Document
Claude ◀─figma_read──── MCP Server ◀──HTTP (localhost:38451)── Figma Plugin ◀── Figma Document
How the localhost bridge works
The MCP server starts a small HTTP server bound to localhost:38451. The Figma plugin (running inside Figma Desktop) uses long polling — the server holds requests up to 8s until work arrives, flushing immediately when new ops are queued (near-realtime latency Claude Code (CLI)
# Project scope (default)
claude mcp add figma-ui-mcp -- npx figma-ui-mcp
# Global scope (all projects)
claude mcp add --scope user figma-ui-mcp -- npx figma-ui-mcp
Claude Desktop
Edit config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"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 / GitHub Copilot
Edit .vscode/mcp.json (project) or add to settings.json (global):
{
"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"]
}
}
}
Antigravity (Google)
- Open "..." dropdown at the top of the agent panel
- Click "Manage MCP Servers" → "View raw config"
- Add to
mcp_config.json:
{
"mcpServers": {
"figma": {
"command": "npx",
"args": ["-y", "figma-ui-mcp"]
}
}
}
From source (any client)
git clone https://github.com/TranHoaiHung/figma-ui-mcp
cd figma-ui-mcp
npm install
# Then point your MCP client to: node /path/to/figma-ui-mcp/server/index.js
> ⚠️ IMPORTANT: After adding the MCP server, you MUST restart your IDE / AI client (quit and reopen). The MCP server only loads on startup — simply saving the config file is not enough. This applies to Claude Code, Cursor, VS Code, Windsurf, and Antigravity.
Step 2 — Install the Figma plugin
⬇ Download plugin.zip — no git clone needed
- Download and unzip
plugin.zipanywhere on your machine - Open Figma Desktop (required — web app cannot access localhost)
- Go to Plugins → Development → Import plugin from manifest...
- Select
manifest.jsonfrom the unzipped folder - Run Plugins → Development → Figma UI MCP Bridge
The plugin UI shows a green dot when the MCP server is connected.
Updating to a newer version
# Step 1 — get the new version + plugin path
npx figma-ui-mcp@latest --version
# figma-ui-mcp v2.5.12 — plugin: /.../.npm/_npx/.../figma-ui-mcp/plugin
# Step 2 — restart Claude / your IDE so the MCP server reloads
# Step 3 — re-link the Figma plugin (manual, one-time per update)
# Figma Desktop → Plugins → Development → Manage plugins in development
# Remove old "Figma UI MCP Bridge" → "+" → Import plugin from manifest...
# Select manifest.json from the plugin path printed in Step 1
# Step 4 — verify
# Ask your AI: "figma_status"
# pluginVersion in the response should match the npm version above
> The Figma plugin does not auto-update — re-linking (Step 3) is required whenever the plugin changes.
Step 3 — Connect AI to Figma
Tell your AI assistant to connect:
"Connect to figma-ui-mcp"
The AI will call figma_status and confirm:
✅ Connected — File: "My Project", Page: "Page 1", Plugin v2.5.5
> If you see "Plugin not connected", make sure the Figma plugin is running (Step 2).
Step 4 — Start designing with prompts
Once connected, just describe what you want in natural language:
"Use figma-ui-mcp to draw a login screen for mobile"
The AI will automatically:
- Call
figma_docsto load the API reference and design rules - Call
figma_read get_page_nodesto understand the current canvas - Call
figma_writeto create the design on your Figma canvas - Call
figma_read screenshotto verify the result
Prompt examples
| Prompt | What happens | |--------|-------------| | "Draw a mobile login screen with social login buttons" | Creates a 390×844 frame with email/password inputs, Apple/Google buttons | | "Read the selected frame and describe the design" | Extracts colors, typography, spacing from your selection | | "Take a screenshot of the current frame" | Returns an inline image the AI can analyze | | "Create a dark theme dashboard with KPI cards" | Draws a full dashboard layout with charts and stats | | "Design an e-commerce product card" | Creates a product card with image, price, rating, CTA | | "Draw a settings page with toggle switches" | Creates grouped settings with icons and toggles |
Tips for better results
- Be specific about style:
"dark theme","glassmorphism","minimal white"gives the AI clear direction - Mention platform:
"mobile"(390×844),"tablet"(768×1024),"desktop"(1440×900) - Iterate: After the first draw, say
"fix the spacing"or"make the buttons bigger"— the AI reads and modifies existing nodes - Use selection: Select a frame in Figma and ask
"improve this design"— the AI reads your selection first - Multi-screen flows:
"Now draw the signup screen next to the login screen"— the AI positions frames side by side
Workflow summary
You: "Connect to figma-ui-mcp"
AI: ✅ Connected to Figma
You: "Draw a mobile onboarding screen with 3 steps"
AI: [calls figma_docs → figma_write → figma_read screenshot]
AI: ✅ Done — here's what I created: [inline screenshot]
You: "The title text is not centered"
AI: [calls figma_read get_selection → figma_write modify → screenshot]
AI: ✅ Fixed — text is now centered
You: "Now draw the next onboarding screen beside it"
AI: [reads page_nodes to find position → draws at x+440]
AI: ✅ Done — 2 screens side by side
figma_status — check connection (always call first)
figma_docs — load API reference (call before drawing)
figma_write — draw / modify UI on canvas
figma_read — extract design data, screenshots, SVG
Usage Examples
Draw a screen
Ask Claude: "Draw a dark dashboard with a sidebar, header, and 4 KPI cards"
Claude calls figma_write with code like:
await figma.createPage({ name: "Dashboard" });
await figma.setPage({ name: "Dashboard" });
const root = await figma.create({
type: "FRAME", name: "Dashboard",
x: 0, y: 0, width: 1440, height: 900,
fill: "#0f172a",
});
const sidebar = await figma.create({
type: "FRAME", name: "Sidebar",
parentId: root.id,
x: 0, y: 0, width: 240, height: 900,
fill: "#1e293b", stroke: "#334155", strokeWeight: 1,
});
await figma.create({
type: "TEXT", name: "App Name",
parentId: sidebar.id,
x: 20, y: 24, content: "My App",
fontSize: 16, fontWeight: "SemiBold", fill: "#f8fafc",
});
// ... continue
Read a design
Ask Claude: "Read my selected frame and convert it to Tailwind CSS"
Claude calls figma_read with operation: "get_selection", receives the full node tree, then generates corresponding code.
Screenshot a frame
figma_read → operation: "screenshot" → nodeId: "123:456"
Returns a base64 PNG Claude can analyze and describe.
Architecture
figma-ui-mcp/
├── server/
│ ├── index.js MCP server (stdio transport)
│ ├── bridge-server.js HTTP bridge on localhost:38451 (long-poll, multi-session)
│ ├── code-executor.js VM sandbox — safe JS execution + 7-lib icon fetcher
│ ├── tool-definitions.js MCP tool schemas (figma_status / _write / _read / _docs)
│ └── api-docs.js API reference text (served to AI via figma_docs)
├── src/plugin/ Plugin source (concat-built into plugin/code.js)
│ ├── utils.js, svg-path-helpers.js, paint-and-effects.js, read-helpers.js
│ ├── handlers-write.js, handlers-read.js, handlers-read-detail.js,
│ │ handlers-library.js, handlers-tokens.js, handlers-write-ops.js
│ └── main.js
└── plugin/
├── manifest.json Figma plugin manifest
├── code.js Plugin main (auto-generated — 3600+ LOC)
└── ui.html Plugin UI — long-poll client + status dot
Security
| Layer | Protection | |-------|-----------| | VM sandbox | vm.runInContext() — blocks require, process, fs, fetch | | Localhost only | Bridge binds localhost:38451, never exposed to network | | Operation allowlist | 56 predefined operations accepted (WRITEOPS + READOPS) | | Timeout | 30s VM execution + 60-90s per plugin operation (adaptive by op type) | | Body size limit | 5 MB max per request | | Session isolation | Multi-instance sessions scoped by Figma file ID |
Available Write Operations (figma_write)
Core CRUD
| Operation | Description | |-----------|-------------| | figma.create({ type, ... }) | Create FRAME / RECTANGLE / ELLIPSE / LINE / TEXT / SVG / VECTOR / IMAGE | | figma.modify({ id, ... }) | Update node properties (fill, size, text, layout, etc.) | | figma.delete({ id }) | Remove a single node | | figma.delete({ ids: [...] }) | Batch delete multiple nodes in one call | | figma.query({ type?, name?, id? }) | Find nodes by type, name, or ID | | figma.append({ parentId, childId }) | Move node into parent |
create / modify — advanced props available on any node:
| Prop | Example | Notes | |------|---------|-------| | fill (solid) | "#6C5CE7" or "#6C5CE780" (8-digit hex with alpha) or "rgba(108,92,231,0.5)" | Alpha auto-extracted into paint opacity | | fill (gradient) | { type: "LINEAR_GRADIENT", angle: 135, stops: [{ pos: 0, color: "#7C3AED" }, { pos: 1, color: "#EC4899" }] } | Also RADIAL_GRADIENT | | stroke, strokeWeight, strokeOpacity | Same hex/rgba rules | | | cornerRadius uniform | 12 | All 4 corners | | Individual corners | topLeftRadius: 20, topRightRadius: 20, bottomLeftRadius: 0, bottomRightRadius: 0 | Rounded top sheet pattern | | effects array | [{ type: "DROP_SHADOW", color: "#00000026", offset: {x:0,y:8}, radius: 24, spread: 0 }] | Types: DROP_SHADOW, INNER_SHADOW, LAYER_BLUR, BACKGROUND_BLUR | | TEXT center | textAlign: "CENTER" with explicit width | Auto-infers textAutoResize: "NONE" so centering works | | VECTOR path | d: "M 150 7 A 143 143 0 1 1 29.26 226.62" | SVG A arc auto-converted to cubic Bézier; commas accepted |
Page Management
| Operation | Description | |-----------|-------------| | figma.status() | Current Figma context info | | figma.listPages() | List all pages | | figma.setPage({ name }) | Switch active page | | figma.createPage({ name }) | Add a new page |
Node Operations
| Operation | Description | |-----------|-------------| | figma.clone({ id, x?, y?, parentId? }) | Duplicate a node with optional repositioning | | figma.group({ nodeIds, name? }) | Group multiple nodes | | figma.ungroup({ id }) | Ungroup a GROUP/FRAME | | figma.flatten({ id }) | Flatten/merge vectors into single path | | figma.resize({ id, width, height }) | Resize any node | | figma.set_selection({ ids }) | Programmatically select nodes | | figma.set_viewport({ nodeId?, x?, y?, zoom? }) | Navigate viewport | | figma.batch({ operations }) | Execute up to 50 ops in one call (10-25x faster) |
Components
| Operation | Description | |-----------|-------------| | figma.listComponents() | List all components in document | | figma.createComponent({ nodeId, name? }) | Convert FRAME/GROUP → reusable Component | | figma.instantiate({ componentId/Name, parentId, x, y }) | Create component instance | | figma.instantiate({ ..., overrides: { "LayerName": { text, fill, fontSize, visible, ... } } }) | Instantiate with per-layer overrides |
Design Tokens & Styles
| Operation | Description | |-----------|-------------| | figma.setupDesignTokens({ colors, numbers, fontSizes, fonts, textStyles, modes }) | Bootstrap complete token system (idempotent) — colors + spacing + typography + text styles + multi-mode in 1 call | | figma.createVariableCollection({ name }) | Create variable collection ("Colors", "Spacing") | | figma.createVariable({ name, collectionId, resolvedType, value }) | Create COLOR/FLOAT/STRING/BOOLEAN variable | | figma.addVariableMode({ collectionId, modeName }) | Add mode (e.g. "dark", "compact") | | figma.renameVariableMode({ collectionId, modeId, newName }) | Rename a mode | | figma.removeVariableMode({ collectionId, modeId }) | Remove a mode | | figma.setVariableValue({ variableId/Name, modeId/Name, value }) | Set per-mode value | | figma.modifyVariable({ variableName, value }) | Change variable value — all bound nodes update | | figma.applyVariable({ nodeId, field, variableId/Name }) | Bind variable to a node property | | figma.applyTextStyle({ nodeId, styleName }) | Apply a local text style to a TEXT node by name (auto-loads font) | | figma.setFrameVariableMode({ nodeId, collectionId, modeName }) | Pin frame to a variable mode (Light/Dark, Compact/Large) | | figma.clearFrameVariableMode({ nodeId, collectionId }) | Reset frame to document default mode | | figma.createPaintStyle({ name, color }) | Create reusable paint style | | figma.createTextStyle({ name, fontFamily, fontSize, ... }) | Create reusable text style (manual — prefer setupDesignTokens.textStyles) | | figma.ensure_library() | Create/get Design Library frame | | figma.get_library_tokens() | Read library color + text tokens |
applyVariable supported fields — bind FLOAT/COLOR/STRING/BOOLEAN variables to:
- Color:
fill,stroke - Geometry:
opacity,width,height,strokeWeight - Corner radius:
cornerRadius+ individualtopLeftRadius/topRightRadius/bottomLeftRadius/bottomRightRadius - Spacing (auto-layout):
paddingTop,paddingBottom,paddingLeft,paddingRight,itemSpacing,counterAxisSpacing - Typography (TEXT nodes):
fontSize,letterSpacing,lineHeight,paragraphSpacing,paragraphIndent - Font swap (STRING):
fontFamily,fontStyle,characters— swap Inter → SF Pro via 1 variable - Visibility (BOOLEAN):
visible
Image & Icon Helpers (server-side)
| Operation | Description | |-----------|-------------| | figma.loadImage(url, opts) | Download image → place on canvas | | figma.loadIcon(name, opts) | Fetch SVG icon with 7-library fallback (iOS-filled first) | | figma.loadIconIn(name, opts) | Icon inside centered circle background |
loadIcon fallback priority (filled-first, iOS style preferred): Ionicons (iOS filled) → Fluent UI (Win11 filled) → Bootstrap (filled) → Phosphor (filled) → Tabler Filled (4,500+) → Tabler Outline → Lucide (outline fallback)
Free replacemen
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: TranHoaiHung
- Source: TranHoaiHung/figma-ui-mcp
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v1.5.1 Imported from the upstream source.