Skip to content

Releases

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 05 Nov 21:39
  • feat: figma_verify checks any running page. Pass url (and optionally
    selector) to pixel-diff your own app against the Figma frame — any framework,
    built by any tool — not only the HTML this server generates.
  • feat: images are written as files by default. Each distinct bitmap or vector
    goes to assets/<hash>.<ext> once (and to next/public/assets/ for the Next.js
    export), so a photo-heavy frame produces a ~60 KB generated.html instead of a
    20 MB one. inlineAssets: true restores the single self-contained file.
  • feat: design tokens. Every conversion writes tokens.json (W3C DTCG),
    tokens.css and a Tailwind v4 tailwind.theme.css, collected from the colours,
    fonts, font sizes, radii and spacing the frame actually uses.
  • feat: Tailwind output. tailwind: true emits the React / Next.js output as
    Tailwind v4 classes (exact arbitrary values, theme tokens for colours and fonts)
    and sets up Tailwind in the Next.js project. The HTML stays plain CSS for verify.
  • fix: fails fast instead of hanging on Figma rate limits. figma_convert no
    longer sleeps through multi-minute cooldowns: it tries once (whole-file download
    capped by FIGMA_FILE_TIMEOUT_MS, default 90 s), then builds from any cached
    copy of the frame and marks it STALE CACHE, or says when the limit lifts.
  • fix: clearer 401/403 errors: a dead token, a file the account cannot open and a
    spent Figma cost budget are now told apart (via /v1/me).
  • fix: verify waits for webfonts before the screenshot. A small HTML file could be
    captured before its Google Fonts had loaded.
  • fix: wrapped Auto Layout no longer pushes rows out of the frame. Figma keeps
    two spacings for a wrapping row — itemSpacing between items and
    counterAxisSpacing between wrapped rows — but CSS gap sets both. A
    "space-between" footer with a 1,439 px item spacing wrapped its second row
    1,439 px down and the frame clipped it. Wrapped layouts now emit column-gap /
    row-gap separately (plus align-content for SPACE_BETWEEN). On a real author
    site this took verify from 148/162 elements with 9 over-clipped to 162/162.
  • fix: rotated elements are measured correctly. The element check compared a
    rotated prop's unrotated design box with the browser's bounds of the turned
    shape, so every rotated image counted as misplaced even when drawn exactly
    right. Expected boxes are now rotated about their centre first.
  • fix: verify matches Figma's downscaled references. Figma exports a frame too
    large for a 2x render at a smaller scale (a 1440x10490 frame came back at
    1.49x). Verify now renders at the reference's actual scale instead of 2x, which
    turned a meaningless 61% diff on such a frame into a real 9%.
  • chore: the optional refine loop defaults to claude-opus-5-5 at effort high,
    with the server-side refusal fallback on (fallbacks: false to disable).
  • chore: dependency updates (MCP SDK 1.32.2, Playwright 1.65, zod 4.6); npm audit
    clean. The Anthropic SDK moves to 1.6, which sends API version 2026-10-21 and
    is Apache-2.0 licensed; the refine code needed no changes for it. After upgrading run
    npx -y figma-codekit-mcp@latest --install-browser once for the new Chromium.

v0.2.4

Choose a tag to compare

@github-actions github-actions released this 15 Oct 14:31
  • Validate trusted npm publishing from GitHub Actions for the canonical package.

v0.2.3

Choose a tag to compare

@github-actions github-actions released this 15 Oct 14:02
  • Rename the npm package and CLI to figma-codekit-mcp.
  • Update install commands, setup checks and the local site branding.
  • Align repository links and the MCP registry identifier with figma-codekit-mcp.

v0.2.1 — --check and --install-browser, tag-triggered trusted publishing, MCP registry metadata

Choose a tag to compare

@github-actions github-actions released this 26 Sep 10:02

Setup can now be checked in one command, and the React and Next.js output
compiles when a frame uses components.

  • feat: --check tells you whether figma-mcp will work before an agent
    tries.
    The server has nothing to say until an MCP host connects, so a fresh
    install gave no way to tell a missing token from a broken browser.
    npx -y @mehmoodqureshi/figma-mcp --check reports the Node version, whether a
    token was found and where (environment, or the .figma-token path, never
    the value), whether Figma accepts it (/v1/me), and whether the Chromium that
    figma_verify renders in is on disk, then prints READY or the fix for each
    problem, exiting 0 or 1. --install-browser redoes a skipped or failed
    Chromium download with the Playwright version this package pins. Both run
    before the stdio transport starts, so stdout carries the report.
  • 28 new checks in example/doctor-test.js, now part of npm test: every
    /v1/me answer (200, 401/403, 429, 5xx, offline, timeout), the report, and
    the real binary in a scratch directory, including that the token value never
    appears in the output.
  • fix: the React and Next.js output did not compile whenever a frame used a
    bound component.
    generateReact emitted import { Badge } from './components/Badge' for every Code Connect instance, and nothing anywhere
    wrote that file — not the MCP server, not generateNext's file map. Two of the
    three frames in the local cache hit it, so npm run dev died on the first
    import. generateComponentModules(ir) (src/codegen/react.js) now emits one
    module per binding, and both callers write them.
  • fix: a bound instance rendered as a self-closing <Badge />, so everything
    Figma had inside it was dropped from the React tree. The HTML emitter keeps
    that content (html.js, ROLES.COMPONENT) and React silently did not — the
    two emitters disagreed about what the design contains, and since only the HTML
    is ever rendered, nothing caught it. The instance's content is now passed to
    the component as children, which is where it has to live: a binding is
    instantiated many times per frame with different copy, so content held in the
    module is content every instance shares. Building a real page with it in the
    module rendered every nav link with the first one's label and lost 53 of 69
    strings; as children, the page carries 63 and the render is pixel-identical to
    the HTML.
  • feat: figma_convert takes next: true and writes the runnable App Router
    project to <dir>/next/. generateNext has existed since 0.2.0 but was
    reachable only through site/ — the MCP server imported just generateHtml
    and generateReact, so the one interface most people use could not emit a
    Next.js project at all.
  • fix: generateReact dropped the line breaks in multi-line copy. A break
    arrives from Figma as "\n" inside a text run, and the HTML emitter turns it
    into <br> (html.js, br()) while React interpolated it as raw whitespace.
    These elements carry white-space:nowrap from cssgen, so a two-line headline
    rendered as one long line overflowing its box, with the design's own breaks
    gone. Found by building the generated project and diffing the running page
    against the HTML: it accounted for the whole remaining 1.42% gap, which is now
    0.00%.

v0.2.0

Choose a tag to compare

@Mehmoodqureshi Mehmoodqureshi released this 03 Sep 12:21

Verify now checks paint order and clipping

figma_verify gains a third signal alongside the pixel diff and the bounding-box IoU. Figma lists children back-to-front, so a node's pre-order index is its paint order; the render is probed with elementsFromPoint over a sample grid and the two orders compared wherever elements actually overlap.

This closes the blind spot the other two signals share by construction — elements present, in exactly the right box, stacked the wrong way round, or cut off by an ancestor's overflow where the design lets them overhang. Findings surface as WRONG STACKING / OVER-CLIPPED, and are reported even when the pixel diff has converged: a decorative overlay on the wrong side of a card shifts too few pixels to clear the threshold while being the first thing a person sees.

Two fixes make that signal trustworthy:

  • Sampling scrolls the page and uses a square pitch, so findings no longer depend on viewport height. A 1440x10490 frame verified at a 900px viewport was previously sampled only across its first screen — 13 overlapping pairs instead of 502, and a clean bill of health for a frame with 24 stacking errors in it.
  • Hit-testing briefly forces pointer-events: auto, without which the probe skips exactly the decorative overlays the check exists to catch.

Next.js output

generateNext(ir, { assets }) emits a runnable App Router project — app/page.jsx, app/layout.jsx, app/globals.css, package.json, next.config.mjs. The component file is byte-for-byte generateReact's output, so the two can never drift apart in what they claim the design looks like; the rest is scaffolding. A canvas-fit design scales itself with hooks, so that variant alone is marked 'use client'.

Fixed: the React emitter produced no images

generateReact never received assets, so every image emitted src={""} and every vector emitted the "asset not exported" outline. The output was structurally correct and completely blank. It now takes the same assets map as the HTML emitter and follows the same rules: a cropped fill becomes a role="img" div carrying the background layer, everything else becomes an <img> with the data URI, and a missing export falls back to the same transparent placeholder.

A local web front end

npm run site opens a page for the same pipeline: a conversation that asks what to build — HTML, React or Next.js; exact or responsive — beside a pane carrying the generated app, its source files, the verify numbers and the pixel diff. It calls src/ directly rather than reimplementing anything, so it and the MCP server always agree. No build step, no new dependencies, excluded from the published package.


fontStylesheetUrls() was split out of html.js and exported so emitters that cannot use a <link> tag reach the same font list. generateHtml output is byte-identical across the change.