FreeMCP for Figma
Enables AI coding agents to read and write a user's Figma file through the Figma Plugin API, offline and privately, without API tokens or rate limits.
README
FreeMCP for Figma — Universal MCP Bridge
A local MCP bridge that lets AI coding agents read and write a user's Figma file through the Figma Plugin API (native, in-desktop) instead of the public REST API. No official-MCP rate limits, no Figma API token, and no design data ever leaves the machine.
- Offline & private — everything runs on
127.0.0.1; a startup guard blocks any outbound HTTP from the server process. - Native design-system writes — create frames/shapes/text, author components, insert library component instances, bind variables & styles, configure Auto Layout, constraints and effects — all executed inside Figma via the Plugin API.
- Design-to-code — generate React (Tailwind), Vue 3, or HTML/CSS from a selected node.
- Batched & undoable —
batch_runapplies 100+ ops in one consented request without blocking the Figma UI;undoreverts the last write(s).
Transport: the bridge is HTTP long-poll (
POST /register,GET /next,POST /result) on127.0.0.1:3700, not WebSocket. The Figma plugin sandbox has noWebSocketglobal — onlyfetch— so the plugin long-polls a loopback HTTP server hosted by the local MCP process.
Architecture
┌──────────────┐ MCP/STDIO ┌────────────────────────────┐ HTTP long-poll ┌──────────────────┐
│ AI client │ ◀──────────▶ │ local MCP server (Node) │ ◀───────────────▶ │ Figma plugin │
│ Claude/VS │ JSON-RPC │ packages/server │ /register │ packages/plugin │
│ Cursor/Ollama│ │ 127.0.0.1:3700 │ /next /result │ (Plugin API) │
└──────────────┘ └────────────────────────────┘ └──────────────────┘
Every MCP tool is a thin dispatch. The server validates arguments (Zod), translates the call into a typed bridge frame, and forwards it over loopback HTTP to the Figma plugin, which executes the actual Plugin API call and returns the result. Deep-dive: docs/ARCHITECTURE.md.
Quickstart (< 5 min)
1. Install
npm install
npm run build:plugin
2. Load the plugin in Figma
Figma → Plugins → Development → Import plugin from manifest… → select
packages/plugin/manifest.json. The plugin auto-connects to
http://127.0.0.1:3700 (a small status panel shows connection state).
3. Start the server (own terminal)
npm run start:server
Expect: FreeMCP Figma bridge on http://127.0.0.1:3700. Open the plugin in Figma to connect.
4. Point your AI client at the MCP server (STDIO)
{
"mcpServers": {
"figma-free-mcp": {
"command": "node",
"args": ["C:/path/to/repo/packages/server/dist/index.js"]
}
}
}
Claude Desktop: Settings → Developer → Edit Config. VS Code / Cursor: .mcp.json.
You can also run the built bundle directly: node packages/server/dist/index.js.
Using it
| Intent | What the agent does |
|---|---|
| "Describe this design" | get_design_context / get_selection → full node tree, fills, typography, effects, Auto Layout |
| "What tokens exist?" | get_variables / get_styles → local variables, collections, text/effect/grid styles |
| "Create a card component" | create_frame (Auto Layout) + create_text children → native Figma nodes |
| "Insert our primary button" | create_component_instance by key → reuses the design system (local or library) |
| "Make 100 variants" | batch_run → many ops in one consented request, no UI block |
| "Undo last change" | undo → reverts creates/duplicates/fills/layout |
| "Generate React for this" | to_react / to_vue / to_html |
Consent: the first write always prompts for consent — every write tool
requires confirm: true in its params (FR-504). Nothing is written silently.
Tool reference
Every input is validated by the Zod schema in
packages/contracts/src/tool-schemas.ts (single source of truth) before
dispatch (FR-404). The per-tool contract lives in
specs/001-freemcp-figma-bridge/contracts/tools.md.
Read
| Tool | Purpose | Key params |
|---|---|---|
get_selection |
Current selection → DesignContext[] (optionally a node by id) |
nodeId?, maxDepth? |
get_design_context |
Deep hierarchical context for a node subtree | nodeId, maxDepth? |
get_metadata |
Lightweight tree: ids, types, names, bounds | nodeId? |
get_screenshot |
Raster/SVG export of a node (base64) | nodeId, format (png|jpg|svg), scale? |
get_variables |
Local variables + collections + mode values | — |
get_styles |
Local text/effect/grid styles | — |
get_components |
Local components + component sets with importable keys | — |
get_page_children |
Top-level nodes on the current page (id/name/type/bounds) | — |
Write (all require confirm: true)
| Tool | Purpose | Key params |
|---|---|---|
create_frame |
Frame at position/size, optional Auto Layout + sizing | x, y, width, height, layout?, parent?, primaryAxisSizing?, counterAxisSizing?, stroke?, strokeWeight?, cornerRadius? |
create_shape |
Rectangle / ellipse / polygon | type, x, y, width, height, fill?, stroke?, layoutAlign?, layoutGrow?, parent? |
create_text |
Text node with font props + text polish | characters, x, y, fontFamily?, fontSize?, fontWeight?, color?, lineHeight?, letterSpacing?, textCase?, textDecoration?, textAutoResize?, layoutAlign?, layoutGrow?, parent? |
apply_fill |
Apply solid/gradient fill to a node (image fills deferred — need a Figma imageHash) | nodeId, fill |
set_auto_layout |
Auto Layout direction, padding, spacing, alignment | nodeId, mode, itemSpacing?, padding?, primaryAxisAlignItems?, counterAxisAlignItems? |
duplicate_node |
Clone preserving properties + children | nodeId, offset? |
delete_node |
Delete a node (not undoable) | nodeId |
create_component_instance |
Insert an instance by key (local or published library) | componentKey, x?, y?, parent?, overrides? |
create_component |
Author a reusable ComponentNode (design-system library) |
x, y, width, height, layout?, parent? |
set_variable |
Bind a variable to a node property; "" unbinds |
nodeId, property (fill|strokes|cornerRadius|opacity), variableId |
apply_style |
Apply a text/effect/grid style; "" clears |
nodeId, kind (text|effect|grid), styleId |
set_constraints |
Layout constraints (align/grow + frame sizing) | nodeId, layoutAlign?, layoutGrow?, primaryAxisSizing?, counterAxisSizing? |
set_effect |
Set arbitrary effect array (e.g. DROP_SHADOW); [] clears |
nodeId, effects |
Batch
| Tool | Purpose | Key params |
|---|---|---|
batch_run |
Run many write ops in one consented batch (100+ < 3s) | ops: { tool, params }[], confirm |
Code generation
| Tool | Purpose | Key params |
|---|---|---|
to_react |
JSX + Tailwind classes | nodeId, includeStyles? |
to_vue |
Vue 3 Composition API SFC | nodeId |
to_html |
Semantic HTML + CSS | nodeId |
Utility
| Tool | Purpose | Key params |
|---|---|---|
undo |
Revert last executed write op(s) | count? (1–100) |
ping |
Health check; returns bridge connectivity + latency | — |
Error envelope (FR-405): every tool result is { ok: true, data } or
{ ok: false, error: { code, message, suggestion } }, serialized as a JSON
string in the MCP text content. suggestion carries a fixable hint.
Security & privacy model
- No tokens, no cloud (FR-501/502/503) — the server binds only to
127.0.0.1; a startup guard (installOfflineGuard) monkey-patches globalfetchto reject any non-loopback request, and anOpLogrecords every dispatch so an operator can assert only local calls happened. - Consent before writes (FR-504) — write tools require
confirm: true; the plugin never re-prompts for a single consented batch. - Sandbox-bound — design data only ever crosses loopback HTTP between the MCP process and the Figma plugin. Nothing is transmitted off-machine.
Configuration
| Env | Default | Purpose |
|---|---|---|
FREEMCP_PORT |
3700 |
Server port. Note: the plugin's target port is compiled in (DEFAULT_PORT in packages/contracts/src/messages.ts), so changing this also requires rebuilding the plugin. |
Troubleshooting
| Symptom | Fix |
|---|---|
NO_PLUGIN: No plugin connected |
Open the plugin in Figma (Plugins → Development) so it registers with the server. |
Connection refused |
Start the server first, then load the plugin. |
| Port conflict | Set FREEMCP_PORT on the server and rebuild the plugin with the matching constant. |
| Text ops fail | Text nodes need a loaded font — the plugin calls figma.loadFontAsync automatically before setting text. |
Development
npm install # workspaces: contracts, server, plugin
npm test # 32 Vitest unit tests (schemas, codec, bridge, undo, codegen)
npm run typecheck # tsc --noEmit (strict)
npm run build:server # esbuild bundle → packages/server/dist/index.js (npx-runnable)
npm run build:plugin # esbuild bundle → packages/plugin/dist/main.js
npm run start:server # tsx dev run of the MCP server + bridge
Coding agents in this repo additionally have codebase-memory MCP, an LSP provider, and obscura browser tools connected for Spec Kit workflow runs — see docs/TOOLCHAIN.md for the wiring and reproduction. See docs/DEVELOPMENT.md for the developer guide (add a tool, build pipeline, sandbox constraints) and docs/ARCHITECTURE.md for the deep technical reference.
Repository layout
packages/
├── contracts/ # Shared, types-only: DesignContext, bridge frames, Zod tool schemas (source of truth)
├── server/ # MCP server (STDIO) + HTTP long-poll bridge host + codegen + command stack
└── plugin/ # Figma plugin: outbound long-poll client + Plugin API read/write/design-system logic
specs/001-freemcp-figma-bridge/ # Spec Kit artifacts: spec, plan, research, data-model,
# contracts (protocol + tools), quickstart, tasks
Docs index
| Doc | What it covers |
|---|---|
docs/ARCHITECTURE.md |
Components, request lifecycle, long-poll internals, undo, codegen pipeline, security |
docs/DEVELOPMENT.md |
Adding a tool, build/test workflow, Figma sandbox constraints, conventions |
docs/TOOLCHAIN.md |
Connected agent tooling for Spec Kit runs: codebase-memory MCP, LSP provider, obscura, routing skill — setup + verification |
skills.md |
Production-ready Figma design workflow for the bridge MCP: audit → tokens → components → screens, Auto Layout rules, verification gates, bridge limits |
specs/001-freemcp-figma-bridge/spec.md |
Feature specification (FRs, user stories, success criteria) |
specs/001-freemcp-figma-bridge/contracts/protocol.md |
HTTP long-poll bridge protocol contract |
specs/001-freemcp-figma-bridge/contracts/tools.md |
MCP tool surface contract |
specs/001-freemcp-figma-bridge/quickstart.md |
Manual end-to-end validation scenarios |
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。