localfig
Provides tools for interacting with the Figma desktop app through its Plugin API, enabling AI agents to read and modify design files, manage layers, variables, components, extract metadata, export assets, and more.
localfig
The Figma desktop app as an MCP server. localfig gives an AI agent the full Figma Plugin API on the file you have open — through a tiny dev plugin and a loopback HTTP bridge, entirely on your machine.
Reads and writes. Frames, text, auto-layout, variables, components, image fills, exports, undo — anything the Plugin API can do, the agent can do.
No Figma cloud API, no personal access token, no per-account quota.
Zero dependencies. Node built-ins only. Nothing leaves your machine.
Works with any MCP client that can launch a stdio server: Claude Code, Cursor, Windsurf, Cline, Claude Desktop and more.
MCP client --stdio JSON-RPC--> localfig --http localhost:8765--> Figma plugin --> Plugin APIWhy localfig
localfig | Official Figma MCP | REST-API servers | Fixed-command plugin bridges | |
Writes to the file | anything the Plugin API can | limited | no — the REST API cannot write nodes | a fixed menu of commands |
Arbitrary Plugin API JavaScript | yes ( | no | no | no |
Quota / login | none | plan-dependent | token + rate limits | none |
Tokens, styles, components read back |
| yes, incl. Code Connect | partial | rarely |
Agent can see the design as an image | yes, inline in the response | yes | files or links, not inline | varies |
Undo per agent step, change tracking | yes | no | no | no |
Related MCP server: figma-proxy-mcp
Requirements
Node.js 18 or newer
The Figma desktop app — dev plugins cannot be imported in the browser
An MCP client: Claude Code, Cursor, Windsurf, Cline, Claude Desktop, …
Install (once)
npx -y localfig --setupThat generates your local secret, writes the plugin to ~/.localfig/plugin/, registers localfig with every MCP client it finds on your machine, and prints the manifest path for the next step. --dry-run shows what it would do without touching anything, and --clients=cursor,vscode limits it to the clients you name. Prefer a clone? git clone https://github.com/LucasGorgal/localfig.git && cd localfig && node server.mjs --setup does the same.
Client | Where setup registers localfig |
Claude Code |
|
Claude Desktop |
|
Cursor |
|
Windsurf |
|
VS Code (Copilot agent mode) | user |
Cline (extension and CLI) |
|
Gemini CLI |
|
Codex |
|
opencode |
|
Zed |
|
LM Studio |
|
A client counts as installed when its config folder exists. Setup merges into existing files instead of replacing them: other servers and settings stay, an existing localfig entry keeps its env, and a .localfig.bak copy is written before any change. Files it cannot safely rewrite, such as a settings.json with comments, are left untouched and reported with the entry to paste. Restart any client that was already open.
For a client that is not in the list, add this to its MCP configuration:
{ "mcpServers": { "localfig": { "command": "npx", "args": ["-y", "localfig"] } } }On Windows, use "command": "cmd", "args": ["/c", "npx", "-y", "localfig"] instead: most clients start servers without a shell, and npx is a .cmd script there.
Then import the plugin into Figma (once):
Open any design file in the desktop app. The Plugins menu only exists inside a file, not on the home screen.
Figma menu (the logo, top-left) → Plugins → Development → Import plugin from manifest… — or press
Ctrl/(Cmd/) and type import plugin from manifest.Pick
~/.localfig/plugin/manifest.json(the setup output prints the full path).
The manifest asks for two permissions — teamlibrary (library components and tokens) and currentuser (who is driving) — and registers a relaunch button.
Use (every session)
Open the file you want to work on.
Run the plugin:
Ctrl/→ localfig — orCtrlAltP(CmdOptP) to re-run the last plugin, or the Reconnect localfig button in the properties panel of a file it has run in. A small panel appears and says Connected to localfig once the MCP server is up. Leave it open.Ask your agent to do Figma work. Every tool acts on the file where the plugin is running — there is no file key, which is why nothing touches the cloud.
Closing the panel (or the file) disconnects; re-run the plugin to reconnect. This is the one manual step: Figma provides no way to launch a plugin from outside.
Tools
Tool | What it does |
| Plugin connected? File, page, selection, top-level frames, who is driving, and whether dynamic code execution is available |
| Run arbitrary Plugin API JavaScript in the file — the workhorse |
| Structural dump of a subtree: ids, names, types, geometry, text. |
| Every local variable collection with its modes and per-mode values, plus paint / text / effect styles |
| Search the current page or every page by type, layer-name regex and text regex |
| What changed since your last call — creations, deletions, property changes, per-node summary; edits made by tool calls are tagged and hidden by default, so this is what the person did in Figma meanwhile |
|
|
| Team library: list variable collections and their variables, import components / styles / variables by key, place instances |
| Export nodes to PNG/JPG/SVG/PDF/JSON on disk. PNG/JPG come back inline as images; JSON (the subtree in Figma REST API shape) and SVG come back inline as text |
| Put a local image into a node's fill. Any format the browser decodes — PNG, JPG, WebP, GIF, BMP, AVIF. Images over 4096px per side are downscaled automatically ( |
figma_eval environment
Code runs inside the plugin sandbox, wrapped in an async function: top-level await and return both work.
const n = await figma.getNodeByIdAsync('10:59'); // sync getNodeById is unavailable (dynamic-page access)
await helpers.setText(n.findOne(x => x.type === 'TEXT'), 'new copy');
helpers.reveal(n); // put it on the person's screen
return { id: n.id, w: n.width }; // JSON-serialized back to the agentGlobals: figma and helpers.
helpers.createText({characters, font: {family, style}, fontSize, color: '#hex', width, lineHeight, letterSpacing, textCase, textAlign, name, parent, x, y})— loads the font and ordersresize()beforetextAutoResize.helpers.setText(node, chars)— loads the node's real fonts, then sets characters.helpers.loadNodeFonts(node).helpers.set(node, props)— batch assign;layoutModefirst,width/heightviaresize, sizing modes after.helpers.rgb('#rrggbb'),helpers.rgba('#rrggbbaa')— hex to the{r, g, b[, a]}fills want.helpers.reveal(nodes)— scroll and zoom the viewport to what you just made.helpers.notify(msg)— a toast.helpers.collection(name, [modes]),helpers.token(collection, name, type, value | {mode: value}),helpers.bind(node, 'fills' | 'strokes' | prop, variable)— create design tokens and bind properties to them, one line each.helpers.importComponent(key),helpers.instance(key, parent, props)— team-library components.helpers.query(root, sel)— minimal selectors:TEXT,[name=X],[name*=X],A B,a, b.helpers.createAutoLayout(dir, props).helpers.command(kind, payload)— run any typed command (tokens,metadata,find,changes,export…) from inside a script:(await helpers.command('tokens', { collection: 'Brand' })).result.
Plugin API gotchas the helpers protect you from
resize()on a TEXT node resetstextAutoResizetoNONE; on an auto-layout frame it sets both sizing modes toFIXED. Resize first, then set the sizing.Return plain data from
figma_eval. Real Figma nodes are collapsed to{id, name, type}so a stray node reference cannot flood the result — usefigma_metadatato inspect a subtree.Load fonts before any text mutation.
figma.notify()works here (the cloud MCP blocks it).
Design-system awareness
figma_tokens returns the file's variables the way a design system thinks about them:
{ "collections": [{ "name": "Brand", "modes": ["Light", "Dark"], "defaultMode": "Light",
"variables": [{ "name": "color/bg", "type": "COLOR", "values": { "Light": "#ffffff", "Dark": "#04060c" } },
{ "name": "space/md", "type": "FLOAT", "values": { "Light": 16, "Dark": 16 } },
{ "name": "color/accent", "type": "COLOR", "values": { "Light": "{color/blue-500}", "Dark": "{color/blue-300}" } }] }],
"styles": { "paint": [...], "text": [...], "effect": [...] } }figma_metadata with styles: true then reports, per node, boundVariables (which token drives each property), fillStyle / textStyle / effectStyle names, and for instances the main component and its componentProperties; css: true adds the CSS Figma computes for each node. Together they let an agent build on a design system instead of hard-coding values — and hand code off from a real file.
Images
In:
figma_place_imagesends the file's bytes to the plugin UI, which is a full Chromium iframe. It decodes any browser-supported format, downscales tomaxSide(default 4096, Figma's hard limit) with high-quality smoothing, converts non-native formats to PNG, and hands the result to the main thread. The tool result reports what happened (prepared).Out:
figma_exportwrites files to~/.localfig/exports/and attaches PNG/JPG (≤ 2 MB each) to the result as images, so the agent sees the render without another call.JSONexports the subtree in Figma REST API shape.
Undo, versions and change tracking
Every mutating tool call is checkpointed with figma.commitUndo, so it lands in the file's undo history as one step a person can Ctrl+Z (the most recent call is committed 60 s after it finishes, or at the next call). Within that window figma_history {action: "undo"} reverts the last call from the agent side — triggerUndo reverts to the last checkpoint — and {action: "snapshot"} saves a named version before risky edits. The plugin also records node changes on every page it has seen (Figma delivers them batched, after the fact): figma_changes returns what changed since a seq, with edits caused by tool calls tagged byPlugin by a time-window heuristic and hidden by default — the agent can notice what the person changed in Figma between calls instead of overwriting it.
Multiple sessions
The first localfig process owns the bridge port; later ones detect it via /health, run as clients, and proxy through it — same tools either way. If the owner dies, the next tool call on a client re-binds the port and takes over ("re-election" in the log); the plugin re-registers when it reconnects.
Files and configuration
~/.localfig/
token local secret (generated on first run)
plugin/ manifest.json + code.js + ui.html — import THIS manifest into Figma
exports/ where figma_export writesVariable | Default | Notes |
|
| Where the token, plugin and exports live |
|
| Also change |
|
| Where |
node server.mjs --help lists the flags.
Security
The bridge binds loopback only (127.0.0.1 and ::1). Every endpoint except /health requires a 32-hex secret generated on first run into ~/.localfig/token and baked into ~/.localfig/plugin/ui.html. Edit plugin/ui.template.html in the package, never the generated ui.html. Nothing is sent to any server.
Design notes
Why a plugin? It is the only local surface with full read/write access to an open Figma document. The REST API is cloud, needs a token, and cannot write nodes.
Why HTTP long-poll and not WebSocket? The plugin main thread has no network at all; only the UI iframe does. Long-poll needs no dependency and no framing code, and
http://localhostis exempt from mixed-content blocking. Figma's manifest validator rejects raw IPs innetworkAccess, hencelocalhost— and the IPv6 mirror listener, because Windows resolveslocalhostto::1first.Binary stays binary on the plugin channel. Exports leave as
POST /blob; images enter viaGET /assetinto aUint8Array. Only JSON goes through the command channel. (Renders are base64-encoded once, in the MCP result, where the protocol requires it.)Why a home directory? The package may live in the npx cache or a read-only global install; the plugin folder you import must not move when the package updates, and per-user state must not live in a package.
Platform notes
Developed and tested on Windows 11 with the Figma desktop app. macOS and Linux should work unchanged — Node built-ins only, no native code — but have not been exercised yet. Reports and fixes welcome.
Test
node test-e2e.mjsSpawns the server in an isolated home, speaks MCP over stdio, and impersonates the plugin over HTTP — covering the handshake, all ten tools, file round-trips, inline images, owner re-election, and setup against sandboxed MCP client configs, without Figma running. 55 assertions.
Layout
server.mjs MCP (stdio) + bridge (http), single process; --setup, --help
plugin/manifest.json copied to ~/.localfig/plugin on start
plugin/code.js main thread: executes commands against the Plugin API
plugin/ui.template.html UI source; token/port get baked into ~/.localfig/plugin/ui.html
test-e2e.mjs end-to-end test without FigmaLicense
MIT — see LICENSE.
Available Tools
10 toolsfigma_changesARead-only
What changed in the file since a previous call: node creations, deletions and property changes recorded by the plugin, with a per-node summary. Changes made by tool calls are tagged byPlugin (heuristic: they arrive during, or within 2 s after, a mutating call) and hidden by default, so this shows what the person edited in Figma between your calls. Pass back the returned seq as since.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max entries returned, newest kept (default 200). | |
| since | No | seq from the previous call. Omit for everything buffered (last 1000 changes). | |
| includePlugin | No | Also list changes caused by tool calls. Default false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the byPlugin tagging heuristic with its exact time window and states that such changes are hidden by default. This goes well beyond the readOnlyHint annotation and tells the agent that attribution is heuristic, not guaranteed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences, each earning its place: purpose, the byPlugin heuristic, and the continuation protocol. The most important scoping information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple polling tool with no output schema, the description provides enough to invoke it correctly: what is returned, the seq continuation contract, and how to include plugin changes. The exact shape of change entries is not specified, but the per-node summary and seq are sufficient guidance.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so parameters are already documented. The description adds meaningful usage semantics for since ('pass back the returned seq') and for includePlugin (plugin changes are hidden by default), raising it above the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states exactly what the tool returns: file changes since a previous call, broken into node creations, deletions, and property changes with a per-node summary. The 'since a previous call' scope differentiates it from generic status or history tools even without naming a sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a concrete usage pattern: pass back the returned seq as since, and explains that plugin-triggered changes are hidden by default so the tool shows human edits between calls. It does not explicitly name alternatives or exclusion conditions, but the intended use case is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_evalA
Run JavaScript inside the Figma plugin sandbox, against the file where the bridge plugin is running.
The code body is wrapped in an async function: top-level await and return both work.
Globals: figma (full Plugin API) and helpers.
Use figma.getNodeByIdAsync(id) — the sync getNodeById is unavailable under dynamic-page access.
Switch pages with: await figma.setCurrentPageAsync(page)
Load fonts before ANY text mutation, or use helpers.setText / helpers.loadNodeFonts.
The return value is JSON-serialized (figma.mixed -> "mixed", cycles pruned). Real Figma nodes collapse to {id,name,type}; return plain data (ids, numbers, strings) instead.
resize() RESETS sizing: on TEXT it sets textAutoResize to NONE (resize first, then set textAutoResize = "HEIGHT"); on auto-layout frames it sets both sizing modes to FIXED (set them after resizing, or use helpers.set / helpers.createText, which order this correctly).
Heights of text and hug-sized frames read back correctly right after the change — if you see h=10 on a text you resized, the auto-resize was reset (see above).
Each mutating tool call becomes ONE undo step for the person (checkpointed with figma.commitUndo); figma_history {action:"undo"} reverts the last call if asked within 60 s. Call helpers.reveal(nodes) at the end so what you built is on their screen.
For structure + styles (fills, fonts, effects, auto-layout, bound variables, components) of an existing frame, prefer figma_metadata with styles:true over a hand-written walker; figma_tokens lists the file's variable collections and styles.
helpers: setText(node, chars), loadNodeFonts(node), createText({characters, font:{family,style}, fontSize, color:"#hex", width, lineHeight(%), letterSpacing(px), textCase, textAlign, name, parent, x, y}), rgb("#hex"), set(node, props), query(root, selector), createAutoLayout(dir, props), reveal(nodes), notify(msg), rgba("#hex8"), collection(name, [modes]), token(collection, name, "COLOR"|"FLOAT"|"STRING"|"BOOLEAN", value | {mode: value}), bind(node, "fills"|"strokes"|prop, variable), importComponent(key), instance(key, parent, props), command(kind, payload) — runs a typed command (tokens, metadata, find, changes...) from inside eval and returns {result}.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | JavaScript to run. Use return to send data back. | |
| timeoutMs | No | Default 60000. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the lone openWorldHint annotation, the description discloses critical runtime behavior: async wrapping, available globals, JSON serialization with node collapsing, resize() side effects on text/auto-layout frames, undo checkpointing, and the need for helpers.reveal(). It also explains font loading requirements before text mutation. This is unusually transparent for a code-execution tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place; it uses bullets to separate behavioral gotchas and a compact helper reference. The main purpose is front-loaded, and subsequent details address exactly the failure modes an agent would encounter when calling eval. No filler or redundant restatement of schema fields appears.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity of arbitrary code execution and no output schema, the description is remarkably complete: it covers what can be returned, how nodes serialize, how to handle fonts and resizing, undo semantics, page switching, and available helpers. An agent has enough information to write correct eval calls and recover from common mistakes.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description richly extends the code parameter's meaning by documenting the execution model, supported syntax, helper functions, return-value behavior, and common pitfalls. It transforms 'JavaScript to run' into a practical programming contract. The timeoutMs parameter is simple, and 'Default 60000' already suffices.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb and resource: 'Run JavaScript inside the Figma plugin sandbox, against the file where the bridge plugin is running.' It clearly distinguishes this from sibling tools like figma_metadata and figma_find, which inspect or search rather than execute arbitrary code. No ambiguity exists about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit alternative guidance for structure and style extraction: 'prefer figma_metadata with styles:true over a hand-written walker' and 'figma_tokens lists the file's variable collections and styles.' It does not exhaustively enumerate when not to use eval against every sibling, but it provides clear context for the most common overlapping case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_exportAIdempotent
Export nodes to local files. PNG/JPG (up to 2 MB each) are attached inline as images — the screenshot path. JSON = the subtree in Figma REST API shape (JSON_REST_V1), for design-to-code tooling; SVG and JSON also come back inline as text (up to 200 KB).
| Name | Required | Description | Default |
|---|---|---|---|
| scale | No | Raster scale, default 1. Use 0.5 for quick looks. | |
| format | No | Default PNG. JSON = Figma REST API shape. | |
| inline | No | Attach PNG/JPG results as image content (default true). Set false when exporting many nodes or when only the files are needed. | |
| nodeIds | No | Node IDs to export. Omit to export the current selection. | |
| contentsOnly | No | Export only the node contents, excluding overlapping siblings (Figma default: true). | |
| useAbsoluteBounds | No | Include the full bounds incl. effects such as shadows. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the idempotentHint/openWorldHint annotations, it discloses concrete output behavior: PNG/JPG attach inline as images (up to 2 MB), SVG/JSON come back inline as text (up to 200 KB). It doesn't mention PDF behavior, but the main return behavior is transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core action, and no filler. The 'local files' phrasing is slightly at odds with the inline-return explanation, but the description is compact and scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
It covers PNG/JPG/SVG/JSON outputs and size limits, but with no output schema it leaves PDF behavior and the meaning of 'local files' (where files land when inline=false) unexplained. That's a meaningful gap for a 6-parameter export tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already explains scale, format, inline, nodeIds, contentsOnly, and useAbsoluteBounds. The description adds size limits and inline-return context, but most parameter meaning is already in the schema, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Export nodes to local files.' It also clarifies the main output families (raster images, SVG, JSON) and their intended roles, which sets it apart from sibling tools like figma_status or figma_find. It doesn't explicitly name alternatives, but the purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear format-selection guidance: PNG/JPG for the 'screenshot path' and JSON for 'design-to-code tooling,' plus inline-vs-file context. It stops short of explicit when-not-to-use or sibling-tool comparisons, so it earns a 4 rather than a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_findARead-only
Search the current page (or all pages) for nodes by type, layer-name pattern and/or text content. Returns ids, names and positions — the way to locate things in a big file before editing.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Case-insensitive regex matched against the layer name. | |
| text | No | Case-insensitive regex matched against text content (TEXT nodes only). | |
| limit | No | Max hits (default 200). | |
| scope | No | Current page (default) or every page. | |
| types | No | Node types, e.g. ["TEXT"] or ["FRAME","INSTANCE"]. Omit for any type. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the description doesn't need to cover safety. It adds useful context by stating it returns ids, names, and positions, and implies a non-destructive locate-before-edit workflow. It does not disclose any additional behaviors like pagination, rate limits, or matching semantics beyond what the schema already covers, so a 3 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler. The first sentence states the action, scope, and filters; the second states the return value and intended use. Everything earns its place and key information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only search tool with a fully documented schema, the description is complete. It covers what the tool searches, the filters, the return values, and when to use it. No output schema exists, but the description supplies the essential return info (ids, names, positions) and the default scope is in the schema. There are no missing prerequisites or side effects to disclose given readOnlyHint=true.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and every parameter (name, text, limit, scope, types) is fully documented in the input schema. The description only generically mentions filtering by type, name pattern, and text content, which adds no meaning beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with a specific verb 'Search' and clearly defines the resource and scope: nodes on the current page or all pages, filterable by type, name pattern, and text content. It also names the return values (ids, names, positions) and frames the tool as the way to locate things before editing, distinguishing it from sibling tools that handle metadata, export, or changes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context: 'the way to locate things in a big file before editing.' It communicates this is a lookup/search tool, not a mutation tool. However, it does not explicitly state when not to use it or name alternative sibling tools, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_historyA
snapshot: save a named version to the file's version history — a restore point before risky edits. undo: revert the edits of the last mutating tool call (figma_eval / figma_place_image / library import) if it ran within the last 60 s; after that they are committed to the file's undo history as one step per call, where the person can Ctrl+Z them.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | snapshot: version title (default "localfig <timestamp>"). | |
| action | Yes | ||
| description | No | snapshot: version description. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations carry only openWorldHint=false, so the description bears the burden of behavioral disclosure. It reveals critical behavior: undo's 60-second window, the one-step-per-call consolidation into the file's undo history, and the fallback that a person can Ctrl+Z afterward. It also clarifies snapshot is a restore point, which is meaningful beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The two action-labeled clauses make the structure scannable and avoid repetition. The undo sentence is long and packs several conditions into a semicolon-connected run-on, but every clause earns its place and the content is dense rather than padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-action tool with no output schema, the description covers both operations, their preconditions, the relevant sibling tools, the time window, and the post-window fallback behavior. Nothing essential is missing for an agent to decide when and how to invoke it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67%, and title/description params are already documented as snapshot-specific. The description adds value by defining the action enum semantics in detail — what undo reverts, the timeout, and the scoped mutating tools — which goes beyond the bare 'snapshot'/'undo' enum values.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly defines two distinct operations: snapshot (save a named version) and undo (revert the last mutating call). Both use specific verb+resource phrasing, and the action labels make the tool's purpose immediately understandable. The title alone is vague, but the description fully resolves that ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to use snapshot ('before risky edits') and exactly what undo applies to (the last mutating call among figma_eval, figma_place_image, or library import) with a 60-second condition. It does not explicitly name exclusions or alternative tools for inspecting history, so it stops short of a full when-not-to-use guideline.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_libraryA
Team library access (the plugin manifest asks for the "teamlibrary" permission). collections: list the library variable collections available to this file. variables: list the variables in one collection (collectionKey). import: bring a component / componentSet / style / variable into the file by its library key; for components, instance:true also places an instance (figma_metadata reports component keys on instances).
| Name | Required | Description | Default |
|---|---|---|---|
| x | No | ||
| y | No | ||
| key | No | import: the library key. | |
| kind | No | import: what the key refers to (default component). | |
| action | Yes | ||
| instance | No | import component: also create an instance. | |
| parentId | No | import: parent node for the instance (default: current page). | |
| collectionKey | No | variables: the collection key from action collections. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only openWorldHint in annotations, the description carries the burden of behavioral disclosure and does so well: it notes the required 'teamlibrary' permission, indicates that import modifies the file ('bring ... into the file'), and discloses the instance side-effect. It stops short of describing duplicate-import behavior or return values, but the core mutation and permission traits are transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loads the permission requirement before enumerating actions. The semicolon-separated action list is dense but scannable; a more structured format would improve readability, but there is no wasted wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with eight parameters, three action modes, and no output schema, the description covers the key workflows but leaves gaps: x/y semantics are absent, return formats for collections/variables are not mentioned, and error or duplicate-import behavior is undisclosed. It is adequate for action selection but not fully self-sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 63%, so the description must add action-to-parameter meaning, which it does: collectionKey for variables, key for import, instance behavior for components. However, x and y are not explained anywhere, leaving their purpose unclear, so the compensation is strong but not complete.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly names the resource (team library) and uses explicit verbs for each action: 'list' for collections/variables and 'bring' for import. It also distinguishes the three sub-actions and references figma_metadata for component keys, though it does not explicitly contrast itself with sibling tools like figma_tokens or figma_find.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Each action is introduced with a clear directive ('collections:', 'variables:', 'import:') that tells an agent when to use that mode. It also provides useful workflow context by pointing to figma_metadata for component keys, but it does not state exclusions or explicitly say when to prefer a sibling tool over this one.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_metadataARead-only
Structural dump of a node subtree (id, name, type, x/y/w/h, text characters). Omit nodeId for the current page. Cheap way to find node IDs before editing. Pass styles:true to also get fills, strokes, effects, opacity, corner radius, auto-layout and text styles per node — plus bound variables (token names), named styles, and for instances the main component and its property values. css:true adds the CSS Figma computes per node.
| Name | Required | Description | Default |
|---|---|---|---|
| css | No | Include the CSS Figma computes for each node (getCSSAsync) — the design-to-code handoff. Default false. | |
| depth | No | Levels to descend (default 6). | |
| nodeId | No | e.g. "10:59". Omit for the current page. | |
| styles | No | Include fills, strokes, effects, opacity, corner radius, auto-layout and text styles (font, size, line-height, spacing, case) per node. Default false. | |
| maxNodes | No | Node cap (default 400). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adds valuable behavioral context beyond the schema: it is a 'structural dump,' it is 'cheap' (implying low cost/performance), it has a default depth of 6 and node cap of 400 (though those are in the schema), and it explains what styles:true and css:true expand the output to include. It doesn't mention pagination or truncation behavior, but the maxNodes parameter covers the cap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place. The first sentence defines the core output and the key usage pattern (omit nodeId). The second sentence explains the styles flag with a comprehensive list. The third sentence explains the css flag. No filler, no repetition of schema details that don't need emphasis.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only inspection tool with 100% schema coverage and no output schema, the description is quite complete. It covers what the tool returns, how to scope it (nodeId), and how to enrich output (styles, css). The only minor gap is that it doesn't explicitly describe the output structure or warn about the maxNodes cap truncation, but the schema covers maxNodes and the description's 'structural dump' phrasing implies a JSON tree.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds meaning beyond the schema by explaining the purpose of the styles flag ('fills, strokes, effects, opacity, corner radius, auto-layout and text styles per node — plus bound variables, named styles, and for instances the main component and its property values') and the css flag ('the design-to-code handoff'). It also clarifies nodeId semantics ('Omit for the current page'), which the schema already states but the description reinforces in context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb and resource: 'Structural dump of a node subtree' and enumerates exactly what is returned (id, name, type, x/y/w/h, text characters). It also distinguishes itself from editing tools by calling itself a 'cheap way to find node IDs before editing,' which separates it from siblings like figma_find and figma_export.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use it: 'Cheap way to find node IDs before editing.' It also gives concrete usage guidance for optional flags: 'Pass styles:true to also get...' and 'css:true adds the CSS Figma computes per node.' This tells an agent exactly how to invoke it for different needs and implies it is the read-only exploration tool among siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_place_imageA
Put a local image file into a node as an image fill. Any format the browser decodes (PNG, JPG, WebP, GIF, BMP, AVIF...): images over 4096px per side are downscaled automatically in the plugin UI and non-native formats are converted to PNG. The result reports what was done (prepared).
| Name | Required | Description | Default |
|---|---|---|---|
| nodeId | Yes | Node that receives the image fill. | |
| maxSide | No | Downscale so the longest side is at most this many pixels (default 4096, the Figma limit). | |
| filePath | Yes | Absolute path to an image on this machine. | |
| scaleMode | No | Default FILL. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses important behaviors: automatic downscaling over 4096px, conversion of non-native formats to PNG, and that the result reports what was done. The annotation openWorldHint=false is minimal, so the description carries the burden and does it well.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded with the core action, followed by important behavioral details. Every sentence adds value, and it's appropriately sized for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the key behaviors and result reporting. It doesn't explicitly mention error cases or prerequisites (e.g., node must exist, file must be accessible), but the schema and annotations provide enough for an agent to call it correctly. The output schema is absent, but the description mentions the result reports what was done.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all parameters. The description adds context about the default maxSide (4096) and the default scaleMode (FILL), which aligns with the schema. It doesn't add much beyond the schema, but the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: placing a local image file into a node as an image fill. It specifies the resource (node) and the action (place image), and distinguishes it from sibling tools like figma_export or figma_metadata by focusing on image placement.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context on when to use this tool (placing local images) and mentions automatic downscaling and format conversion. It doesn't explicitly name alternatives or exclusions, but the sibling list and the specific action make the use case clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_statusARead-only
Bridge + plugin health: is the Figma plugin connected, which file/page is open, current selection, and whether dynamic code execution is available.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds meaningful behavioral scope beyond annotations by naming the reported state: connection status, active file/page, selection, and code execution availability. It does not contradict the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence with a colon that sets the scope. Every element—connection, file/page, selection, code-execution availability—earns its place with no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only status tool with no output schema, the description is largely complete: it names the key state dimensions an agent would need to decide about calling the tool. It could go slightly further by noting what happens when the bridge is disconnected, but this is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema is empty with 100% coverage, so there are no parameters to explain. The description correctly focuses on the tool's informational output rather than param semantics, which earns the baseline for zero-parameter tools.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies a status-reporting tool: 'Bridge + plugin health' and enumerates the exact state it returns (connection, open file/page, current selection, dynamic code availability). It is distinct from siblings like figma_eval and figma_metadata, though it does not explicitly name or contrast them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies a health-check use case: an agent would call this to verify plugin connectivity and dynamic code execution availability before taking actions such as figma_eval. However, it never explicitly states when to prefer this tool over alternatives or provides exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_tokensARead-only
Design tokens and styles of the open file: every local variable collection with its modes and per-mode values (colors as hex, aliases as {name}), plus local paint/text/effect styles. Read this before generating code or building on a design system; figma_metadata with styles:true then tells you what each node is bound to.
| Name | Required | Description | Default |
|---|---|---|---|
| collection | No | Only collections whose name contains this (case-insensitive). | |
| maxVariables | No | Cap on variables returned (default 2000). | |
| includeStyles | No | Include local paint/text/effect styles. Default true. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint=true already covering side-effect safety, the description adds meaningful context about output shape: colors as hex, aliases as {name}, and inclusion of paint/text/effect styles. It does not mention limits or error behavior, but those are less important for an annotated read-only tool. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences carry both return content and usage ordering with no filler. The first sentence front-loads what the tool returns, and the second tells the agent when to use it and what to call next.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given three optional, fully documented parameters and no output schema, the description adequately explains scope and return shape. It could additionally note the default variable cap or behavior when no collection matches, but those are minor gaps and the schema partially covers the cap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so collection, maxVariables, and includeStyles are already documented in the schema. The description only implicitly touches includeStyles by mentioning local styles and adds no filter, cap, or default semantics beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description specifies a concrete resource: local variable collections with modes and per-mode values, plus paint/text/effect styles. It also names figma_metadata as a complementary tool, which helps distinguish this from siblings. It lacks a direct operative verb like 'return' or 'list', but the colon-definition and usage directive make the purpose clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit when-to-use: 'Read this before generating code or building on a design system.' It also points to figma_metadata with styles:true as the next step for node bindings. It does not state exclusions or when not to use this tool, so it falls just short of full guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.4.0- First observed
figma_changes - First observed
figma_eval - First observed
figma_export - First observed
figma_find - First observed
figma_history - First observed
figma_library - First observed
figma_metadata - First observed
figma_place_image - First observed
figma_status - First observed
figma_tokens
TDQS
Scored across 10 tools
Each tool targets a clearly distinct concern: connection status, arbitrary plugin execution, structural metadata, design tokens, search, change tracking, history, library access, export, and image placement. Even the overlap between metadata and find is resolved by their descriptions: metadata dumps subtree structure, find locates nodes by criteria.
All tools share the figma_ prefix, which makes the family recognizable. There is slight inconsistency in grammatical form—nouns like figma_metadata and figma_tokens versus verbs like figma_find and figma_place_image—but the pattern is still predictable and readable.
Ten tools is well within the ideal 3–15 range and matches the server's scope: a Figma bridge needs status, inspection, mutation, search, history, assets, and library access. Each tool earns its place without redundancy or bloat.
The surface covers the full workflow: inspect the file, find nodes, read tokens, run arbitrary mutations via figma_eval, track changes, snapshot/undo, import from libraries, export files, and place images. figma_eval acts as a well-documented escape hatch for anything not explicitly exposed, so there are no obvious dead ends.
Related MCP Connectors
The Figma MCP server brings Figma design context directly into your AI workflow.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
MCP server that lets AI assistants use all OneSchema features exposed via the public API.
Related MCP Servers
- AlicenseAqualityDmaintenanceLocal-first MCP server that connects AI coding agents to the currently open Figma file through a local plugin bridge, requiring no Figma API token.8MIT
- FlicenseNot gradedqualityAmaintenanceA local MCP server that gives AI agents live access to open Figma files for design handoff and UX writing without API tokens or rate limits.3-
- AlicenseBqualityBmaintenanceLocal MCP server connecting AI clients to the Figma desktop app for inspecting and editing Figma documents via the Plugin API.1696 npm1MIT
- AlicenseAqualityCmaintenanceFigma MCP server that lets agents execute arbitrary JavaScript inside a running Figma desktop app via a local development plugin, enabling real-time document inspection and manipulation while avoiding REST API rate limits.3MIT