# Figma Ui Mcp

> Bidirectional Figma MCP — AI draws UI on Figma canvas, reads designs back

- **Type:** MCP server
- **Install:** `agentstack add mcp-tranhoaihung-figma-ui-mcp`
- **Verified:** Pending review
- **Seller:** [TranHoaiHung](https://agentstack.voostack.com/s/tranhoaihung)
- **Installs:** 0
- **Latest version:** 1.5.1
- **License:** MIT
- **Upstream author:** [TranHoaiHung](https://github.com/TranHoaiHung)
- **Source:** https://github.com/TranHoaiHung/figma-ui-mcp

## Install

```sh
agentstack add mcp-tranhoaihung-figma-ui-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# figma-ui-mcp

  

  
  
  
  

  Claude Code to Figma · Antigravity to Figma · Cursor to Figma · Any MCP IDE to Figma

  ✅ Tested: Claude Code, Antigravity &nbsp;|&nbsp; 🔧 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)

```bash
# 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`

```json
{
  "mcpServers": {
    "figma": {
      "command": "npx",
      "args": ["-y", "figma-ui-mcp"]
    }
  }
}
```

Cursor

Edit `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):

```json
{
  "mcpServers": {
    "figma": {
      "command": "npx",
      "args": ["-y", "figma-ui-mcp"]
    }
  }
}
```

VS Code / GitHub Copilot

Edit `.vscode/mcp.json` (project) or add to `settings.json` (global):

```json
{
  "mcp": {
    "servers": {
      "figma": {
        "command": "npx",
        "args": ["-y", "figma-ui-mcp"]
      }
    }
  }
}
```

Windsurf

Edit `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "figma": {
      "command": "npx",
      "args": ["-y", "figma-ui-mcp"]
    }
  }
}
```

Antigravity (Google)

1. Open **"..." dropdown** at the top of the agent panel
2. Click **"Manage MCP Servers"** → **"View raw config"**
3. Add to `mcp_config.json`:

```json
{
  "mcpServers": {
    "figma": {
      "command": "npx",
      "args": ["-y", "figma-ui-mcp"]
    }
  }
}
```

From source (any client)

```bash
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](https://github.com/TranHoaiHung/figma-ui-mcp/raw/main/plugin.zip)** — no git clone needed

1. Download and **unzip** `plugin.zip` anywhere on your machine
2. Open **Figma Desktop** (required — web app cannot access localhost)
3. Go to **Plugins → Development → Import plugin from manifest...**
4. Select `manifest.json` from the unzipped folder
5. 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

```bash
# 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:
1. Call `figma_docs` to load the API reference and design rules
2. Call `figma_read get_page_nodes` to understand the current canvas
3. Call `figma_write` to create the design on your Figma canvas
4. Call `figma_read screenshot` to 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:

```js
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 (WRITE_OPS + READ_OPS) |
| 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` + individual `topLeftRadius` / `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](https://github.com/TranHoaiHung)
- **Source:** [TranHoaiHung/figma-ui-mcp](https://github.com/TranHoaiHung/figma-ui-mcp)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Versions

- **1.5.1** — security scan: pending review — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-tranhoaihung-figma-ui-mcp
- Seller: https://agentstack.voostack.com/s/tranhoaihung
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
