Skip to main content
Glama
figma-jev-console

figma-jev-console

by anuragfolio

Figma JEV Console MCP

MCP License: MIT Node.js Figma plugin Claude Code plugin Fork of Figma Console MCP Local model GitHub stars

A live, two-way bridge between your running app and Figma. Push the screens of a coded app into Figma as auto-layout frames. After that, a change you make in Figma is written into the code about 150 ms later, and a change in the code updates the same Figma frames in place.

Figma JEV Console MCP is a fork of Figma Console MCP by Southleft. It keeps the parts that made that project work well: an MCP server, a Figma Desktop plugin that talks to it over a local WebSocket, a stable plugin install path, and an escape hatch for raw Plugin API code. It adds one thing the original does not do: it treats the app's code and the Figma file as one design, and keeps them in sync while you work.

What is this?

It connects Claude (or any MCP client) to Figma and to your app's source code:

  • 📥 Code → Figma push. Each route of a running app is rendered in headless Chrome and rebuilt in Figma: every container is an auto-layout frame, sizes are hug / fill / fixed as the CSS says, and coded components become Figma components with variants.

  • ✏️ Figma → code, live. Change padding, gap, direction, alignment, sizing, fills, text, type, radius, borders, shadows or opacity in the Design panel, and the edit lands in the right file and line: a Tailwind class, a CSS declaration, an inline style, a JSX string or a component prop at its usage site. Your dev server's hot reload shows it.

  • 🔁 Code → Figma, live. Edit the code (you or Claude) and the Figma screens update in place. Layer ids, comments, prototype links and layers the designer added survive.

  • 🧱 New layers become code. Draw a frame, text, shape, vector or instance inside a pushed screen and it is written into the code; a component made in Figma gets its own file. Deleting, reordering and moving linked layers is written too.

  • 💸 No Claude tokens for small edits. Rules write the edits. A small local model (Kev-0.8B, runs on your Mac) only settles ties, such as two code lines that match a layer equally well.

  • 🤖 Fine-tune with LLM. Anything the rules cannot write is collected in a changelog. One button in the plugin hands it to the Claude session running the server, and the plugin shows Claude's progress.

  • 🎨 Design-system build skill. figma-system-build builds or tidies screens in Figma with every color, text style, spacing, radius and icon bound to variables.

Related MCP server: Sunnyside Figma MCP

How it differs from Figma Console MCP

Figma Console MCP

Figma JEV Console MCP

Main job

Give AI full access to Figma: extraction, creation, tokens, audits, debugging

Keep a running app and its Figma screens in sync, both ways

Source of truth

The Figma file

The app's code and the Figma screens, kept equal

Code → Figma

Tokens and components via tools

Whole screens, rendered from the running app, as auto-layout frames

Figma → code

Token export and sync

Every Design-panel edit written to the exact source line, live

Figma plugin

Figma Desktop Bridge, ports 9223–9232

Jev Bridge, ports 9340–9344

Stable plugin path

~/.figma-console-mcp/plugin/

~/.figma-jev-console/plugin/

Models

None needed

Optional local tie-breaker (Kev-0.8B, MLX); Claude only on request

Tool count

120+

12, focused on sync

The two run side by side without conflict: different plugin, ports, server name and skills. Use Figma Console MCP to explore a file, manage variables or audit a design system; use Figma JEV Console MCP when the screens already exist as code and you want to design on top of them.

✅ Works with

Status

Vite + React + Tailwind CSS v3/v4

Tested (end-to-end test suite)

Plain HTML + CSS

Tested (end-to-end test suite)

Vite + vanilla JS with CSS files

Used on a real app

Next.js, Remix, Vue, Svelte, Angular

Not tested yet. The capture works on any page Chrome can render; writing edits back relies on finding the element in the source, which should work for JSX and HTML-like templates but is unproven. Reports welcome.

CSS-in-JS (styled-components, Emotion), CSS Modules

