Free, two-way Figma MCP server. Turn designs into framework-aware code, and push code back to the canvas. Works with Claude Code, Cursor, Codex, and any MCP client.
Figwright demonstrates solid tool design with 30 well-documented tools covering a comprehensive design-to-code and code-to-design workflow. Most tools (85%) have clear descriptions in the 100-400 character range that explain what they do and when to use them. Input schemas are explicitly defined and visible in source files (packages/shared/src/tool-budgets.ts and individual tool files). However, several baseline issues reduce the score: (1) descriptions for some parameter fields are generic or missing context ('Node ids to screenshot' could be clearer about constraints), (2) error handling descriptions are often implicit rather than explicit in tool docs, (3) output schemas are documented inline but not formally exposed as structured JSON Schema in tool registration, (4) some tools lack explicit guidance on retryability and error recovery patterns. The tool naming is consistent and action-oriented (get_*, set_*, create_*, delete_*, export_*, scan_*, apply_*, etc.), following arcade patterns well. Parameter constraints exist (e.g., scale 0.5-4, enums for field types) but are not always mentioned in descriptions. Response shaping is reasonable, tools return structured objects with typed fields, but some tools return complex nested structures (e.g., DesignContextResult) without clear documentation of the schema shape. Tools like 'batch' and 'design_diff' show good composition and support agent planning, but missing explicit error guidance for common failure modes (e.g., 'file_not_found', 'already_in_use' are mentioned only in use_file, not broadly adopted).
Scan the project's tech stack and infer styling (Tailwind, utility-first, etc.), component library structure, framework, CSS format, build system, and package manager. Returns { styling, components, framework, cssFormat, build, packageManager }. Driven by scanning node_modules, tsconfig, package.json, vite.config.ts, and CSS files — no Figma involvement.
Apply a Figma Motion animation-style preset to a node (get styleIds from get_motion_styles). config tunes it: duration (seconds), timelineOffset (seconds — the lever for staggered entrances: give each node index * step), and preset-specific props. Returns { ok, nodeId, appliedStyleId } — keep appliedStyleId to remove exactly this instance later. To stagger a whole row in one atomic, undoable call, drive N apply_animation_style ops through `batch` with increasing timelineOffset. Motion is a Figma-Design-only beta feature.
Set a hand-authored Figma Motion keyframe track on a node for one field — e.g. TRANSLATION_X, OPACITY, ROTATION, SCALE_XY, or an indexed fills / strokes / effects item. `field` selects what to animate; `track` carries an optional baseValue plus keyframes (each with timelinePosition in seconds, a typed value, and optional easing). Replaces any existing track on that field. Returns { ok, nodeId }. Motion is a Figma-Design-only beta feature.
Bind a shared style to a node. `field` selects which slot the style applies to: fill / stroke / effect / grid / text. Returns { ok, nodeId }.
Output schemas for complex return types (DesignContextResult, SerializedNode, etc.) are not formally documented in tool registration. LLMs cannot plan downstream operations without knowing the structure of returned objects.
Error handling patterns are inconsistent. use_file and release_file document specific error codes (file_not_found, already_in_use), but most other tools lack explicit error guidance. LLMs cannot determine recovery actions without documented error cases.
Parameter descriptions for file paths (dirPath, filePath, rootDir) lack explicit format constraints. Descriptions should state 'must be absolute path' or 'relative to project root' in every case, not just some.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 59 | 2026-07-28+ | v2 |
Execute multiple operations atomically in a single undo/redo step. ops is an array of { tool, params } objects — each tool is resolved from the registry and called with params as-is. Returns { ok, results: [{ ok, ...result or error }, ...] }. If any op fails, the entire batch is rolled back. A batch is atomic: either every op succeeds and the document advances one undo step, or every op rolls back and the document is unchanged. The inverse of batch is the single undo entry it creates.
Map Figma components to project components by name and propTypes. Calls get_design_context and analyzes the document's component tree, then matches each Figma component to a project component by inspecting their argument names, types, and defaults. Returns { matches: [{ figmaComponentId, figmaComponentName, projectComponentPath, projectComponentName, propMatches: [{ figmaProp, projectProp, match }, ...] }, ...], unmatched: [{ figmaComponentId, figmaComponentName, reason }, ...] }. unmatchedReasons help the LLM guide the user.
Create a new node (frame, shape, text, component, etc.) in the document or under a parent. Returns { ok, nodeId }. Accepts: type, name, parent (optional nodeId), properties (fill, stroke, text, size, position, etc.). Changes are not immediately persisted — the plugin queues them and batches persists on a timer.
Delete a node by id. Returns { ok, nodeId }. Changes are not immediately persisted — the plugin queues the delete and batches persists on a timer.
Compute the delta between the live Figma document and a snapshot (from a prior design_context call). Highlights what changed: new nodes, deleted nodes, moved nodes (path change), property updates (fill color, text, size, etc.). Returns { changes: [{ type, nodeId, nodeName, details }, ...] }. Useful for tracking incremental design work — the LLM can see what the designer just changed and adapt code accordingly.
Export one or more nodes as a single PDF file. Returns { ok, exportedPath, bytes } on success, or { ok: false, error } on failure. The PDF is saved to projectRoot / figwright / exports / {timestamp}.pdf by default; override with filePath (must be absolute and end with .pdf).
Export one or more nodes with Motion animations as an MP4 video. Returns { ok, exportedPath, bytes } on success, or { ok: false, error } on failure. The video is saved to projectRoot / figwright / exports / {timestamp}.mp4 by default; override with filePath (must be absolute and end with .mp4). Motion is a Figma-Design-only beta feature.
Fetch the full design context: every node in the document (or subtree if you specify a rootNodeId), their properties (fill, stroke, size, text, effects, etc.), and Figma variables (color, spacing, etc.). Returns a DesignContextResult: { nodes: {...}, globalVars: {...}, localVars: {...}, components: {...}, styles: {...}, projectTokens: {...} }. Nodes are the centerpiece: a recursive map keyed by nodeId, each entry holding type, name, properties, children (recursive), and bounds. Each property (fill, stroke, etc.) carries enough info to regenerate the value in code (hex color, rgba, gradient, image url, etc.). Called on every design-to-code pass; pair with token_map (call it first) to annotate raw colors with their upstream project tokens. Returns at most 100 nodes by default (set maxNodes higher for big exports); maxDepth caps recursion. If budget is true, the call carries a stricter size cap (the Figma-side payload limit) and omits some heavy properties to fit.
Fetch metadata about the Figma document: name, version, last edit time. Returns { name, version, editedTime }.
Fetch all Motion animation-style presets in the document. Returns { styles: [{ styleId, name, preset }, ...] }. Motion is a Figma-Design-only beta feature.
Fetch a single node and its full subtree by id. Returns a SerializedNode: { id, type, name, properties (fill, stroke, size, text, etc.), children (recursive), ...}. Properties carry enough info to regenerate the node in code. No maxNodes / maxDepth limits (the entire subtree is always returned); a huge tree may time out, but it won't be truncated.
Fetch a lightweight version of multiple nodes (no children, no text runs): just type, name, properties, and bounds. Returns { nodes: [{ id, type, name, properties, bounds }, ...] }. Faster than get_node for large selections when you don't need the full tree.
Fetch a rasterized screenshot of one node as a PNG, base64-encoded and inlined as an image content block. Suitable for vision-model analysis. If called with forVision: true (which the server sets automatically when inlining for Claude), the sandbox caps an oversized scale to what a vision model can resolve. Ignores forVision when serving save_screenshots (those bytes go to disk and keep the caller's scale). Returns { ok, base64, width, height, mimeType }.
Map every icon component in Figma to a project icon library, matching by name. Returns { matches: [{ figmaComponentId, figmaComponentName, iconPath, iconSource (optional) }, ...], unmatched: [{ figmaComponentId, figmaComponentName, suggestions: [...] }, ...] }. unmatched entries carry suggestions: alternative names that nearly matched (Figma component 'ChevronDown' suggests 'ChevronDownIcon' if that file exists). Driven by a project scan (scan_components) and design_context.
List all files (nodes with type FILE) across every connected plugin session. Each entry carries { fileId, fileName, inUse }. inUse is true iff one session claims that file (use_file call); at most one session can claim any one file at a time. Call this before use_file to find a file, or to poll a file's claim status (is the LLM using it, or is it free?).
Election and build status across the Figwright server pool. Returns { ok, election { leaderId, leaderVersion, leaderBuild, state, votesForLeader }, server { version, build, port } }. The pool's single leader is the only server that plugin windows route to; if leadership flips (new leader starts, old leader crashes, network glitch heals) the plugin reconnects. A client who sees election.state !== 'leader' should retry the call later — you may be talking to a follower, which routes to the leader under the hood but hands back its own (stale) election view.
Release the claimed file (from use_file) immediately. The session reverts to document-root routing and closes the file (undo history is preserved; unsaved changes are discarded). Returns { ok }.
Remove an applied Motion animation-style instance from a node. appliedStyleId is the value returned by apply_animation_style. Returns { ok, nodeId }. Motion is a Figma-Design-only beta feature.
Export every image fill in the given nodes to disk as .png files. Walks the tree and exports fills of type 'IMAGE'. Saves to projectRoot / figwright / image-fills / {imageHash}.png; override with dirPath. Returns { images: [{ hash, exportedPath, bytes }, ...], errors: [...] }. Aborts the batch if dirPath doesn't exist or isn't writable.
Snapshot one or more nodes to disk as .png files. Saves to projectRoot / figwright / screenshots / {nodeId}.png by default; override with dirPath. Every successful export appends { nodeId, exportedPath, bytes } to the result. On error, returns a mix of successes and an error entry { nodeId, error }. Aborts the batch if dirPath doesn't exist or isn't writable.
Scan the project's component library (TypeScript/TSX and Vue files) and extract the shape of every exported component: name, props (argument names, types, defaults), and JSDoc comment. Returns { components: [{ filePath, name, props: [{ name, type, default, doc }], doc }, ...] }.
Walk every node in the document (or subtree) and collect all nodes matching one or more types. Returns { nodes: [{ nodeId, nodeName, type }, ...] } — a lightweight list, no properties. Useful for finding all frames, all text nodes, all components, etc., without fetching the full tree.
Walk every text node in the document (or subtree) and extract text content, font name, size, line height, and styling metadata. Returns { nodes: [{ nodeId, nodeName, text, fontFamily, fontSize, lineHeight, fontWeight, fontStyle, textDecoration, textTransform, letterSpacing, paragraphIndent }, ...] }. Useful for auditing typography or bulk-editing text.
Update properties on a single node: fill (color or gradient), stroke, corner radius, rotation, opacity, text content or style, size, position, constraints, visibility, effects, etc. Returns { ok, nodeId }. Changes are not immediately persisted — the plugin queues them and batches persists on a timer (usually ~100ms). If you need synchronous writes or want to undo as a group, use batch instead.
Map every design token (color, spacing, etc.) in the project to every Figma variable that has a matching name and value, yielding a two-way binding: design_context's globalVars carry variable.value (the resolved Figma value); token_map returns the upstream project token each value came from. Call this once per session before design_context to drive annotations: design_context then includes projectTokens (every color in the payload keyed to its upstream token, where variables can't reach). Returns { tokens: [{ name, value, from (optional), type }, ...], unmatched: [{ name, value, from (optional) }, ...] }.
Claim a file (node with type FILE) in a plugin session and prepare it to receive ops: load the file, open it in the editor, and hold the claim until release. While claimed, every tool call (batch, design_diff, get_node, etc.) targets this file instead of the document root. release_file() returns the claim immediately; on disconnection or MCP shutdown, the claim is released automatically. Returns { ok, fileId, fileName } on success, or { ok: false, error } on failure. The error may be 'file_not_found' (no file with this id exists in any session), 'already_in_use' (another session claimed it), or 'load_failed' (the file couldn't open — very rare, usually a permission or corruption issue). use_file is a session-pinning operation: every sub-call of a multi-call tool routes to the exact session it claims, so they can't drift if routing flips mid-flight.
Tools like get_design_context expose budget flags (_budget) and internal tuning parameters (forVision) that are marked 'internal flag: set by the server automatically'. These should be hidden from the schema or removed entirely from the public interface.
Idempotency is not documented for tools that write to disk (save_screenshots, save_image_fills, export_pdf, export_video). If called twice with the same parameters, do they overwrite or error? This is critical for agent retry loops.
Token limit guidance is missing. get_design_context can return up to 100 nodes by default (configurable to maxNodes). LLMs need explicit guidance on when to reduce maxNodes to avoid context window exhaustion.
Several tools return nested structures with many optional fields (e.g., SerializedNode.properties contains fill, stroke, size, text, effects, etc., not all documented). Response shaping could omit irrelevant fields and strip verbose API metadata.