bettercss
Click on "Install 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., "@bettercsscheck overflow on http://localhost:3000"
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.
csstruth
Hard ground truth for CSS. csstruth extracts the browser's actual rendered layout — positions, boxes, the cascade — as deterministic, diffable text, so coding agents (and humans) stop guessing what rendered.
Backend code gets real feedback: tests fail, APIs return status codes, databases have state. CSS gets… pixels and vibes. Screenshot diffing is fuzzy and nondeterministic; poking DevTools by hand is slow and unrepeatable. csstruth gives layout the "backend treatment": structured truth you can assert against, byte-identical across runs, with every violation traced to the source rule that caused it.
$ csstruth verify http://localhost:3000
VERDICT: PASS
checked 3 viewports: 375x800=clean, 768x800=clean, 1280x800=clean$ csstruth check http://localhost:3000 --hover .cta
parent-bleed: a.cta bleeds 100px outside div.rail (child 400px wide, parent 300px)
suspect: width: 400px @ main.css:4$ csstruth explain http://localhost:3000 --selector .sidebar --property width
.sidebar width = 240px
✓ width: 300px sidebar.css:1 (.sidebar (0,1,0)) — computed 240px differs from
declared 300px — constrained by max-width: 240px @ main.css:1
✗ width: 100% reset.css:1 (div (0,0,1)) — lost: lower specificity (0,0,1) vs (0,1,0)How it works
Chromium's DevTools Protocol exposes everything DevTools itself knows: one bulk
DOMSnapshot call returns the full DOM with layout boxes and computed styles;
CSS.getMatchedStylesForNode returns the complete cascade for any element —
every rule that matched, its specificity, and the stylesheet position it came
from (source-mapped back through your build). csstruth packages that truth
into 12 composable CLI commands (11 of them also exposed as MCP tools —
watch is CLI-only, a streaming daemon doesn't fit MCP's request/response
shape) instead of megabytes of protocol JSON.
The core representation is the LayoutTree: one line per rendered element, deterministic (same render → byte-identical text), with warnings inline:
body (0,0 1280x623)
header#top (0,0 1280x64) flex row pad:0,24
span.logo (24,25 55x15)
nav (964,13 292x39) flex row gap:8
main (0,95 1280x480) flex row
aside.sidebar (0,95 240x480) pad:16
section.content (240,95 1040x480) pad:24
div.card ×6 (~317x140)Deterministic text is what makes layout diffable — "did my CSS change break anything?" becomes a structural diff with exact px deltas, not a flaky screenshot comparison.
Related MCP server: websight
The tools
Tool | What it answers |
| "Is this OK?" — one call: invariants across a viewport sweep (default 375/768/1280) + optional snapshot diffs. First line is always |
| Layout-bug scan: viewport overflow, visible parent bleed, clipped text, unintended overlap, zero-size/tiny tap targets — exact px + the suspect rule at |
| Snapshot the CURRENT violation set as a sorted, diff-friendly file — the fix for adopting |
| Propose (default) or apply mechanical patches for fixable violations (clipped text, tiny tap targets, a fixed px width bleeding/overflowing). DRY-RUN unless |
| "Which commit broke this?" — walks a git repo's history backwards (newest→oldest, capped at 25 commits by default) checking out each commit into a scratch |
| Live diff stream while you edit. Holds one page open, polling the layout signature every |
| The LayoutTree of the rendered page (scope with |
| One element in depth: box model, non-default styles, stacking context, why it has its width/height. |
| Trace any property to its source: which declaration wins ( |
| Lock the current layout to a named |
| Structural diff vs a snapshot: what moved/resized/appeared/disappeared, in px. |
| Load-time layout-shift report (Cumulative Layout Shift): what moved, when, and by how much, plus unsized |
Interaction states: pass --hover/--focus/--active <selector> (CLI) or the
matching params (MCP) to force pseudo-states without a mouse — combinable with
--viewports, because hover effects that fit at desktop routinely bleed at
mobile widths. For JS-driven UI a pseudo-state can't reach (menus, tabs,
anything behind a click), --click <selector> (repeatable, real trusted
click) and --scroll-to <selector_or_y> run real interaction pre-steps before
capture — layout/inspect/explain/check/verify only, in order: scroll-to →
click(s) → settle. For animated pages, --settled fast-forwards every CSS
transition/animation to its end state before capturing (recommended before
snapshot/diff/verify on anything with a CSS animation); --at-time N
seeks to a specific ms instead, everywhere except snapshot/diff (a
specific frame isn't a reproducible baseline). Runs after interact steps and
before forced states.
Violations are designed to be real bugs. The invariants are tuned against
real-world pages (intentional overlays, carousels, SVG internals, and
scroll-managed content are exempt). For a genuinely intentional pattern, put
data-csstruth-ignore on the element.
Safety (fix): dry-run is the default — nothing is written unless you
pass --apply (CLI) or apply: true (MCP), and --root/root is always
required so a proposed patch's target file is explicit. Only a handful of
violations have a safe, mechanical fix (clipped text, a tiny tap target, a
fixed px width that's bleeding or overflowing); everything else reports "no
mechanical fix — see suspect" instead of guessing. Writes are confined to
--root: a stylesheet URL is resolved and the result is verified to stay
inside it, refusing anything that would escape (including through a
source-mapped sources[] path). Before writing, each patch re-reads its
target file and checks the suspect declaration still appears within 3 lines
of where it was seen — a mismatch (e.g. someone else edited the file
concurrently) refuses that one patch with a clear reason; unaffected patches
in the same run still apply. --apply always re-runs check afterward and
reports before: N violations → after: M violations plus any new
violations the patch introduced, exiting non-zero unless the fix strictly
improved things. Inline <style>/style="" suspects are never patchable —
refused, naming the page:line to hand-edit instead.
Adopting verify on a brownfield page — baselines. An all-or-nothing
VERDICT: FAIL is useless for confirming a targeted fix on a page that has
standing, known-benign violations, and it blocks verify from ever gating CI
on a page that isn't already fully clean (the standard linter-adoption
problem). Fix it once:
$ csstruth baseline http://localhost:3000 --viewports 375x800,768x800,1280x800
baseline written: .csstruth-baseline (15 violations)Then pass --baseline to check/verify (same --viewports, so the keys
line up): violations already in the file collapse to one line, only genuinely
NEW violations are itemized and drive the verdict/exit code, and anything
RESOLVED (in the file, no longer present) is celebrated:
$ csstruth verify http://localhost:3000 --viewports 375x800,768x800,1280x800 --baseline .csstruth-baseline
VERDICT: PASS (2 resolved, 0 new, 13 baseline)
[1280x800] resolved: parent-bleed a.cta
[1280x800] baseline: 13 accepted violations unchanged
checked 3 viewports: ...A genuinely new violation still fails the build, naming only itself — not the
13 you haven't gotten to yet. When you intentionally accept or fix a batch of
violations, --update-baseline rewrites the file to match and prints what
was added/removed:
$ csstruth verify http://localhost:3000 --viewports 375x800,768x800,1280x800 --baseline .csstruth-baseline --update-baseline
...
baseline updated: .csstruth-baseline (0 added, 2 removed)
- [1280x800] parent-bleed a.ctaThe baseline file is a sorted, diff-friendly text file — one line per
violation, keyed by (viewport, rule, selector) with pixel amounts excluded
(they drift run to run) — so a PR that fixes or accepts violations shows up
as a small, reviewable diff. A baseline's viewport labeling must match the
run it's compared against: capture with the same --viewports (or neither
side passes it) you'll later pass to check/verify. Without --baseline,
behavior is unchanged.
Install
git clone https://github.com/ikraamg/csstruth.git
cd csstruth
npm install && npm run buildRequires Node ≥ 20 and Chrome/Chromium. By default csstruth launches its own
isolated headless Chrome, so it never opens tabs in a browser you have open.
To inspect logged-in or app-state pages in your own Chrome, start it with
--remote-debugging-port=9222 and pass --attach (CLI); --port N attaches to
an explicit port. The MCP server is always isolated.
Any viewport ≤ 500px wide — the default sweep's 375x800 leg included — is
emulated as a real phone (mobile: true, deviceScaleFactor: 2, touch
enabled), not a desktop window squeezed narrow. This exercises the actual
mobile render: <meta viewport> fallback behavior, coarse-pointer/hover media
queries, and touch feature detection. Reported geometry stays in CSS px
regardless of the device pixel ratio, so a static page's element boxes are
byte-identical to the old emulation, and overflow/tap-target detection is fully
preserved. What legitimately changes is genuinely mobile-specific rendering — a
page missing <meta name=viewport> renders at the ~980px desktop fallback, as a
real phone would. Pass --desktop-only to force the old squeezed-desktop
emulation (mobile: false, DPR 1) at every width.
Use as an MCP server (the agent loop)
claude mcp add --scope user csstruth -- node /path/to/csstruth/dist/mcp.jsor per-project in .mcp.json:
{
"mcpServers": {
"csstruth": { "command": "node", "args": ["/path/to/csstruth/dist/mcp.js"] }
}
}The agent loop this enables: dev server renders → agent reads layout →
edits CSS → diff shows the actual effect in px → verify gates "done".
Note: snapshot/diff resolve a relative dir (default .csstruth) against
the MCP server's working directory. With a globally-registered server, pass
an absolute dir.
Use from the CLI (CI / scripts)
csstruth verify http://localhost:3000 # the one-call gate, exit 1 on FAIL
csstruth check http://localhost:3000 --viewports 375x800,1280x800
csstruth layout http://localhost:3000 --selector main
csstruth explain http://localhost:3000 --selector .sidebar --property width
csstruth snapshot http://localhost:3000 --name home --dir .csstruth
csstruth diff http://localhost:3000 --name home --dir .csstruth
csstruth blame --root . --page index.html --selector .sidebar
csstruth watch http://localhost:3000
csstruth baseline http://localhost:3000 --file .csstruth-baseline # once, to adopt on a non-clean page
csstruth verify http://localhost:3000 --baseline .csstruth-baselinecheck/verify exit 1 on violations — drop them straight into CI. On a page
that isn't fully clean yet, add --baseline (see Adopting on a brownfield
page above) so CI only fails on NEW violations, not the backlog.
Use watch from a background shell (agents)
watch is a long-lived streaming process, which doesn't fit MCP's
request/response shape — it's CLI only. An agent (or a human) drives it by
starting it in a background shell and reading the stream instead of
re-running diff/check after every edit:
csstruth watch http://localhost:3000 & # or your shell/agent's background-job equivalentThen read the shell's output as you edit — a quiet stream means nothing
changed; a block under a [HH:MM:SS] timestamp names exactly what moved and
which violations appeared or resolved. Stop it with Ctrl+C (or a signal to
the background job) when you're done; it shuts Chrome down cleanly on exit.
Claude Code skill
skills/csstruth/SKILL.md encodes the working doctrine (snapshot before
editing, explain before touching a cascade you didn't write, diff after
every edit, verify before done). Install it user-wide:
cp -r skills/csstruth ~/.claude/skills/Development
npm run typecheck # strict TS over src + test
npm test # 111 tests against real headless Chrome — fixture pages with planted bugs
npm run build # emits dist/Every fixture in fixtures/ is a page with a deliberately planted layout bug
(or a deliberately clean control); the tests assert csstruth finds exactly
what was planted. The design docs and per-release plans live in
docs/superpowers/.
Limitations
Chromium-only (CDP is the only engine API exposing the full cascade with source positions). Static layout truth — no animation timing. Framed content (iframes) is not walked. Heuristic ceilings are commented in the source where they live.
License
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-qualityDmaintenanceEnables AI agents to automate and debug real Chromium browsers with capabilities like screenshots, video recording, performance analysis, visual regression testing, and OCR text extraction.13
- Alicense-qualityDmaintenanceEnables AI agents to see, analyze, and visually verify web page changes through pixel-perfect diffing, theme extraction, layout analysis, and interactive element detection.2MIT
- AlicenseAqualityDmaintenanceLets AI agents visually inspect web elements, test CSS edits in real-time, and iterate until pixel-perfect, functioning like browser DevTools for debugging UI issues.151MIT
- Alicense-qualityBmaintenanceDeterministic screenshot diffing for AI coding agents. Extract design tokens, diff implementations vs reference, get CSS fix suggestions.1144MIT
Related MCP Connectors
Browser-backed QA with evidence and fix-ready reports for coding agents.
Capture screenshots, detect visual regressions between page versions, and analyze with AI.
Image + screenshot API for agents: HTML/CSS to images or PDF, screenshot any URL, verify text.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ikraamg/csstruth'
If you have feedback or need assistance with the MCP directory API, please join our Discord server