An MCP server that lets an AI agent (Claude Code, or any MCP client) write real designs onto a Figma canvas — auto-layout frames, live text, color variables — from a small typed spec.
Figma's REST API cannot create design nodes; only Plugin API code running inside Figma can. So Design Bridge is three pieces: an MCP server the agent talks to, a local WebSocket, and a Figma plugin that does the actual building.
agent → MCP server → ws://127.0.0.1:3055 → plugin UI iframe → plugin sandbox → canvas
Illustration of the flow — a render_screen spec becomes real auto-layout on the canvas.
You need two halves running: the server (once) and the plugin (per Figma file). It takes about two minutes. Requires Node 18+ and the Figma desktop app (the plugin can't run in the browser).
0. Preflight (optional)
npx -y figma-design-bridge --doctorChecks your Node version and that the port is free, then prints the exact next steps. Run it first if anything won't connect.
1. Register the server with your MCP client
No clone required — run it straight from npm:
claude mcp add figma-bridge -- npx -y figma-design-bridge
claude mcp list # should show figma-bridgeOther MCP clients (Cursor, Windsurf, Claude Desktop)
Any MCP client works — point it at the same command. Add this to the client's
MCP config (.mcp.json, .cursor/mcp.json, or Claude Desktop's
claude_desktop_config.json):
{
"mcpServers": {
"figma-bridge": {
"command": "npx",
"args": ["-y", "figma-design-bridge"]
}
}
}Or run from a clone
git clone https://github.com/evidence-codes/figma-design-bridge
cd figma-design-bridge
npm install
claude mcp add figma-bridge -- node "$(pwd)/src/server.js"2. Load the plugin in Figma
- From Figma Community: (link once published) — click Install, then run it
from
Plugins → Design Bridge. - From source: grab
design-bridge-plugin.zip(or use theplugin/folder from a clone). Figma desktop → open a Design file → right-click the canvas →Plugins → Development → Import plugin from manifest…→ pickplugin/manifest.json, then runPlugins → Development → Design Bridge.
A small panel appears with a green dot when it finds the server. Leave that panel open — closing it kills the socket.
3. Check the wiring
Ask your agent to call figma_status. You should get back your file and page
name.
| Tool | Does |
|---|---|
figma_status |
Connection check. Start here when something's wrong. |
render_screen |
Builds a screen from a spec. Same key replaces in place. |
create_variables |
Creates a colour variable collection. |
list_screens |
Everything this bridge has rendered, with keys. |
delete_screen |
Removes one by key. |
Paste this to your agent once everything's connected:
Use render_screen with this spec:
{ "key": "smoke-test", "name": "Smoke test", "width": 600, "background": "#FBFBF9", "root": { "type": "frame", "layout": "col", "gap": 16, "pad": 32, "width": "fill", "children": [ { "type": "text", "content": "Extracted scope", "font": "Inter", "weight": "Semi Bold", "size": 24, "color": "#14201C" }, { "type": "frame", "layout": "row", "gap": 12, "pad": [12, 16], "fill": "#E7F0EB", "radius": 8, "width": "fill", "children": [ { "type": "text", "content": "Stated", "font": "Inter", "weight": "Medium", "size": 12, "color": "#1F5F45" }, { "type": "text", "content": "Five-page marketing site", "font": "Inter", "size": 14, "color": "#14201C" } ] } ] } }
A frame should appear on your canvas with real auto layout. Run it twice — the second run replaces the first rather than stacking a duplicate.
src/schema.js is the contract. Three node types:
- frame — the only container.
layout(row/col/none),gap,pad,align,justify,fill,stroke,radius,width(hug/fill/fixed). - text —
content,font,weight,size,lineHeight,color. - rect —
w,h,fill,stroke,radius.
Everything maps one-to-one onto a Figma concept. That's deliberate: no CSS to
reverse-engineer, no layout engine, no guessing what a div was supposed to be.
Keep it small — every field you add is a field the plugin must handle and the
model must learn.
The server listens on 127.0.0.1:3055. To change it, pass --port <n> (or set
FIGMA_BRIDGE_PORT) and type the same port into the plugin panel's Port
field — both halves must agree. npx figma-design-bridge --doctor will tell you
if the default port is already taken.
Run npx figma-design-bridge --help for all flags.
Fonts must exist in Figma. The plugin loads every font in a spec before
building. If Figma doesn't have it, the render fails with a clear message rather
than silently substituting. weight is Figma's style name — "Regular",
"Medium", "Semi Bold" — not a CSS number.
width: "fill" needs an auto-layout parent. Inside a layout: "none" frame
it's ignored. This fails soft rather than throwing, so a screen that looks
subtly wrong is usually this.
One socket at a time. A second Figma window connecting drops the first. If renders start going to the wrong file, that's why.
Keys are how you iterate. Re-rendering "review-screen" replaces it. Change
the key and you get a second copy — useful for comparing variants, annoying by
accident.
Components and variants (figma.createComponent, combineAsVariants), binding
variables to node properties rather than just creating them, images, and
multi-page targeting. Those are where this stops being a toy — contributions
welcome.
cursor-talk-to-figma-mcp
is an open-source implementation of the same architecture that goes considerably
further. Design Bridge trades breadth for a small, strict IR: one spec renders a
whole screen, every field maps to exactly one Figma concept, and there's no
layout engine to fight.
MIT — see LICENSE.

