ui-assert
Allows running UI assertion checks against Storybook stories by pointing at a story URL and scoping the checks to the story root.
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., "@ui-assertcheck http://localhost:5173/settings for token, layout, and a11y issues"
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.
ui-assert
Point it at a running page and it tells you, in a few lines of text, whether the UI is actually right: which colours bypass your design tokens, which tokens are referenced but never defined, what overflows or gets clipped at each viewport, which tap targets are too small, and what axe finds. It exits non-zero when something is wrong.
It exists because coding agents can build UI but can't prove it. Screenshots cost a fortune in tokens and still leave the agent guessing, linters never see the rendered page, and pixel diffs say that something changed, not what's wrong. ui-assert gives an agent, a CI job, or a person one call that answers "is this fine?" with reasons.
$ npx ui-assert http://localhost:5173/settings
ui-assert http://localhost:5173/settings chromium 3 viewports x light,dark (4.8s)
tokens 1 error, 2 warnings (412 rules inspected, 0 sheets skipped)
error undefined token --surface-primary background-color .Card_root_x1 4 elements
warning hardcoded rgb(255, 0, 0) color .danger 1 element
warning hardcoded rgb(1, 2, 3) background-color body > main > p [style] 1 element
layout 2 errors, 1 warning
[375x812 light, 375x812 dark] error horizontal page overflow +107px document ui-assert-out/375x812-light.png
[all cells] error text overflows box 466 > 200px main > div.Row > h2.Title
[all cells] warning target 16x16 smaller than 24x24 button.IconBtn
a11y 1 error
[all cells] critical image-alt 1 node img https://dequeuniversity.com/rules/axe/4.13/image-alt
FAIL 4 errors, 3 warningsOnly problems are listed. A clean page prints three ok lines and PASS.
Install
npm i -D ui-assert
npx playwright install chromiumNode 20 or newer. Chromium is the default browser; --browser firefox and --browser webkit work if you've installed them.
Related MCP server: DesignDiff MCP
What it checks
tokens. It walks the stylesheets the page actually loaded and looks at the colour properties of every rule that applies to at least one element on screen. A value written as var(--something) is token-derived; a literal (#fff, rgb(...), white, a gradient) is reported as hardcoded. Then, for every token that's referenced, it reads the computed value off the matching elements. If it comes back empty the token was never defined for that element in that theme. Browsers drop invalid custom properties silently, so the component just inherits something and looks almost right. No static tool can catch this; it needs the rendered page.
Inline style attributes get the same treatment. Stylesheets from node_modules are skipped unless you ask for them.
layout. For each viewport and colour scheme: horizontal page overflow, text that overflows its box or is clipped without an ellipsis, content clipped vertically, elements that extend past the viewport edge, and interactive targets smaller than 24×24 CSS pixels (WCAG 2.2, 2.5.8). Cells with errors get a screenshot in ui-assert-out/; cells that pass don't.
a11y. axe-core through @axe-core/playwright, once per cell, because contrast changes between light and dark. Critical and serious impacts are errors, moderate and minor are warnings.
Findings that repeat across cells are collapsed to one line that says where they happened, and the summary counts distinct problems, not problem × cell.
Options
ui-assert <url> [options]
--scope <selector> restrict tokens + layout checks to a subtree (default: body)
--viewports <list> default 375x812,768x1024,1280x800
--schemes <list> default light,dark
--theme-attr <name> also set this attribute on <html> to the scheme name,
for apps that switch theme with e.g. data-theme="dark"
--browser <name> chromium (default) | firefox | webkit
--wait <selector|ms> after load, wait for a selector or extra ms (default 250)
--token-prefix=<str> only custom properties with this prefix count as tokens;
any other var() is reported. Use the = form, since the
value starts with dashes: --token-prefix=--ds-
--props <list> CSS properties the tokens check inspects
--ignore <selectors> comma-separated selectors skipped in all checks
--include-third-party also inspect node_modules stylesheets
--strict-tokens hardcoded colours are errors instead of warnings
--no-tokens --no-layout --no-a11y
--json JSON report to stdout
--out <dir> failing-cell screenshots (default ./ui-assert-out)
--fail-on error|warning|none (default error)
--config <path> JSON file with the same options in camelCaseFlags that pile up are better kept in a ui-assert.config.json next to your package.json; it's read automatically, and anything you pass on the command line still wins:
{
"url": "http://localhost:5173/",
"scope": "main",
"themeAttr": "data-theme",
"ignore": ["[data-agentation-root]"],
"tokenPrefix": "--",
"failOn": "warning"
}The default properties are color, background-color, the four border-*-colors, outline-color, fill, stroke, caret-color, text-decoration-color and accent-color. Add background-image if you want gradients caught, or spacing properties if your tokens cover those too.
Using it from an agent
The text output is meant to be pasted straight into a model's context. A Claude Code hook that runs after every file edit and hands the verdict back looks like this in .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx ui-assert http://localhost:5173/ --scope main --fail-on warning || true"
}
]
}
]
}
}For CI, drop || true and let the exit code fail the job. --json gives you the same data with per-cell detail if you want to post-process it.
Dev-only overlays (an annotation toolbar, a devtools panel) tend to ship their own colours and tiny buttons. Keep them out of the verdict with --ignore, e.g. --ignore "[data-agentation-root]".
Storybook stories are just URLs: ui-assert "http://localhost:6006/iframe.html?id=button--primary&viewMode=story" --scope "#storybook-root".
MCP server
The same checks are available to an agent as an MCP tool, so it can verify its own work without you wiring up a hook. Add it to Claude Code with:
claude mcp add ui-assert -- npx -y -p ui-assert ui-assert-mcpThe -p is needed because the binary is called ui-assert-mcp but lives in the ui-assert package. The same thing in a .mcp.json, or Cursor's mcp.json:
{
"mcpServers": {
"ui-assert": {
"command": "npx",
"args": ["-y", "-p", "ui-assert", "ui-assert-mcp"]
}
}
}If the package is already a dev dependency, "command": "ui-assert-mcp" with no args works too and skips the registry lookup.
The server exposes one tool, ui_assert, taking the same options as the CLI in camelCase. It returns the text verdict you'd get on the command line, or the full JSON report with format: "json". A FAIL comes back as a normal result the agent can read and act on, not an error; errors are reserved for a run that couldn't happen at all, such as a missing browser or a page that wouldn't load.
Programmatic use
import { run, formatText } from "ui-assert";
const report = await run({ url: "http://localhost:5173/", scope: "main" });
console.log(formatText(report));
if (!report.summary.passed) process.exit(1);run takes the same options as the CLI in camelCase and resolves to the full report; the types are exported.
Limitations
Shadow DOM: stylesheets and elements inside shadow roots aren't inspected by the tokens and layout checks. axe does look inside them.
Cross-origin stylesheets can't be read without CORS headers. They're counted and reported as skipped, never silently ignored.
Styles that only exist in
adoptedStyleSheetsaren't enumerated bydocument.styleSheetsand are missed.color-mix()and relative colour syntax are treated as literals even when every argument is avar().The undefined-token check reads computed styles off elements that are in the DOM right now. A token used only by a modal, another route or a hover state isn't checked until that state is on screen. Drive the page there first, or use
--wait.Rules inside media and container queries are inspected whether or not the query currently matches. The "applies to at least one element" filter keeps this mostly quiet.
--theme-attrflips the attribute after load, so an app that reads its theme once at startup may not react.The target-size check implements the inline-link exception from WCAG 2.5.8 but not the spacing exception.
The layout scan looks at the first 5000 elements in scope and reports at most 25 findings per kind per cell; the tokens check stops at 50 per kind and says so.
Licence
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Score any URL against a real design contract — 42 checks, A-F grade, token + motion validation.
Validate HTML/CSS, audit SEO and JSON-LD, check links, and capture responsive screenshots.
Check AI work against requirements and return structured verdicts, findings, and repair steps.
On-demand drift checks: declared CSS color, radius, spacing & type vs your own tokens or a pack
Related MCP Servers
- AlicenseAqualityAmaintenancePoint your coding agent at a URL and get a real-browser QA audit: broken signup/login/checkout flows, JS console errors, missing analytics, consent + security headers, mobile tap targets, and accessibility — returned as machine-verified findings graded A-F.442Apache 2.0
- AlicenseNot gradedqualityDmaintenanceVerifies that AI-generated UI code matches Figma design specs by rendering components in a real browser, comparing computed CSS, and returning patch-ready fixes with scored parity reports.MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to score live URLs against a 40-check design contract, validate DTCG tokens and Lottie animations, audit accessibility, and retrieve design-system contracts, catalogs, and review rubrics.5 npmMIT
- AlicenseAqualityCmaintenanceEnables AI-driven UI inspection and optimization by scanning websites with configurable viewports, capturing screenshots, running deterministic accessibility and layout checks, and generating prioritized reports with fix prompts.2MIT