Figma CodeKit — Pixel-Verified Figma to Code
Converts Figma frames into self-contained HTML and verifies generated code against the design by rendering it in Chromium, pixel-diffing it with the Figma reference, and measuring element bounding boxes to report missing or misplaced elements.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Figma CodeKit — Pixel-Verified Figma to CodeConvert this Figma frame and verify it until it converges"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
figma-codekit-mcp
Figma → code that is actually checked against the design. The pixel-diff verifier for Figma-to-code: it renders the output and diffs it against the frame until it converges — and it can verify your own running app the same way, so it works alongside Figma's official MCP server or Framelink rather than replacing them.
Most Figma-to-code tools stop at the moment of generation: they hand you JSX and walk away. Nobody ever renders the output and compares it to the frame, so the result stalls at "90% there" and you spend the afternoon nudging padding by eye.
This MCP server closes that loop. It converts a frame to self-contained HTML deterministically (no LLM, no hallucinated layout), then renders your code in Chromium, pixel-diffs it against the Figma reference, measures every element's box, and tells the agent exactly what is missing or misplaced. The agent edits, calls verify again, and repeats until the diff is at the anti-aliasing floor.
Those are real artifacts from npm run example, which runs the loop end to end
with no API key. The generate callback in that demo is a scripted stand-in
for an LLM, so it shows the loop mechanics honestly — the measuring is real, the
"model" is not.
Why the verify step matters
A Figma node tree tells you what the designer declared. It does not tell you what a browser will do with your CSS. Those diverge constantly — a flex gap that collapses, a font that falls back, an absolute child that escapes its parent. The only way to know is to render it and look.
figma_verify gives the agent three independent signals per pass:
Pixel diff — the overall mismatch ratio plus the worst regions, so it knows how wrong and where.
Element bounding-box IoU — every IR node is tagged
data-ir-id, measured in the live DOM, and matched against its Figma box. This is what turns "the bottom-left looks off" into "the CTA button is missing" or "the price label is 40px too low".Paint order and clipping — Figma lists children back-to-front, so a node's pre-order index is its paint order;
elementsFromPointover a sample grid gives the rendered order wherever two elements actually overlap. This catches the class of bug the other two signals are blind to by construction: elements present, in exactly the right box, stacked the wrong way round — or cut off by an ancestor'soverflowwhere the design lets them overhang. Neither moves a box, and on a tall frame neither moves the diff ratio much, but a decorative overlay drawn over a photo instead of under it is the first thing a person sees. Reported asWRONG STACKING/OVER-CLIPPED, and reported even when the pixel diff has converged.
Related MCP server: figmad-mcp
Tools
Tool | What it does | Network |
| Fetch a frame → HTML + | Figma API (cached) |
| Render HTML in Chromium, pixel-diff vs the reference, IoU-check every element, check paint order and clipping, return targeted fix instructions. Pass | none (or your dev server) |
| Print the IR as an indented outline (role, box, layout, text, tokens), filterable — read the structure without dumping raw Figma JSON into context. | none |
Responsive output depends on what the design actually declares
responsive: true reads Figma's sizing intent — FILL becomes flex/100%, HUG becomes
fit-content, FIXED keeps its px. That only exists where the designer used Auto Layout.
A frame with no Auto Layout on the root is a canvas: every child is position:absolute at a
coordinate on a 1440px artboard. There is nothing to relax, so making the root width:100% does not
reflow it — the children stay pinned at their canvas coordinates and the right-hand side disappears
under overflow:hidden. That reads exactly like a page breaking when you zoom.
For those frames the converter keeps the exact canvas and scales it to the viewport instead, so the
design stays intact at every width and zoom level (down to 0.5x, after which the page scrolls).
figma_convert tells you which strategy it used. If you want real reflow rather than proportional
scaling, add Auto Layout in Figma — that is the signal the converter needs.
Every tool returns file paths and numbers, never large blobs. A converted frame is often 100+ KB of HTML; pushing that through a tool result would burn the agent's context for nothing. The agent reads and edits the files directly.
Figma responses are cached per frame, so after the first figma_convert the
whole loop runs offline and free — including when you are rate-limited.
Quick start
You need a Figma personal access token: Figma → Settings → Security → Personal
access tokens, scope File content: read.
export FIGMA_TOKEN=figd_REPLACE_WITH_YOUR_TOKENAdd it to Claude Code:
claude mcp add figma --env FIGMA_TOKEN=$FIGMA_TOKEN -- npx -y figma-codekit-mcp@latestOr for any MCP host that reads a JSON config:
{
"mcpServers": {
"figma": {
"command": "npx",
"args": ["-y", "figma-codekit-mcp@latest"],
"env": { "FIGMA_TOKEN": "figd_REPLACE_WITH_YOUR_TOKEN" }
}
}
}Then, in the agent: copy a frame link out of Figma (right-click the frame → Copy link to selection) and say "convert this frame and verify it until it converges."
Check your setup
FIGMA_TOKEN=$FIGMA_TOKEN npx -y figma-codekit-mcp@latest --checkIt checks Node, whether a token was found (and where, never its value), whether
Figma accepts it, and whether the Chromium figma_verify renders in is
installed, then prints READY or what to fix. Exit code 0 means all three tools
will work. If Chromium is missing (a skipped download, or CI), install it with
npx -y figma-codekit-mcp@latest --install-browser.
Windows
npx resolves to npx.cmd, which some MCP hosts cannot spawn directly. If the
server fails to start, point at the shim explicitly:
{
"command": "cmd",
"args": ["/c", "npx", "-y", "figma-codekit-mcp@latest"]
}The loop, concretely
figma_convert → generated.html, reference.png, ir.json (deterministic, no LLM)
↓
figma_verify → "NOT CONVERGED — 4.93% of pixels differ.
Elements: 22/26 match — 1 missing, 3 misplaced.
MISSING: button 'Get started'
MISPLACED: h1 'Pricing plan' — 6px too high"
↓
agent edits generated.html
↓
figma_verify → "CONVERGED — 0.31% of pixels differ (threshold 2.00%)."The refine loop runs through the calling agent, not through an LLM inside the
server. That means no ANTHROPIC_API_KEY, no second model billing, and the agent
keeps full context on what it already tried. A headless refine loop
(src/refine/) still exists for library use.
Watch the loop without an agent
The same pipeline has a one-page local web front end, split in two: a chat on the left, the generated app on the right. You send a frame link, it asks what to build — HTML, React or Next.js; exact or responsive — and then converts, renders and diffs in front of you. The right pane carries the running app, its source files, the verify numbers and the pixel diff.
npm run site # http://localhost:5173It uses the same .figma-token and the same .figma-cache/, so a frame you have
already converted re-runs offline in about a second. Useful for checking a frame
converts cleanly before pointing an agent at it — and for showing someone what
"verified against the design" means rather than describing it. Details in
site/README.md.
Configuration
Variable | Purpose |
| Required. Also read from a |
| Where converted frames are cached. Defaults to |
| Optional, headless refine loop only. The MCP server never calls an LLM. |
| Skip the Chromium download on install. |
See .env.example. Add .figma-cache/ to your project's
.gitignore — a cached frame keeps the original bitmaps and can be tens of megabytes.
FIGMA_FILE_TIMEOUT_MS (default 90000) caps how long figma_convert waits for
Figma's whole-file download, which it falls back to when the per-node endpoint is
rate-limited. When Figma will not answer, the tool says so within that time and,
if the frame is already cached anywhere, builds from the cached copy and marks the
result STALE CACHE instead of hanging.
Verify your own app, not just the generated HTML
figma_convert url=<frame link> → .figma-cache/<frame>/ (reference.png)
figma_verify dir=<that dir> url=http://localhost:3000/pricing selector="#plans"Build the page however you like — Figma's official MCP server, Framelink, by hand —
then point figma_verify at it. It screenshots the page (or just the selected
element) at the frame's size and pixel-diffs it against Figma. Element-box and
layering checks need the converter's data-ir-id markers, so for a URL only the
pixel diff runs.
What it does not do
Being straight about the edges, because they are Figma's, not bugs:
Token names are derived, not designed, unless you are on Enterprise. The Figma Variables REST API is Enterprise-only. Without it the converter still collects every colour, font, size and spacing value into
tokens.json/tokens.css/tailwind.theme.css, but names them after the value (--color-c8960c) because it cannot know intent. Rename them in your theme.Figma rate-limits by cost. A very large file fetched repeatedly can lock a token out for hours (free accounts hit this sooner). Cached frames keep working offline;
figma_converttells you when the limit lifts.Component prop bindings need Code Connect. Components are matched by name and reported, but the API does not expose which instance prop drove which value.
The diff has a floor. Anti-aliasing and font hinting keep the ratio around 0.5–1.5% even on a perfect match.
thresholddefaults to 2% for that reason — chasing 0 is chasing rendering noise.One frame at a time. No whole-file crawling, by design: it keeps token spend and Figma rate-limit pressure predictable.
Use it as a library
The verify loop is framework-agnostic and does not need the MCP layer:
import { verifyLoop } from 'figma-codekit-mcp';
import fs from 'node:fs';
const result = await verifyLoop({
initialCode: firstPassHtml,
referencePng: fs.readFileSync('frame.png'),
ir, // enables element-level IoU findings
viewport: { width: 1440, height: 900, deviceScaleFactor: 2 },
threshold: 0.02,
maxIterations: 5,
generate: async ({ code, correction }) => callYourModel({ code, correction }),
});
console.log(result.converged, result.diffRatio);
fs.writeFileSync('diff.png', result.bestDiffPng);The loop keeps the best iteration seen, so a later regression never makes the
result worse. To verify a React component on a dev server instead of an HTML
string, pass render: () => renderUrl('http://localhost:3000/preview', viewport).
Develop
git clone https://github.com/Mehmoodqureshi/figma-codekit-mcp.git
cd figma-codekit-mcp
npm install # also downloads Chromium via postinstall
npm test # full chain against a mocked Figma API — no token, no network
npm run example # the verify loop end to end; watch the diff ratio fall
npm run serve # run the MCP server on stdioPath | Role |
| MCP server, Figma REST source, frame loading, asset export |
| Figma node tree → normalized IR (roles, boxes, auto-layout, style, tokens) |
| IR → HTML / React / CSS |
| Playwright render, pixel diff, bounding-box IoU, z-order + clipping |
| Turn a diff into fix instructions; drive the loop |
| Optional headless LLM refiners (Anthropic, Gemini) |
| Runnable demos and the offline test suite |
example/ doubles as the test suite — npm test runs the full
loadFrame → figmaToIR → generateHtml chain against a mocked Figma REST API,
including rate-limit handling and rotation/mirror transforms. No token, no
network, no browser, so it runs in CI on every push.
Upgrading from an earlier package name
Replace the package in your MCP client command with npx -y figma-codekit-mcp@latest. Existing environment settings and saved data remain compatible.
Related
MCP Browser Extension (mcp-browser-extension): let Claude, Cursor or any MCP agent use the Chrome you are already logged into, with a deny-all domain allowlist.
License
MIT © Mehmood Ur Rehman Qureshi
Available Tools
3 toolsfigma_convertConvert a Figma frame to codeA
Fetch a Figma frame and deterministically convert it to self-contained HTML (no LLM). Writes generated.html, reference.png, ir.json and assets.json to a cache dir and returns the paths plus a summary. Pass react=true for a component file, or next=true for a runnable Next.js App Router project you can npm install && npm run dev. Figma responses are cached, so re-running is offline and free unless refresh=true. Follow with figma_verify to see how close the render is.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Figma frame URL, e.g. https://www.figma.com/design/KEY/Name?node-id=1-2 | |
| next | No | Also emit next/ — a runnable Next.js App Router project (package.json, next.config.mjs, app/layout.jsx, app/page.jsx, app/globals.css and the component) that you can npm install && npm run dev as it stands. | |
| react | No | Also emit generated.jsx (React with inline styles) alongside the HTML, plus components/<Name>.jsx for every Code Connect binding it imports. | |
| outDir | No | Where to write output. Defaults to .figma-cache/<fileKey>-<nodeId>/ | |
| refresh | No | Ignore the cache and re-fetch from the Figma API. | |
| tailwind | No | Emit the React / Next.js output with Tailwind v4 classes instead of inline style objects (colours and fonts use the generated @theme tokens). The HTML stays plain CSS for verify. | |
| responsive | No | Emit the responsive variant (Figma FILL→flex/100%, HUG→fit-content, FIXED→px kept) instead of the exact-sizing variant. A frame with NO Auto Layout has no sizing intent to relax, so it keeps its exact CSS and is scaled to fit the viewport instead — otherwise its absolutely-placed children stay pinned to the design canvas and get clipped rather than reflowed. Exact is still the right starting point for verify. | |
| inlineAssets | No | Embed images and vectors as data URIs (one self-contained file). Default false: they are written once to assets/<hash>.<ext> and referenced, which keeps generated.html small. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that output is written to a cache dir with named files, that Figma responses are cached so re-runs are offline and free, and that refresh=true bypasses that cache. The notable gaps are auth requirements (a Figma token is surely needed) and error/failure behavior.
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?
Front-loaded with the core action and output set, then flags, then caching, then the follow-up tool — a sensible ordering with no filler sentences. It is on the long side for a description, though every clause conveys operative information.
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 an eight-parameter tool with no annotations and no output schema, the description covers what is produced and where, the caching model, and the verification follow-up, which is most of what an agent needs. Only prerequisite credentials and failure modes are missing.
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 the parameter docs are already detailed, so the baseline is 3. The prose reinforces react/next/refresh semantics but adds little the schema does not already state, and the interaction between tailwind and the other flags is left to the schema.
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?
"Fetch a Figma frame and deterministically convert it to self-contained HTML (no LLM)" gives a specific verb, resource, output format, and even the execution model. It then enumerates the exact artifacts written (generated.html, reference.png, ir.json, assets.json) and the return value, so the tool is unmistakable against figma_verify and figma_inspect.
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 explicitly routes the agent: react=true for a component, next=true for a runnable App Router project, and "Follow with figma_verify to see how close the render is." That is real when-to-use guidance plus a named follow-up sibling. It never says when to prefer figma_inspect instead, so the exclusion set is incomplete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_inspectInspect the design structureA
Print the IR tree as an indented outline (role, name, box, layout, text, tokens) so you can understand the design structure and find a specific element without reading raw Figma JSON or the whole ir.json. Use after figma_convert.
| Name | Required | Description | Default |
|---|---|---|---|
| dir | Yes | Cache dir from figma_convert. | |
| depth | No | Max tree depth to print. | |
| filter | No | Only show nodes whose name or text matches this (case-insensitive substring). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden alone; it does disclose that the operation prints an outline rather than mutating anything and describes the shape of that output. It is silent on side effects, output size/pagination for large trees, and failure behavior when dir is invalid or absent.
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?
Front-loaded with the action and its output format, followed by a short rationale-and-ordering sentence. There is some redundancy between "understand the design structure" and the enumerated fields, but nothing is wasted at length.
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?
A simple three-parameter read tool with no output schema, and the description explains what the returned outline contains and what fields it shows. That is nearly everything an agent needs, with only edge-case behavior (empty results, invalid dir) left unspecified.
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 dir, depth, and filter are already documented in the schema. The description adds no extra parameter meaning such as how filter interacts with depth or the default depth of 4, so the baseline of 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?
States a specific verb and resource ("Print the IR tree as an indented outline") and even enumerates the fields emitted (role, name, box, layout, text, tokens). This clearly separates it from figma_convert and figma_verify, which produce/check the IR rather than read it back.
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?
Gives explicit sequencing ("Use after figma_convert") and the motivating job ("find a specific element without reading raw Figma JSON or the whole ir.json"). It does not contrast with figma_verify or say when inspection is unnecessary, so it stops short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
figma_verifyVerify rendered code against the Figma designA
Render an HTML file in Chromium, pixel-diff it against the Figma reference, and check each element position with bounding-box IoU, paint order (z-index) and clipping. Returns the diff ratio plus specific instructions on what to fix (MISSING / MISPLACED / WRONG STACKING / OVER-CLIPPED elements, worst regions). Writes render.png and diff.png. Edit the HTML based on the correction and call this again to iterate toward pixel-exact. Read the returned diff.png to see the mismatches highlighted.
| Name | Required | Description | Default |
|---|---|---|---|
| dir | Yes | Cache dir from figma_convert. Supplies reference.png and ir.json. | |
| url | No | Verify a RUNNING page instead of the generated HTML — e.g. your own app at http://localhost:3000/pricing, built in any framework. The page is screenshotted at the frame size and pixel-diffed against the Figma reference. Element/layering checks need the converter's data-ir-id markers, so for a URL only the pixel diff runs. | |
| width | No | Viewport width. Defaults to the design frame width from the IR. | |
| height | No | Viewport height. Defaults to the design frame height from the IR. | |
| fullPage | No | Capture full scroll height rather than just the viewport. | |
| htmlPath | No | HTML file to verify. Defaults to <dir>/generated.html. | |
| selector | No | With url: CSS selector of the element to compare (e.g. "#pricing-card"). Default: the viewport. | |
| threshold | No | Diff ratio considered "exact enough". Anti-aliasing puts a floor near 0.005-0.015. | |
| includeImages | No | Attach reference/render/diff PNGs to the response. Costs a lot of context — prefer reading diff.png from disk only when the numbers alone are not enough. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description still discloses the rendering engine, the verification checks, the return payload (diff ratio plus categorized fixes), and the side effects of writing render.png and diff.png. It also flags the read-diff.png follow-up, giving enough behavioral context for an agent to operate it.
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 front-loaded with the core action, followed by outputs, iteration, and a reading tip. Every sentence adds useful operational detail without repetition.
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 nine-parameter tool with no output schema, the description explains what is returned, what files are written, and how to iterate. It omits the schema-documented URL/running-page mode and its pixel-diff-only limitation, which keeps it from being fully complete.
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 has 100% description coverage for all nine parameters, so the schema already explains dir, url, selector, threshold, includeImages, and the rest. The prose adds no parameter-level details beyond the workflow, so the baseline of 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 uses specific verbs and resources: render an HTML file in Chromium, pixel-diff it against the Figma reference, and check element positions with IoU, paint order, and clipping. That clearly separates it from figma_convert and figma_inspect, which handle conversion and inspection rather than rendered verification.
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 clear iterative workflow: edit the HTML based on the correction and call again, and read diff.png when the numbers alone are insufficient. It does not explicitly name when not to use it or point to sibling tools, so it stops short of full when/when-not 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.
3 tool updates
v0.1.0- First observed
figma_convert - First observed
figma_inspect - First observed
figma_verify
TDQS
Scored across 3 tools
Each tool targets a clearly distinct stage of a linear pipeline: convert (Figma→HTML), verify (pixel-diff render against reference), and inspect (read the IR tree). The descriptions explicitly state ordering ('use after figma_convert', 'follow with figma_verify'), so an agent cannot reasonably confuse them.
All three tools follow the same figma_<verb> pattern (figma_convert, figma_verify, figma_inspect) with short, single-word verbs. Fully predictable and consistent.
Three tools is exactly the minimal surface for a convert → inspect → verify iteration loop, and each one has a non-overlapping job. There is no filler or redundant tool in the set.
The core lifecycle (convert, inspect structure, verify/fix loop, output artifacts) is covered with no dead ends. The main gap is discovery: there is no tool to list files/frames or resolve a Figma URL to a node ID, so the agent must already know the target node before calling figma_convert.
Maintenance
Related MCP Connectors
Compare Figma designs against live websites and get visual + content QA reports, from inside Claude.
Generate images, GIFs, videos, and PDFs from HTML, URLs, or templates — from your AI agent.
A design agent in your coding agent's loop: briefs each screen, reviews UX and visual quality.
Visual testing for coding agents: your agent sees every UI change it makes and triages each diff.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables HTML page analysis, verification, and automated correction using Playwright for rendering and Mistral AI for visual inspection. Captures screenshots, analyzes renders against specifications, and generates fixes for HTML issues.2ISC
- AlicenseAqualityDmaintenanceEnables AI agents to interact with Figma to create, read, and manage designs using the Figma REST API and a dedicated plugin. It supports advanced features like UI generation from text, webpage reconstruction in Figma, and design token synchronization with codebases.20MIT
- AlicenseBqualityBmaintenanceUse your signed-in Chrome through a local MCP server and companion extension. Supports tabs, navigation, forms, screenshots, and page reading, with a deny-all domain allowlist. Install: npx -y mcp-browser-extension@latest.22684 npm6MIT
- AlicenseNot gradedqualityBmaintenanceEnables agents to compare a live web page against a Figma frame and receive a bounded JSON verdict of design deviations, without sending screenshots or mockups through the model context.MIT