Skip to content
Son Tran (Snow)
All posts
  • Essay
  • #mcp
  • #figma
  • #open-source

Mirra: a two-way MCP server for Figma

Mirra lets an agent read a Figma or FigJam design and render one back through the Plugin API, with no REST token and no monthly call limit.

Mirra is an open-source MCP server for Figma that works in both directions. An agent can read a design or a FigJam board, and render one back, using the Figma Plugin API. Because it talks to the plugin running inside Figma Desktop, it needs no REST token and has no monthly call limit.

The code is on GitHub under the MIT license.

Mirra reading a design and rendering one back in Figma

Why the Plugin API

Most Figma integrations go through the REST API. That means a token to create and store, and a rate limit to plan around. Mirra skips both by running as a Figma plugin and letting the agent drive it over a local connection.

The trade-off is that Figma Desktop has to be open, with the plugin panel running. For a design tool an agent works alongside, that felt like a fair price.

Try it

npx @ttsalpha/mirra init

Then restart your agent. init adds Mirra to Claude Desktop, Claude Code, Cursor, VS Code and Antigravity, and installs the plugin. It needs Figma Desktop (macOS or Windows) and Node 24+.

In Figma, open a file and run Plugins → Development → Mirra, and keep the panel open. It reads Disconnected until your agent has started Mirra. If it still does not connect, npx @ttsalpha/mirra doctor tells you why.

What the agent can do

Mirra exposes a small set of tools, each doing one thing:

ToolWhat it does
mirra_renderBuild a whole screen, or a FigJam board, from one declarative tree
mirra_readRead the selection, or a node, back as that same tree
mirra_patchChange nodes by key, as one undo step
mirra_findLocate layers by name, kind or key, and select them
mirra_pagesList the pages, add one, or switch to one
mirra_screenshotExport a layer as PNG and hand the image back
mirra_tokensList variables and styles
mirra_define_tokensCreate or update variables, modes, aliases and styles
mirra_export_assetsWrite layers to disk as PNG or SVG

There is also mirra_ping to check the connection, and mirra_exec, which I will come back to.

One tree, both directions

The part I care about most is that mirra_render and mirra_read share a single format. An agent reads a layer as a tree, and can write code from exactly what it read. The same tree goes back in to build or change a design.

The format is flexbox-like. Nodes are frame, text, rect, ellipse, instance, svg and image. Sizes are a number, "fill" or "hug", and a "$name" binds a field to a variable.

{
  "type": "frame",
  "key": "card",
  "layout": { "dir": "vertical", "gap": 12, "pad": 16 },
  "fill": "#ffffff",
  "radius": 12,
  "children": [
    { "type": "text", "text": "Welcome back", "size": 24 },
    { "type": "rect", "w": "fill", "h": 1, "fill": "#eeeeee" }
  ]
}

A key on a node lets mirra_patch address it later, so an agent can adjust one part of a screen without rebuilding it.

FigJam too

FigJam boards use the same idea with different nodes: section, sticky, shape, connector, table, code, link and gif. A board is one section, since FigJam has no frames, auto layout or variables. A connector joins nodes by key and routes itself.

How it works

Agent ⇄ stdio ⇄ MCP server ⇄ ws://localhost:51730 ⇄ plugin ⇄ Figma

The agent talks MCP over stdio to the server. The server talks to the plugin over a WebSocket on loopback, and the plugin calls the Plugin API inside Figma.

Running arbitrary code

mirra_exec runs any Plugin API code the agent writes, so it is off by default. Turning it on takes two separate switches: MIRRA_ALLOW_EXEC on the server, and Allow code execution ticked in the panel, which resets on every run.

The socket only listens on loopback and rejects browser origins other than Figma's. It does not authenticate against other processes running as you while the plugin is open, which is worth knowing before you leave the panel running.

Under the hood

The repo is a small monorepo: packages/schema holds the tree and the wire protocol, packages/plugin the Figma side, and packages/server the MCP server and CLI. Tests run against a fake of the Figma sandbox, so they do not need Figma open. The fake does not reproduce FigJam's quirks, so FigJam changes still need a try in a real FigJam file.


If you try it, the issues are the best place to tell me what breaks.