PlayGuardThe MCP proxy for Playwright MCP and Figma MCP
One MCP server replaces both. It revives dead browser sessions and strips 70–90% of snapshot tokens before any of it reaches your agent.
{"mcpServers": {"playguard": {"command": "npx","args": ["-y", "playguard"],"env": {"PLAYGUARD_SCREENSHOTS": "redirect","FIGMA_MCP_CMD": "npx @figma/mcp","FIGMA_API_KEY": "your-figma-api-key","FIGMA_CACHE_TTL": "60000"}}}}
Drop it in · MIT licensed · Node 18+ · Tests in CI
Works with Claude Code · Claude Desktop · Cursor · Codex · any MCP client
What happens to every tool call
Unified entry
One MCP server for everything. Calls are routed by prefix — the agent config stays one entry.
Crashes intercepted
Dead-session errors never reach the agent. PlayGuard restarts the browser, restores the last URL, retries the call.
Responses shrunk
Snapshots are compacted to interactive elements, page changes become diffs, Figma JSON is deduped and stripped.
Nothing fetched twice
Snapshots prefetch in the background, evals are cached by TTL, and every call lands in NDJSON logs for npm run analyze.
Same page, minus the noise
Removed lines are static text and decorative images — they carry no [ref=], so the agent can't interact with them anyway. Every interactive element survives, always. Need the full tree? PLAYGUARD_COMPACT=false returns the raw snapshot.
Design files, minus the bloat
100 instances of the same button? The agent gets one full definition + 99 references with overrides. Geometry blobs become _svgRef — the shape id without thousands of path coordinates. Layout structure is untouched. Still over budget? The tree is trimmed structurally, not sliced as text — oversized branches collapse to a stub the agent can re-fetch by id, so nothing vanishes silently.
Figma vs. the live DOM, in numbers
playguard_compare_design reads a Figma node and the DOM element that implements it, then reports the exact deltas. No screenshots, no eyeballing, no "looks about right".
It picks the properties
A TEXT layer is compared on typography and colour; a container on background, padding, radius and shadow. Pass properties[] only when you disagree.
Box values compare side by side
8px 8px 0 0 never slips through as a match for 8px. Tolerances for size and per-channel colour are yours to set.
Unset is not unknown
A shadow Figma never defined is still checked against the browser, so a stray value gets caught. margin is reported as unknown — Figma has no such concept.
Auto-map never guesses
Layers match DOM by data-figma-id, data-testid, id, class or exact text. A selector hitting zero or several elements is reported unmapped, not silently attached to the wrong node.
One proxy, six upgrades
Auto Recovery
Dead browser sessions are revived automatically. The agent never sees a crash.
Token Optimization
Compact snapshots and delta diffs cut response size 70–90%.
Smart Caching
Snapshot prefetch and eval caching skip redundant round-trips.
Screenshot Control
Redirect, warn, block, or allow screenshot calls per policy.
Figma Optimizer
Strips metadata, dedupes components, and drops SVG geometry noise.
Design Diff
Compares a Figma node against the live DOM and reports the exact mismatches.
What changes the day you add it
Every number above comes from npm run bench and the NDJSON call log PlayGuard writes on every run — no LLM in the loop, just measured bytes and milliseconds. Run npm run analyze on your own logs to see your numbers.
Drop it into your agent's config
{"mcpServers": {"playguard": {"command": "npx","args": ["-y", "playguard"],"env": {"PLAYGUARD_SCREENSHOTS": "redirect","FIGMA_MCP_CMD": "npx @figma/mcp","FIGMA_API_KEY": "your-figma-api-key","FIGMA_CACHE_TTL": "60000"}}}}
npm run analyze
Node 18+. Nothing to build.
npx pulls the latest version from npm the moment your agent starts it — no clone, no build step, no manual updates.
The questions you're about to ask
Do I have to replace my Playwright MCP setup?
You remove it. PlayGuard bundles Playwright MCP as a dependency and spawns it itself — adding both to your config runs two browsers. One entry replaces both.
What if I don't use Figma?
Leave FIGMA_MCP_CMD unset and PlayGuard runs browser-only, with no behaviour changes. The Figma optimizer never loads.
Can it break my agent by hiding something?
Compaction keeps every line carrying a [ref=] plus structural landmarks — exactly what an agent needs to navigate. Set PLAYGUARD_COMPACT=false and you get the raw tree back. Budget trimming collapses oversized branches into re-fetchable {id, name, type, _stub} markers instead of dropping them, so nothing disappears silently.
What does the proxy cost me?
About 1–3 ms per Playwright call, measured by npm run bench. Against a snapshot that shrinks from 300 KB to 30 KB, that is not the number worth worrying about.
Where do my API keys go?
Nowhere but the child process. FIGMA_API_KEY is forwarded to your Figma MCP exactly as you set it. PlayGuard runs locally over stdio, talks to no server of its own, and phones nothing home.
How do I know the savings are real?
Every call is logged to logs/YYYY-MM-DD.ndjson with raw and kept byte counts. npm run analyze turns your own logs into a report — cache hits, bytes saved, latency percentiles per tool.
Is it maintained, and can I read it?
MIT licensed, source on GitHub, CI runs the full suite on every push and PR — the compactor, all 8 Figma optimizer modules, crash detection, caching, and design-diff extraction each have tests.
What about screenshots?
Your call: allow, warn (default), block, or redirect — the last swaps a screenshot for a snapshot, with {visual:true} as the escape hatch when you genuinely need pixels.
Want the long version? How PlayGuard works walks through every mechanism in full — compaction, recovery, the eight Figma modules, and every environment variable.
Stop paying for noise.
One proxy in your config. 70–90% fewer snapshot tokens. Zero crashes seen by your agent.
MIT licensed · runs locally over stdio · nothing phoned home · read every line before you trust it.