
Figsnap Mcp
io.github.arimunandar由 GitHub 账号 arimunandar 发布v0.2.0更新于 Oct 24, 2026
The Figma file you have open, as MCP tools. Extract, edit, and keep a saved set.
概览
本地 MCP 服务器,把你正在打开的 Figma 文件变成工具:助手可读取图层、导出代码和图片,并在允许编辑时修改画布。
- 可以做什么
- 读取当前打开的 Figma 文件:选区、页面图层树、组件库、样式与变量,按类型或名称查找节点,也可通过选区、页面、组件库和按 id 的单个节点等资源直接引用。将节点导出为 PNG 图片、React、HTML 或 CSS,或按 Figma 实际持有的属性输出 iOS 规格。通过写入类工具修改画布,这些工具在插件中开启编辑或守护进程以允许编辑启动前一律被拒绝。管理收藏的组件集合:列出、保存、取消保存、移动、清空,以及新建、重命名或删除文件夹。检查并安装随包提供的技能文件,无需打开 Figma 即可回答。
- 适用场景
- 适合让编码助手基于 Figma 桌面版中打开的设计工作:读取当前选区、把图层转成 React、HTML、CSS 或 PNG,或进行受控的画布修改。也适合希望使用无账号、无中继的纯本地桥接的设计师和开发者。
- 运行要求
- 以本地 npm 包形式由 MCP 客户端启动,另需保持一个守护进程运行,并在 Figma 桌面版中打开 Figsnap MCP 插件。守护进程默认监听本机回环端口 3058;客户端从用户主目录下的 agent-token 文件读取守护进程令牌。可用 FIGSNAP_MCP_TOKEN 和 FIGSNAP_MCP_URL 覆盖令牌与地址。仅限桌面端。
安装
在 SourceWeft 中
- 打开 控制台中的 Figsnap Mcp,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
Figsnap MCP
The Figma file you have open, as MCP tools — and nothing else.
[Install in VS Code] [Install in Cursor]
Those install the MCP server. It answers nothing until the daemon is running and the Figma plugin is open — see Getting it running, which is five steps and takes about two minutes.
A Figma plugin, a local daemon, and an MCP server. Your coding agent reads the design, extracts a node as PNG, React, HTML or CSS, edits the canvas when you allow it, and shares a saved set of components with you. No relay, no account, no network beyond loopback.
Three processes and one rule: figma.* exists only while the plugin is open, so
the plugin holds the connection and everything else asks questions down it.
What this is not
This repo is a derivative of Figsnap, cut down to one job. Figsnap
ships four products from one package.json — the plugin, a Cloudflare Worker
relay with accounts, an ACP chat client, and this MCP server. If all you want is
MCP tools and a saved set, you had to clone all of it, build a 4,200-line panel
you would never open, and load a plugin whose manifest whitelists a Worker you
would never sign in to.
So: no ACP client, no harness discovery, no session store, no relay, no accounts, no chat, no API browser, no code-export UI. What is left is the extraction engine, the 41 tools, and a panel with three panes.
Figsnap is not modified by any of this, and the two can run side by side — this
daemon is on port 3058 with its token in ~/.figsnap-mcp/agent-token, where
Figsnap's is on 3056 with its token in ~/.figsnap/agent-token.
One caution on that manifest: Figma validates allowedDomains and refuses an IP
literal — Invalid value for allowedDomains. 'ws://127.0.0.1:3058' must be a valid URL — and a manifest it refuses is a manifest it does not load, so the
plugin quietly keeps running the last good one. Name localhost, and check the
console after any change here, because nothing else reports it.
They are kept apart in Figma too. clientStorage is keyed by plugin id, so the
manifest carries an id of its own rather than Figsnap's REPLACE_ON_PUBLISH, and
every key this plugin writes is prefixed figsnap-mcp: on top of that. Sharing
either one means sharing the stored daemon address, and a panel that inherits
ws://localhost:3056 is then blocked by its own manifest's CSP — with a console
error the designer never sees. The panel also refuses any stored or typed address
outside the port its manifest allows, and drops the token that came with it.
Install
Two commands come with it:
You can skip the install and let npx fetch it, which is what an MCP client
config usually does. The Figma plugin ships in the package too: after a global
install its manifest is at
Or clone the repository and npm install && npm run build, which is the same
thing with the sources beside it.
Getting it running
Five steps, once.
1. Build it.
From a clone instead: npm install && npm run build.
2. Start the daemon. Leave it running; it is the only thing that talks to Figma.
From a clone: npm run daemon. figsnap-mcp-daemon --help lists the rest.
It prints its address and its token:
3. Load the plugin. In Figma desktop: Plugins → Development → Import
plugin from manifest, pick the package's manifest.json (npm root -g finds
it, or it is in the repository root), then run Figsnap MCP.
4. Pair the panel. Copy the token line from step 2 into the panel's
Connect pane and press Connect. The dot turns green and the Address
reads ws://localhost:3058/panel.
This is the only place a token is ever typed. It is stored in Figma's own per-user storage, so the panel reconnects itself every time from now on.
5. Register the MCP server.
Restart the client, then claude mcp list shows figsnap-mcp · ✔ Connected.
From a clone, name the file instead: -- node /path/to/FigsnapMCP/agent/mcp-stdio.mjs.
Check the whole chain:
panelConnected: true is the line that matters — it means Figma is on the
other end.
The token
An MCP client never needs it. This is the part that surprises people:
claude mcp add takes no token, no environment variable, no config. The MCP
server reads the daemon's own file on the way past.
Only two things use the token, and only one of them is you:
Where it comes from. The first time the daemon starts it makes one — 24
random bytes, base64url — and writes it to ~/.figsnap-mcp/agent-token with
mode 600. Every later start reads that same file back, so the token is stable
and the panel is not re-paired every morning.
How to see it again without restarting anything:
Why there is one at all. The daemon listens on a loopback port, and any web
page you happen to visit can open a socket to localhost. Two things stop it:
the Origin header, checked on upgrade, which a browser cannot forge; and this
token, because a browser WebSocket cannot set headers. Only GET /health is
reachable without it — so the panel can tell you the daemon is running before it
has been paired.
If it leaks, rotate it:
That writes a fresh one and invalidates the old. Re-paste it in Connect; MCP clients pick the new one up on their own, because they read the file.
To use one of your own — a fixed token in a script, say — set
FIGSNAP_MCP_TOKEN and the daemon uses it instead of the file. Set the same
variable for the MCP client if it cannot read your home directory.
FIGSNAP_MCP_URL moves the address the client dials.
Wiring up an MCP client
For a client that takes JSON:
figsnap-mcp-daemon --mcp prints both, with paths already filled in.
Then claude mcp list should show figsnap-mcp · ✔ Connected, and in a session
figma_get_selection answers about whatever is selected on the canvas.
When it does not answer
Three things can be wrong, and each says so differently:
Resources
Three things a question about a Figma file almost always needs are addressable
rather than called for, so a client can @-mention them:
The panel
Four panes, and the one it opens on is Selection.
Selection is a preview of whatever you have picked on the canvas — the picture, its name, type, size, child count and node id — with a folder picker and a Save button beside it. Saving from here is the short way round: no switching panes, no hunting for the layer again in a list.
The preview costs one PNG export. The panel used to run a full extraction on every selection change — HTML, TSX, two stylesheets and a 2× image — for a picture nobody was looking at; the code outputs are what MCP asks for, on request, not what a click costs. A small layer is magnified at most 4×, so an icon and a screen do not both fill the stage.
Minimise (the ▼ at the top right) drops the window to a 44px strip and the preview under it, so the canvas is clear while the plugin keeps running — the daemon needs the panel open, but you do not need to look at it. The strip still names what is selected and still has Save, because those are the two things worth having while you work. ▲ puts it back to whatever size you had dragged it to.
Connect and Tools are pairing and the tool list; Saved is the folder manager. Both are described below.
The tools
41 of them: 14 read Figma, 26 write to it, and one is answered in the daemon.
The Tools pane lists them all with the writing ones marked; GET /tools is
the same list as JSON.
Reading is always allowed. Writing is not, until you say so — every mutating
tool is refused until Allow edits is on, which is a switch the designer holds
rather than a prompt the agent can talk past. A harness running with permission
prompts disabled still cannot get past it. --allow-edits opens the same gate
from the terminal, for when the work is happening there.
Two tools fold thirteen plugin commands into one argument each, because 41 tool descriptions already cost real context on every request:
figma_extracttakesnodeId, ornodeIds,urls,selection: true,saved: truefor a batch.figma_savedtakes anaction:list,folders,save,unsave,clear,move,newFolder,renameFolder,deleteFolder.
A picture comes back as a real image block, never as base64 in a text field —
that is what figma_export_png is for, one node at a time.
figma_ios_spec answers with the properties Figma actually holds rather than
with CSS, in points: per-axis sizing, auto layout and its padding, stroke weight
and alignment, corner smoothing, effects, the text block — and every fill and
stroke marked painted or not. Reach for it when the answer becomes code.
figmaCss describes what a node would look like on the web, which drops what an
implementation needs and also reports paint that is never painted: a stroke
switched off still comes back as border: 1px solid. A stroke here carries both
layoutFrame and visualFrame, because a CENTER stroke puts half its weight
outside the bounds while UIKit draws borders inside them, and an effect carries
uikitShadowRadius, which is half the blur.
One limit: a gradient paint reports its type, opacity and painted flag but not
its stops, so you learn that a gradient is there without being able to reproduce
it. Read those colours off figma_export_png for now.
figsnap_skill is the odd one out: the plugin has no part in it, so it answers
with Figma closed. check says whether this project has the skill this package
ships — missing, current, or modified if you have edited your copy, which
is yours and so is reported rather than replaced. install hands back the file
and where it goes instead of writing it, because whatever is calling already
asks you before it writes into your repository. Which project it means comes
from the MCP client's own directory, which mcp-stdio.mjs names on every call —
the daemon's is wherever you started it, and usually not where you are working.
The saved set
The Saved pane is a place to keep the components you keep coming back to:
folders, Save selection, jump-to-node, move, remove. It is reachable over MCP
as figma_list_saved and figma_saved, so you and the agent are looking at the
same list.
It lives in figma.clientStorage, keyed by document id. That means: per user,
per file, per machine. Up to 100 entries and 30 folders, one level deep.
One deliberate loss. Figsnap also mirrors this set to its relay, so it follows you to a second machine. Without the Worker, these sets are local only. That is the right trade for this repo — no account, no network, and always writable even in a file you can only view — but it is a real difference. If cross-device sync is wanted later it is an additive change: the plugin would gain a sync target, not a new owner of the data.
Layout
Using it as a library
Most people want the two commands. If you are building your own bridge, the package exports the catalogue and the pieces the daemon is assembled from:
Importing it starts no server and opens no socket — agent/mcp-stdio.mjs
connects an MCP server to stdio the moment it loads, so it is deliberately not
re-exported, and the constants that used to live on it are in
agent/lib/paths.mjs. index.d.mts is hand-written and the test suite checks it
against the runtime in both directions.
Semver applies from 1.0.0. While this is 0.x, the factories are the part most likely to move; the catalogue and the constants are the stable half.
Security
The daemon binds 127.0.0.1 only, and two things guard the socket, because a
local port is reachable by any page you happen to visit:
- Origin, checked on upgrade. A plugin iframe is a sandboxed document and
sends
null; the editor sends figma.com. Anything else is closed with 4001. A browser cannot forge this header, and CORS does not apply to an upgrade, so it is the check that matters. - A token in the query string, because a browser WebSocket cannot set
headers. It is the same one HTTP callers send as
x-figsnap-token, and only/healthis reachable without it — so the panel can probe before it is paired.
--new-token rotates it if one ever leaks.
Tests
e2e-plugin.mjsruns the shippeddist/code.jsagainst a fakefigma, wired to a real daemon, and drives it throughPOST /tool— extraction fidelity,figma_find_nodes, the saved set including a reload, and a write with the gate both shut and open.e2e-mcp.mjsspawns the daemon, fakes the panel as a WebSocket client, and drives a real MCP client over stdio: the guards, the tool list, the batch and image rules, all ten saved-set commands, the resources, and the three ways a call can fail before it reaches Figma.e2e-panel.mjsloads the shippeddist/ui.htmlinto jsdom with the main thread and the daemon replaced, and drives the designer's side: the panes render what they are sent, the clicks mean what they say, and a destructive folder action arms before it fires.contract.mjsis the drift guard.shared/,agent/lib/tools.mjsandsrc/code.tsexist in both this repo and Figsnap, and the protocol between them has no shared type. So it asserts what a one-sided edit would break: every command a tool can name is a case insrc/code.tsand every case is reachable from a tool,MAX_BATCHagrees, the caps the panel prints are the caps the plugin enforces, thefind_nodesschema offers exactlyFINDABLE_TYPES, all three files agree on 3058, and nothing has quietly imported the relay, the accounts or the ACP client back in.
Publishing the plugin to your organisation
The plugin can stay a development install — import the manifest, done — but publishing it to your Figma organisation puts it in everyone's plugin list and updates them automatically, without it appearing in Community.
That option needs a Figma Organization or Enterprise plan. On Professional the only published option is Community, which is public; a development install is the private route there.
- Build first:
npm run build. Figma publishes whatdist/holds, not whatsrc/says. - Figma desktop → Plugins → Development → Figsnap MCP → Publish.
- Choose Only rather than Community.
- Fill in the listing: a 128×128 icon, a description, and a cover image. An org-only publish skips Community review, so it is live once you submit.
Figma writes a plugin id into manifest.json on that first publish,
replacing REPLACE_ON_PUBLISH_FIGSNAP_MCP. Commit that change — it is what
identifies later versions as updates rather than a new plugin, and it is also
what keeps this plugin's clientStorage separate from Figsnap's. To release an
update, build again and publish again from the same menu.
One thing worth being clear about: this repository and the npm package are public, and both carry the built plugin. Org-only publishing controls listing and distribution inside Figma, not who can obtain the code — it is MIT either way.
Releasing
CI runs the suites on Node 20, 22 and 24 for every push.
The first release is manual, because npm configures a trusted publisher on a package's own settings page and there is no page until the package exists:
Then turn on trusted publishing, once, at
npmjs.com/package/figsnap-mcp/access → Trusted Publisher → GitHub Actions:
Every release after that is a tag:
npm version also runs scripts/sync-server-json.mjs, which copies the new
version into server.json's two version fields and stages it, so all three end
up in the same commit. The workflow refuses a release where they disagree —
which is how 0.2.0 was caught before it reached the registry.
.github/workflows/publish.yml picks the tag up, refuses it if it disagrees with
package.json, and publishes with provenance over OIDC — no token exists to
leak, which matters because npm is restricting tokens that bypass 2FA (account
changes August 2026, direct publishing January 2027).
prepublishOnly typechecks, builds and runs every suite first, so a release that
would not have worked cannot reach the registry. npm pack --dry-run shows
exactly what would be sent; test/e2e-package.mjs asserts those contents.
Licence
MIT.
来源:README.md,提交 b10f5f9
工具
0版本历史
1- v0.2.0最新Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分