Repository navigation
Releases
v0.41.0 — default port 3939 + easier install
Changes
Default port: 3000 → 3939
The server's default port moved from 3000 to 3939 to avoid collisions with common dev servers and, notably, the official Figma Dev Mode MCP (3845) this tool is designed to run alongside. PORT still overrides at runtime.
⚠️ Re-install the plugin. The Figma plugin'sallowedDomainschanged, so the Figma Community plugin has been re-published for this release — end users get it automatically, but the server (npm/.mcpb) and the plugin must both be on 3939 or the connection won't establish.
Easier install
Quick start now leads with the Claude Desktop one-click (.mcpb) install before the Claude Code / SSE-url config — the simplest path for most users.
CI
.mcpbis now auto-attached to every release by a GitHub Actions workflow (previously only v0.33.0 had it).- Workflow runner bumped to Node 24.
Install: download figma-prototype-mcp.mcpb below and double-click, or see the README. 24 tools.
v0.40.0 — proto_media (media playback control)
Feature
proto_media (24th tool) + low-level media action — wire a reaction that controls media playback (video/GIF fill) when the source is triggered, via Figma's UPDATE_MEDIA_RUNTIME action. This closes the last buildable interaction-surface gap — every other unimplemented Figma action is platform-blocked.
proto_media({ medias: [{ from, action, target, amountToSkip?, newTimestamp?, trigger? }], replaceExisting? })
- 9 actions, 3 shape-variants:
PLAY/PAUSE/TOGGLE_PLAY_PAUSE/MUTE/UNMUTE/TOGGLE_MUTE_UNMUTE(no param);SKIP_FORWARD/SKIP_BACKWARD(amountToSkip, seconds);SKIP_TO(newTimestamp, seconds). targetis REQUIRED and must be a different, valid media node — Figma rejects a null/self destination at write time. No self-target (controller and media node are distinct).- No motion (media control has no transition). Top-level only (not inside
proto_conditionalbranches). - Read + codegen: decoded by
list_reactions/get_prototype_flow/export_interactions;generate_interaction_codeemits a guide stub across all 5 targets (React / React Native / SwiftUI / Compose / Flutter). - Live-probed PASS on a real video node: targeted 3-shape + echo round-trip ✅; units confirmed = seconds (
amountToSkip:5→"Jump (+5s)",newTimestamp:30→"00:30.00"). Live finding:destinationId:null/self is rejected ("Invalid format") despite the typings declaringstring | null→targetmade required. 731 tests. - Plugin changed → this version requires the Figma Community plugin to be re-published to use it via the Community-installed plugin.
v0.39.0 — proto_set_variable_mode (switch variable collection mode)
Feature
proto_set_variable_mode (23rd tool) — wire a reaction that switches a variable collection to a named mode (e.g. Light→Dark theme, density) when the source is triggered, via Figma's SET_VARIABLE_MODE action. The common "tap a toggle → whole screen goes Dark" prototype.
proto_set_variable_mode({ modes: [{ from, collection?, mode, trigger? }], replaceExisting? })
- Resolve by name:
modeis the mode name (e.g."Dark");collectionis optional and only needed to disambiguate when more than one collection has a mode of that name. An ambiguous mode returns a candidate list (never a silent first-pick); a missing mode lists the available ones. - Discovery:
list_variablesnow returns acollectionsarray (each local collection's name + modes, withisDefault) — call it first to see what modes exist. (Additive; existing fields unchanged.) - No motion (a mode switch has no transition). Local collections only; conditional then/else branch deferred.
- Pure
mode-catalogresolver (unit-tested); 701 tests; live-probed PASS on a real file (Figma accepts the action; mode-only resolve + echo + not-found verified). - Plugin changed → this version requires the Figma Community plugin to be re-published to use it via the Community-installed plugin.
v0.38.0 — includeElements: richer get_canvas_overview
Feature
get_canvas_overview({ includeElements: true }) — opt-in flag that returns each screen's wireable inner elements, collapsing the abstract-request find_nodes-per-screen round-trips into a single call. (Round-trip-reduction lever #3, after node-name acceptance and orient-skip steering.)
Each frame gains:
elements: [{ id, name, type, hasReactions }]elementCount(true pre-cap total) +elementsTruncated
Filter — "interactive-looking" (name-agnostic): a node is included iff it has reactions, or is a component INSTANCE, or is a non-default-named FRAME/GROUP/COMPONENT. Pure visuals (TEXT/RECTANGLE/VECTOR/…) and the screen frame itself are excluded. Traversal stops descending into a matched node (a button is one entry, not its internals); nested matches under non-matching containers are still found. Per-screen cap = 20 with elementsTruncated (no silent caps).
- Backward compatible / additive only — with the flag absent/false the response is byte-for-byte the prior shape (the lean default is preserved).
- Pure
collectWireableElementshelper (35 unit tests); 691 tests total; live-probed PASS on a real file. - Plugin changed → this version requires the Figma Community plugin to be re-published to use it via the Community-installed plugin.
v0.37.1 — proto_scroll SCROLL_TO transition fix
Fix
proto_scroll now emits a Figma-valid transition on SCROLL_TO actions.
Figma's runtime accepts only INSTANT or SCROLL_ANIMATE on a SCROLL_TO action — SMART_ANIMATE / DISSOLVE / directional transitions are all rejected ("Reaction at index 0 was invalid"). Since proto_scroll's default motion is M3_EMPHASIZED (= SMART_ANIMATE) and all 10 motion presets are SMART_ANIMATE, every proto_scroll call that didn't explicitly pass INSTANT failed at the plugin. Latent since proto_scroll shipped (v0.19.0); surfaced by the first live probe of the default-motion path.
compileProtoScroll now rewrites any animated transition to SCROLL_ANIMATE (mirroring the existing overlay rewrite), preserving easing + duration so the designer's M3/HIG curve survives. INSTANT and SCROLL_ANIMATE pass through unchanged.
- Server-only change — no plugin change, no Figma Community re-publish needed.
- Conditional-branch SCROLL_TO is unaffected (Figma accepts SMART_ANIMATE there; verified separately).
- Live-verified on a real Figma file; 676 tests green.
v0.37.0 — validate_prototype (prototype flow lint)
validate_prototype — static prototype flow lint (22nd tool)
A new server-only read/lint tool that checks a whole page's prototype flow in one call and reports problems. Built on the same GET_PROTOTYPE_FLOW + pure-transform pattern as export_interactions; the plugin is unchanged (no Figma Community re-publish needed).
Four rules
broken-reference(error) — a navigate/overlay/swap points to a frame not on this page (incl. a missing/null destination).unreachable(error) — a frame no start frame can reach via BFS (skipped when no start frame is set).dead-end(warning) — a frame with no outgoing screen-changing action; may be a final screen.start-frame(warning) — zero or multiple start frames (multiple is allowed in Figma for multi-flow pages).
Recurses into conditional then/else; excludes scrollTo and changeVariant from frame-edge logic. Returns a flat report: { ok (errors===0), page, issues[], summary{errors,warnings,frames,interactions}, truncated }.
Notes
- Pure
analyzeFlowinsrc/server/validate-flow.ts(unit-tested); 667 tests total. - Live-probed on a real file — caught a genuine bug (a conditional login button whose
thenbranch navigated to a null destination), 0 API surprises. - Server-only: npm + GitHub only. No Figma Community re-publish required.
v0.36.0 — overlay codegen + codegen finish
Codegen-focused release. All targets (React / React Native / SwiftUI / Compose / Flutter) now produce meaningful output for every interaction type.
Overlays as first-class modals
generate_interaction_code reads each overlay's Figma metadata (overlayPositionType / overlayBackground / overlayBackgroundInteraction) and emits a real modal presentation instead of a plain navigate:
- Router/store gain
presentOverlay/dismissOverlay(1급 presentation, separate from the nav stack) CENTER→ dialog,BOTTOM_*→ sheet; scrim + tap-outside-to-dismiss honored- SwiftUI
.sheet/.fullScreenCover, ComposeModalBottomSheet/Dialog, FluttershowModalBottomSheet/showDialog, React/RN modal state - Verified against a real Figma file.
Codegen finish
- scrollTo: guide stubs that name the target node + the exact framework scroll API (was a vague "no equivalent"). React's scrollTo no longer wrongly emits a route navigation.
- openUrl: now real on every target — Compose via
router.onOpenUri, Flutter vialaunchUrl(conditionalurl_launcherimport). - changeVariant: guide stubs naming the target variant + "set your component's variant prop/state".
- Reserved-word guard: SwiftUI/Compose/Flutter identifiers that collide with a language keyword (e.g. a frame named
Switch/Class) get a trailing_; Dart also guardsvalues/index. - Kotlin
$escaping: Compose string literals (URLs, variable values) escape$so Kotlin doesn't string-interpolate them.
Notes
- The plugin now reads overlay destination-frame properties, so the Figma Community plugin is re-published for this release.
651 tests.
v0.35.0 — multitarget codegen + name acceptance + steering
Highlights
Multi-target interaction codegen
generate_interaction_code now emits 5 frameworks (was React-only). Pick target:
react— react-router + framer-motionreact-native— @react-navigationswiftui— NavigationStack Router + ObservableObject storecompose— Jetpack Compose: Navigation Compose Router + ViewModel store (new)flutter— GoRouterProtoRouter+ ChangeNotifier store (new)
Each target emits the same 4-file set (navigation Router + variable store + per-screen interaction handlers + README). It generates the interaction layer only (navigation, variables, conditionals) — not screen UI. Generated code is written to compile: navigation to a screen outside the graph is commented out, and handler names are sanitized to valid identifiers per language.
proto_* accept node names
proto_wire (from/to) and proto_back/proto_url/proto_overlay/proto_scroll now accept a node name (not just an ID), with an optional fromScreen to scope the lookup. Fewer round-trips: wire directly from named nodes.
Steering: wire directly from named nodes
proto_* descriptions now steer the agent to wire straight from named nodes; get_canvas_overview is softened to optional orientation rather than a required first call.
Fixes
get_canvas_overviewnow includes frames nested inside Sections (previously missed).
Server-only changes plus a plugin update (name resolution lives in the plugin). 616 tests.
v0.34.0 — create_variable + generate_interaction_code
New tools
create_variable — find-or-create a Figma Variable
Create only the variables a prototype actually needs. Reuses an existing same-named variable (local or library) if present; only creates when genuinely missing. New variables land in a dedicated forProto collection by default. type (BOOLEAN/FLOAT/STRING/COLOR) is explicit; value optional. Complements the existing set/toggle/conditional variable tools — closes the create→set→condition loop.
generate_interaction_code — deterministic React codegen (MVP)
Turns a prototype's wired interactions into a structured set of React files: routes.tsx (react-router), prototype-store.tsx (React Context for variables), per-screen interaction hooks, and a README. Built on the same canonical spec as export_interactions. Emits the interaction layer (routing / state / transitions / handlers) — not screen UI — so it complements design→UI codegen. Screen identity is keyed by frame ID, so duplicate frame names don't collide. Extensible emitter registry (more frameworks later).
Notes
- Server-only change for codegen + a plugin-side variable creation path; 549 tests; live-validated end-to-end on a real Figma file.
npx figma-prototype-mcp@0.34.0
🤖 Generated with Claude Code
v0.33.0 — Claude Desktop extension (.mcpb / one-click install)
What's new
Ship a Claude Desktop extension (figma-prototype-mcp.mcpb, attached) so you can install the server without a terminal or JSON config.
Install:
- Most accounts: download
figma-prototype-mcp.mcpbbelow and double-click → Claude Desktop installs & auto-runs the server. - Org-managed Claude Desktop (only registry extensions allowed): Settings → Extensions → Extension Developer → Load unpacked, pointing at the unzipped
.mcpbfolder (or clone +npm run build:dxtand load thedxt/folder).
You still install the Figma Community plugin and run it (it connects to localhost:3000).
Under the hood:
- Self-contained bundle (
dist/dxt/index.js, deps bundled,createRequireshim) — nonpm installon the user's machine. - Built-injected version + robust fallback so a packed bundle never crashes reading
package.json. dxt/manifest.json(mcpb v2,manifest_version 0.2) packed via@anthropic-ai/mcpb;versionhook keeps the manifest in sync.- The default SSE/
npxand--stdiopaths are unchanged. 522 tests.
Live-verified: loaded (unpacked) in an org-managed Claude Desktop, extension server v0.33.0 ran, and a proto_set_variable call round-tripped successfully.