Not tested. Edits the rules cannot place go to the changelog.

macOS (Apple Silicon)

Tested, including the local model

macOS (Intel), Windows, Linux

Server not tested; the local model does not run there (rules only).


⚡ Quick start

Prerequisites

  • Figma Desktop (the plugin needs it; the web app cannot reach localhost)

  • Node.js 20+, Google Chrome and git

  • Claude Code, or another AI coding assistant with MCP support (Cursor, Windsurf, Claude Desktop)

  • For the local model: a Mac with Apple Silicon, about 2 GB of disk and 1.5 GB of free memory. Without it, everything still works on rules; ties wait for your review instead.

Install with one prompt

Paste this into Claude Code (or your AI coding assistant). It installs everything and hands you the file to load in Figma.

Install Figma JEV Console MCP for me (https://github.com/anuragfolio/figma-jev-console-mcp).

1. Check that Node.js 20 or newer, git and Google Chrome are installed. If one is missing,
   stop and tell me how to install it.
2. Clone the repository into ~/figma-jev-console-mcp (if the folder exists, run git pull
   there instead), then run npm install in it.
3. Register the MCP server under the name figma-jev-console so it starts with my AI client.
   In Claude Code run:
   claude mcp add figma-jev-console -s user -- node ~/figma-jev-console-mcp/bin/figma-jev-console-mcp.js
   (use the full path of my home folder). For another client, add the same command to its MCP
   config file.
4. If you are Claude Code, copy the folders in ~/figma-jev-console-mcp/skills/ into
   ~/.claude/skills/.
5. Run: node ~/figma-jev-console-mcp/bin/figma-jev-console-mcp.js --setup
   It prints the path of the Figma plugin's manifest.json. Show me that full path, and open
   the folder that contains it (macOS: open -R <path>, Windows: explorer /select,<path>).
6. Tell me the last steps: in Figma Desktop choose Plugins → Development → Import plugin
   from manifest…, pick that manifest.json, then run Plugins → Development → Jev Bridge.
   Restart the AI client so it loads the new MCP server.

The plugin's manifest.json lives at ~/.figma-jev-console/plugin/manifest.json. The server refreshes that copy every time it starts, so the path never changes.

Not on npm yet. Clone and install:

git clone https://github.com/anuragfolio/figma-jev-console-mcp.git ~/figma-jev-console-mcp
cd ~/figma-jev-console-mcp && npm install

Add it to Claude Code, either as a Claude Code plugin (MCP server + both skills):

claude plugin marketplace add anuragfolio/figma-jev-console-mcp
claude plugin install figma-jev-console@figma-jev-console

or as an MCP server only:

claude mcp add figma-jev-console -s user -- node ~/figma-jev-console-mcp/bin/figma-jev-console-mcp.js

Cursor / Windsurf / Claude Desktop: add to your MCP config file:

{
  "mcpServers": {
    "figma-jev-console": {
      "command": "node",
      "args": ["/Users/you/figma-jev-console-mcp/bin/figma-jev-console-mcp.js"]
    }
  }
}

Write the Figma plugin files and print the manifest path:

node ~/figma-jev-console-mcp/bin/figma-jev-console-mcp.js --setup

In Figma Desktop: Plugins → Development → Import plugin from manifest…, pick that manifest.json, then run Plugins → Development → Jev Bridge. It finds the server on ports 9340–9344.

Plugin updates. Figma caches plugin files. After updating (git pull), close and reopen Jev Bridge to load the new code.js and ui.html. The panel says so when its version is behind the server's.

Local model (optional, Apple Silicon)

The first time Jev Bridge opens, it offers to install Kev-0.8B (about 1.7 GB) next to the manifest, with a progress bar and log. After that the normal panel appears. The model starts only when an edit needs it and stops after 5 idle minutes.

Try it

Start your app's dev server, then ask Claude:

Push this app to Figma

→ The screens appear on the current page. Change a padding or a text in Figma and watch the app update.


🧭 Using it

The plugin panel

  • Header: the app URL of the selected screen (click to open it), the server port, and an arrow that collapses the panel to just the header.

  • LIVE: on by default. Edits are written about 150 ms after you stop changing a layer; the second line shows the last one, e.g. ✓ text → "Log in" in index.html:11 · 4 ms. Switch it off to pause; paused edits are written when you switch it back on.

  • Local model: its state (off, starting, running) and how much memory it and the Mac use.

  • Screens: every pushed screen whose app is running, with its URL. Click one to jump to it in Figma. While a push or fine-tune runs, this area shows its progress.

  • Fine-tune with LLM: hands the leftovers to Claude (see below).

Live sync

  • Figma → code: every Design-panel field is read and written (jev_contract lists the table). Tailwind v3/v4 utilities, CSS rules, inline styles, JSX text and component props at usage sites are all handled.

  • Code → Figma: a code change redraws only what changed. A layer you are editing, or edited in the last 4 seconds, is left alone.

  • The screen is the truth. Sizes come from layers on the screen (instances in their real parent), never from main components sitting alone in the Components section. Style edits on a main component go to the component's file, so every instance follows.

  • Restarts are safe. Pushed frames store their app URL, project folder and viewport. When the plugin reconnects to a restarted server, live sync resumes by itself. An edit counts as synced only after the server confirms it, so an edit is never lost to a restart.

  • Git is your undo. Edits are written straight into your source files. When sync starts, the panel warns if the project is not a git repository, or has uncommitted changes, so you can commit first and undo any edit with git.

  • Several Claude sessions: each runs its own server. The plugin checks every port and uses the one with a live session, or else the newest.

Fine-tune with LLM

Whatever the rules cannot write goes to a changelog (layer code, Figma node id, file:line hint, reason; jev_changelog). Fine-tune with LLM compares every linked layer with what was last synced, adds the changelog, and sends that brief to the Claude Code session running the server. Only then are Claude tokens spent. Claude reports each step with jev_progress, and the plugin shows those lines; when it calls jev_progress { done: true }, the changelog is cleared and Figma is redrawn from the code.

The hand-off uses Claude Code channels (a research preview). With the server added by claude mcp add, start Claude Code like this:

claude --dangerously-load-development-channels server:figma-jev-console

Without it the brief does not reach Claude, and after 60 seconds the panel says so. JEV_FINE_TUNE=headless uses a separate claude -p run instead.


🧩 Skills

Two skills ship in skills/. The Claude Code plugin installs both; to use them without the plugin, copy the folders into ~/.claude/skills/.

Skill

Use it for

figma-jev-console

Pushing an app to Figma, live sync, and what to do when an edit did not reach the code

figma-system-build

Building or tidying screens in Figma by hand, with every color, text style, spacing, radius and icon bound to variables; Phosphor icon set; a lint pass instead of screenshots

figma-system-build runs its Plugin API scripts through jev_execute (or figma_execute from Figma Console MCP). Pushed screens copy raw values from CSS; binding them to variables with this skill is design work on top, and the sync compares values, so it writes nothing to the code unless a value changes.

🛠️ Tools

Tool

Purpose

jev_status

Plugin connection, model backend, live session

jev_push_screens

Push routes to Figma and start live sync

jev_sync_now

Write queued edits, then refresh screens from code

jev_list_pending / jev_resolve_pending

Edits that need a decision

jev_changelog

What the rules could not write, with a ready prompt

jev_progress

Claude reports fine-tune steps to the plugin

jev_sync_log

Applied edits with file:line and who decided

jev_locate

Layer code → file:line

jev_prefs

What the edit check has learned for this project, and where it is stored

jev_contract

The CSS ↔ Figma property table

jev_stop_live

Stop watching and close the browser

jev_execute

Run raw Figma Plugin API code (escape hatch)


⚙️ How it works

Step

Done by

Render each route in headless Chrome, read layout and computed CSS

code (capture-script.js)

Copy padding, gap, colors, type, radius, borders, shadows into Figma

code (fixed table in properties.js)

Direction (HORIZONTAL / VERTICAL / WRAP), hug / fill / fixed, components

rules from CSS and measured layout

Find the source line or CSS rule for an edited layer

rules; the local model only on a tie

Write the edit (Tailwind class, CSS declaration, inline style, text)

code (edits.js, tailwind.js)

Check the edit in the running app; roll back and try the next place if it missed

code (verify.js)

Anything left over

the changelog, then Claude on request

Every edit is checked. After a Figma edit is written, the screen is reloaded in the headless browser and the element is measured again:

  • Did it land? The element must now show the Figma value (within 0.5 px for lengths, 1/255 per colour channel, exact for text, direction, wrap and alignment).

  • Nothing else changed? No other element's own style may change (padding, colours, type, radius, borders, gap, direction). Elements drawn from the same template that change the same way are fine, and so are position and size changes caused by the edit.

If the check fails, the file is put back exactly as it was (byte for byte) and the next place is tried: another source line, the next class list, a CSS rule, an inline style. At most 4 tries. If none works, the code stays as it started and the changelog gets an item listing every try and why it failed, ready for Fine-tune with LLM.

The first write happens at once, so the app updates as fast as before. The panel's LIVE line then shows "Verifying…", followed by ✓ verified, "Corrected: ", or a note that the edit is in the changelog. Creates, deletes, reorders and moves are not checked yet.

Learned preferences. Each try is recorded: for this component (or page) and property, which kind of write passed or failed. Next time, the kind that works for that component is tried first (score = passes − 2 × fails). It is a lookup table, not machine learning, and the local model is not asked anything new. Two copies are kept, and either one rebuilds the other:

  • ~/.figma-jev-console/prefs/<figma-file>/<project>--<hash>.json

  • <project>/.figma-jev-console/prefs.json (added to .git/info/exclude, never to your .gitignore; the git safety notice does not count it)

jev_prefs shows what was learned and where both copies are; { reset: true } clears them.

Layer names are short codes such as @a3.0.2.1: screen a3, then the element's index among its parent's element children at each DOM level (base 36). :t is the text inside a boxed element. The code is also stored in plugin data, so moving a layer does not change what it points to. Screen frames keep their route name.

Updates are in place. Layers are matched to elements by a content fingerprint (tag, classes, text, component), then by code, then by position. Each update is one undo step.

New layers: a frame becomes a flex div, a text layer a text element, an instance of a pushed component <Button variant="primary">…</Button> with its import, a component made in Figma a new file in src/components/, and a frame wrapped around linked layers (Shift+A) a new wrapper around the existing code.

Deletes, reorders, moves, vectors: a deleted layer's element (or data-list item) is removed; reordered layers move their code blocks; a layer dragged into another linked frame moves its code; vectors become inline <svg>; a variant swap driven by an expression maps to its condition.

🧠 Local model

Ties go to Kev-0.8B (weights), a Jev-compatible decision model running on the Mac with MLX, installed from the plugin's setup screen.

  • Starts on demand (about 8 seconds the first time), stops after 5 idle minutes.

  • Will not start when less than 12% of memory is free, and is stopped if memory stays that low while the Mac starts swapping.

  • Memory use is logged every 10 seconds to ~/.figma-jev-console/sync.log while it runs.

  • Any server that speaks the same /v1/systemone format can replace it (JEV_LOCAL_URL), including TypeSafe's cloud Jev (TYPESAFE_API_KEY, optional).

🔧 Configuration

Set in .env next to package.json, in ~/.figma-jev-console/.env, or in your MCP client's env. Keep keys out of version control.

Variable

Default

Meaning

JEV_PORTS

9340,…,9344

WebSocket ports for Jev Bridge

JEV_MODE

local after the model installs

local, local-first or cloud

JEV_LOCAL_URL

http://127.0.0.1:8009

Local Jev-compatible server

TYPESAFE_API_KEY

none

Optional cloud Jev key

JEV_AUTO_APPLY

0.6

Minimum confidence to write a tie-broken edit without review

JEV_MODEL_IDLE_MIN

5

Minutes before the idle local model stops

JEV_MIN_FREE_PCT

12

Memory floor for starting or keeping the local model

JEV_CHROME_CHANNEL

chrome

Chrome channel for headless capture

JEV_FINE_TUNE

channel

headless runs claude -p instead of the channel hand-off

JEV_PLUGIN_DIR

~/.figma-jev-console/plugin

Where the plugin files are kept

JEV_MODEL_DIR

<plugin dir>/local-model

Where the local model is installed

JEV_VERIFY

on

0 writes edits without checking them in the running app

JEV_HOME

~/.figma-jev-console

Where the log and learned preferences are kept (tests use a temp folder)

🧪 Tests

npm test

Runs every end-to-end scenario: the real MCP server on a test port (JEV_PORTS=9390, so a Jev Bridge open in Figma is not touched), a fake plugin, a push, Figma-style edits, and the resulting source files. One scenario:

FIXTURE=fixture-react SCENARIO=scenario-react.mjs node test/e2e.mjs

test/live-harness.mjs runs a fixture against real Figma.

🩺 Troubleshooting

The panel says "Looking for server…" or "Server offline". The MCP server is not running. It starts with your MCP client: restart Claude Code (or your client) and check that figma-jev-console is listed (claude mcp list). Ports 9340–9344 must be free for at least one server; lsof -i :9340 shows what holds a port.

The plugin connected to the wrong server. Every Claude session starts its own server. The plugin prefers the one with a live session, else the newest, and re-checks every 15 seconds. If it still picks an old one, close the other Claude sessions or reopen the plugin.

"Jev Bridge is out of date". Figma is running a cached copy of the plugin. Close the plugin and open it again (Plugins → Development → Jev Bridge). If the notice stays, re-import ~/.figma-jev-console/plugin/manifest.json.

"Could not start Chrome". Install Google Chrome, or point JEV_CHROME_CHANNEL at another Chromium channel you have (chrome-beta, msedge).

A Figma edit did not reach the code. Ask Claude "why didn't my edit sync?": the skill reads jev_list_pending. Common reasons are a design token (it asks whether to change the token or just this element) or a value built from variables in the code. Anything left over goes to Fine-tune with LLM.

Screens are missing from the panel's list. Only screens whose app answers at their URL are listed. Start the app's dev server; screens pushed by early versions store no URL and stay hidden until you push them again.

Fine-tune with LLM says "No reply from Claude". Claude Code must be started with channels on: claude --dangerously-load-development-channels server:figma-jev-console. Or set JEV_FINE_TUNE=headless to use a separate claude -p run.

The local model does not start. It needs Apple Silicon and at least 12% free memory. The panel shows the reason; the log is ~/.figma-jev-console/sync.log. Start again retries without downloading anything.

An edit was "corrected" or ended in the changelog. Every edit is checked in the running app. "Corrected" means the first place it was written did not show the change, so it was put back and written somewhere else. A changelog item means no place worked, so the code was left unchanged; the item lists each try. The log (~/.figma-jev-console/sync.log) has one verify line per try with the value expected and the value found. JEV_VERIFY=0 turns the check off.

The app shows an edit that Figma no longer matches. Checking reloads the page in a headless browser, not in yours. If your browser tab does not hot-reload, refresh it.

Undo an edit. Figma's undo changes the layer back, and the sync writes that like any other edit. For the code itself, use git (git diff, git checkout -- <file>).

Known limits

  • One viewport per session; hover and focus states, animations and icon fonts are not pushed. ::before / ::after text (such as a placeholder) is drawn but not written back.

  • Showing a hidden data item again, and moves or reorders across files, go to the changelog.

  • Block layouts with uneven margins use the median spacing as the auto-layout gap, so a few pixels can shift.

  • Instances cannot change structure; a component rendered with different children becomes separate variants.

  • The local model needs Apple Silicon.

Credits and license

MIT, see LICENSE. A fork of Figma Console MCP by Southleft and its contributors (MIT). Tie-breaking model: Kev by Jared Palmer. Icons in figma-system-build: Phosphor.

Available Tools

12 tools
jev_changelogB

Figma changes that need an LLM to finish in code (deletes, moves, reorders, expression-driven variants, anything the rules and Jev could not write), with layer codes, Figma node ids, code hints and a ready prompt.

Inputs: no named parameters.

jev_contractA

The CSS ↔ Figma property table the sync supports, and which direction each property syncs.

Inputs: no named parameters.

jev_executeA

Escape hatch: run Figma plugin API code inside the Jev Bridge plugin (async function body with figma in scope; return a JSON-serializable value).

Inputs: code (required).

jev_list_pendingA

Figma edits that were not written to code automatically (Jev was unsure where they go, or a design token is involved), with the reason for each.

Inputs: no named parameters.

jev_locateA

Find the source file and line for a Figma layer code such as @a3.0.2.1.

Inputs: code (required).

jev_progressA

During a Fine-tune (sent to you through the figma-jev-console channel): report a step to the designer's Figma panel. Call with done: true when every item is fixed; Figma is then redrawn from the code and the changelog is cleared.

Inputs: text (required), done.

jev_push_screensA

Push the app's screens into Figma as auto-layout frames and components, then keep them in live two-way sync. The app's dev server must be running. Call this when the user says 'push to Figma'.

Inputs: baseUrl (required), projectDir (required), live, routes, +2 more.

jev_resolve_pendingA

Decide a pending item: 'apply' writes the best guess, 'reject' drops it, 'element' / 'token' choose the scope for a design-token color, 'llm' hands it to the LLM, 'done' marks it finished after you changed the code yourself (Figma is then redrawn).

Inputs: id (required), action (required).

jev_statusA

Connection status: Jev Bridge plugin in Figma, Jev backend, and the live session (screens, pending reviews).

Inputs: no named parameters.

jev_stop_liveA

Stop the live session: stop watching files and close the headless browser. Figma layers stay.

Inputs: no named parameters.

jev_sync_logB

Recent Figma → code edits: what changed, which file and line, and whether Jev or a rule decided the location.

Inputs: limit.

jev_sync_nowA

One-click sync: write all queued Figma edits into the code, then refresh every screen in Figma from the code.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 12 tools

    Disambiguation2/5

    While each tool has a specific name, several overlap in function and timing: jev_sync_now, jev_push_screens, and jev_resolve_pending all trigger writes to code or Figma, and jev_status, jev_sync_log, jev_progress, jev_changelog, and jev_list_pending all report state, making it hard to select the right one. Descriptions are detailed but the boundaries are blurred.

    Naming Consistency5/5

    All tool names start with 'jev_' followed by a simple verb (status, sync_now, list_pending, changelog, push_screens, resolve_pending, sync_log, locate, progress, contract, stop_live, execute), following a consistent, predictable pattern. All are lowercase with underscores, no mixing of conventions.

    Tool Count5/5

    With 12 tools, the set is well-scoped for the Figma-Jev integration console. Each tool addresses a distinct aspect of the workflow (status, sync, pending, changelog, push, resolve, log, locate, progress, contract, stop, execute), and the count is within the ideal 3-15 range, with no redundancy.

    Completeness3/5

    The set covers core lifecycle operations: status, sync, pending resolution, push, stop, and a progress reporting mechanism. However, there is a notable gap: no explicit tool to start or manage a live session (only stop_live exists), and the fine-tune workflow relies on an external channel rather than a dedicated tool. The escape hatch jev_execute partially mitigates gaps, but the surface feels incomplete for the stated purpose.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers