Skip to main content
Glama

PlayGuard

A proxy MCP: one MCP server in your client config that runs several MCP servers under the hood, exposes all their tools through a single connection, and optimizes their responses.

You add playguard to your MCP client once. Which upstream MCPs it starts is decided by environment variables in that one config entry — an upstream without its variable is not started and its tools are not listed. Every response passes through PlayGuard on the way back, which adds:

  • One connection, several upstreams — Playwright MCP (bundled, always on), Figma (Framelink or the official desktop server), Chrome DevTools MCP, Shopify Dev MCP and Context7. A failing optional upstream only removes its own tools; the others keep working

  • Token optimization — compact/delta/UNCHANGED snapshots, Figma tree trimming, network-list summaries, rewritten documentation answers; browser snapshot cost drops 70–90%

  • Session recovery — dead browser sessions and crashed upstream processes are revived; the agent doesn't see the crash

  • Caching — snapshot, browser_evaluate, Figma and on-disk documentation caches

  • Screenshot policy — redirect, warn, block, or allow screenshot calls

  • Logged savings — every call is written to an NDJSON log; npm run analyze reports tokens saved per upstream

  • Two tools of its own — playguard_a11y_audit (axe-core in the Playwright page) and playguard_compare_design (Figma node vs live DOM element, Framelink only)

Works with Claude Code, Claude Desktop, Cursor, Codex, or any MCP-compatible agent.

Website: playguard.dev · Docs: How PlayGuard works


Upstream MCPs at a glance

Upstream

Enable with

What PlayGuard optimizes

Details

Playwright MCP (@playwright/mcp, bundled)

always on

Compact / delta / UNCHANGED snapshots, snapshot filters, session recovery, screenshot policy, eval cache, --codegen none, batching hints

Browser

Figma, Framelink (figma-developer-mcp)

FIGMA_MCP_CMD + FIGMA_API_KEY (any Figma plan)

Optimizer modules, structural budget trim, cache, playguard_compare_design

Figma Optimizer

Figma, official desktop server

FIGMA_MCP_URL=http://127.0.0.1:3845/mcp (no key; Figma desktop app + Dev/Full seat on a paid plan)

Passthrough, whole responses, no cache

Official Figma MCP

Chrome DevTools MCP

DEVTOOLS_MCP_CMD

Network summary, request trim, snapshot compaction, trace trim, error hints, privacy launch defaults

Chrome DevTools MCP

Shopify Dev MCP

SHOPIFY_DEV_MCP_CMD

Response rewrites, disk cache, UNCHANGED repeats, compacted tools/list

Documentation MCPs

Context7

CONTEXT7_MCP_CMD

Response rewrites, disk cache, UNCHANGED repeats, compacted tools/list

Documentation MCPs

Figma is one option at a time: Framelink or the official desktop server (FIGMA_MCP_URL wins if both are set). Figma's remote server (mcp.figma.com) can't be proxied — see Official Figma MCP.

Choosing upstreams

The env block of the playguard entry decides what runs. Nothing to install separately: Playwright MCP ships with PlayGuard, the others are launched through the command you give. For example, Playwright + Framelink Figma + Shopify Dev:

{
  "mcpServers": {
    "playguard": {
      "command": "npx",
      "args": ["-y", "playguard@latest"],
      "env": {
        "PLAYGUARD_SCREENSHOTS": "redirect",
        "FIGMA_MCP_CMD": "npx",
        "FIGMA_MCP_ARGS": "-y figma-developer-mcp --stdio",
        "FIGMA_API_KEY": "your-figma-api-key",
        "SHOPIFY_DEV_MCP_CMD": "npx -y @shopify/dev-mcp@latest"
      }
    }
  }
}

Remove a variable to drop that upstream. Restart the MCP client after changing the config — the server and its upstreams are started once per client session.


Related MCP server: MCP Browser Bridge

Architecture

Claude Code / Claude Desktop / Cursor / Codex
           │
           │  MCP (stdio) — one connection
           ▼
   ┌───────────────────┐
   │     PlayGuard     │  proxy MCP
   │                   │
   │  • router         │  browser_*          → Playwright MCP        (always)
   │  • recovery       │  Figma tools        → Framelink / Figma     (FIGMA_MCP_CMD / _URL)
   │  • optimizers     │  DevTools tools     → Chrome DevTools MCP   (DEVTOOLS_MCP_CMD)
   │  • caches         │  Shopify docs tools → Shopify Dev MCP       (SHOPIFY_DEV_MCP_CMD)
   │  • analytics      │  Context7 tools     → Context7              (CONTEXT7_MCP_CMD)
   │  • own tools      │  playguard_a11y_audit, playguard_compare_design
   └─────────┬─────────┘
             │  one child process per enabled upstream
   ┌─────────┼───────────┬──────────┬───────────┬──────────┐
   ▼         ▼           ▼          ▼           ▼          ▼
Playwright  Figma     Chrome     Shopify    Context7   (each optional
  MCP       MCP      DevTools    Dev MCP                except Playwright)
   │                    │
   ▼                    ▼
Chromium / Firefox / WebKit    Chrome (its own instance)

Every upstream except Playwright is optional. If none of FIGMA_MCP_CMD, FIGMA_MCP_URL, DEVTOOLS_MCP_CMD, SHOPIFY_DEV_MCP_CMD and CONTEXT7_MCP_CMD is set, PlayGuard runs with Playwright only. Playwright MCP is bundled as a dependency — no separate installation required. Optional upstreams start lazily and are isolated from each other; tools/list waits a bounded time for a slow one and announces its tools later with tools/list_changed.


Features

Browser (Playwright)

Automatic Session Recovery

PlayGuard intercepts dead-session errors before the agent sees them:

Target.*closed · Browser.*closed · connect ECONNREFUSED · crashed

It restarts Playwright MCP, restores the last URL, and retries the call transparently. Two concurrent calls during a crash → one restart, both calls continue.

Compact Snapshots

browser_snapshot normally returns the full accessibility tree. PlayGuard keeps only lines with [ref=] (interactive elements) and structural landmarks (nav, main, form, dialog, …). Static text and decorative images have no refs and are not needed for navigation.

[PlayGuard compact: 312/1840 lines, ~83% removed, 124.3KB→14.1KB]

Snapshot Filtering

For pages where even the compact tree is large, browser_snapshot accepts optional params to narrow the result to one subtree:

  • section — return only the landmark subtree whose label starts with or contains this string (e.g. "form", "navigation \"Footer\"")

  • around — return only the landmark subtree containing this [ref=N], useful once you know a specific element's ref

  • depth — cap the returned subtree's depth (combine with section/around for a high-level overview first, or use alone to flatten the whole tree)

Falls back to the full snapshot with a [PlayGuard: ... not found] warning if the requested section/ref doesn't exist. Switching filters between calls always forces a fresh snapshot — it never returns a stale UNCHANGED/cached result from a differently-filtered request.

Smart Wait

Set PLAYGUARD_SMART_WAIT=1 to have browser_snapshot retry when a page looks mid-load — few interactive refs plus a loading/spinner/skeleton indicator in the text. Retries PLAYGUARD_SMART_WAIT_MAX_RETRIES times, PLAYGUARD_SMART_WAIT_MS apart, before returning (with a warning if it still looks like it's loading). Off by default — it adds latency to every snapshot call.

Set PLAYGUARD_COMPACT=false to receive the raw snapshot.

Delta Snapshots

When a page changes only slightly, PlayGuard returns a diff instead of the full snapshot:

[PlayGuard delta: +3 added, 1 removed, ~91% saved]
ADDED:
  - button "Submit" [ref=47]
REMOVED:
  - button "Loading..." [ref=44]

Snapshot Cache + Prefetch

An unchanged page returns UNCHANGED without hitting the browser.

Optional prefetch (PLAYGUARD_PREFETCH_SNAPSHOT=true, off by default since 0.8.0): after browser_navigate, PlayGuard fetches a snapshot in the background so the next browser_snapshot is served from cache. It only pays off for agents that snapshot right after navigating — any browser_evaluate or action in between discards it, and the background call competes with that action for the browser.

Shorter Playwright Responses

PlayGuard starts the bundled Playwright MCP with --codegen none, which drops the ### Ran Playwright code block from every response (80–150 characters per call). Not applied to a custom PLAYWRIGHT_MCP_CMD or when --codegen is already in PLAYWRIGHT_MCP_ARGS; set PLAYGUARD_CODEGEN=keep to restore it.

Batching Hints

Every browser_evaluate / browser_run_code_unsafe call waits at least ~0.5 s inside Playwright MCP for the page to settle. PlayGuard appends a one-line hint to those tools' descriptions (and to browser_wait_for) nudging the agent to combine checks into fewer calls. PLAYGUARD_HINTS=false turns this off.

Screenshot Policy

Mode

Behavior

warn (default)

Screenshot executes; warning written to stderr

redirect

Replaced with browser_snapshot. Pass {visual:true} for a real screenshot; a call with filename, element/ref/target or fullPage always gets pixels

block

Blocked with an error; agent is told which env var to change

allow

No restriction

Eval Cache + Output Compaction

Repeated browser_evaluate calls with the same script on the same URL are served from cache (configurable TTL). Large eval output is truncated to a character limit to avoid blowing the context window.

Accessibility Audit

playguard_a11y_audit runs axe-core (bundled) inside the page the browser_* tools have open and returns a short report instead of axe's raw JSON:

[PlayGuard a11y: 6 violations (6 elements) · 7 rules passed · axe 4.14.0 · wcag2a,wcag2aa,… · https://shop.test/]

critical image-alt — Images must have alternative text (1)
  - img  <img src="…" class="hero">
    Element does not have an alt attribute (+4 more)
  https://dequeuniversity.com/rules/axe/4.14/image-alt

serious  color-contrast — Elements must meet minimum color contrast ratio thresholds (1)
  - main > p  <p style="color:#bbb">Low contrast text</p>
    Element has insufficient color contrast of 1.91 (foreground color: #bbbbbb, …). Expected contrast ratio of 4.5:1

Rules are sorted by impact; each lists at most 5 elements (the rest are counted) with a CSS selector, a 140-char html excerpt and the first way to fix it. Rules axe could not decide are named under "Needs manual review".

Param

Default

selector

whole page

Audit only this part, e.g. #cart-drawer — open a dialog first, then audit it

tags

wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa

Replaces the default. To add best-practice (non-WCAG advice) or wcag2aaa, pass the defaults plus it. A single string is accepted; any tag that matches no axe rule (a typo) is an error, not a clean report

Automated checks don't cover keyboard flow or screen-reader wording. The page keeps axe loaded after the first audit.


Figma Optimizer

PlayGuard intercepts Figma MCP responses and runs them through an optimization pipeline before delivering to the agent.

[PlayGuard figma: -68% (284.0KB→91.0KB)]

Note: Modules 1–4 and 6 target raw Figma REST API JSON (document.children, componentId, fillGeometry). If your Figma MCP upstream returns pre-simplified YAML (e.g. Framelink figma-developer-mcp), those modules find nothing to strip — Module 8 handles that shape instead — and inBytes/outBytes are still measured from the actual upstream response text, so the automatic YAML→JSON reformatting (typically ~2x on its own) and Module 5's metadata trim both still count as real savings. parseSkip in the NDJSON log means the response body couldn't be parsed as JSON or YAML at all, not that the optimizer no-op'd. Framelink 0.13+ defaults to a tree text format that is neither, so PlayGuard starts the Figma child with OUTPUT_FORMAT=json unless you set OUTPUT_FORMAT yourself (yaml and json both work; tree disables the optimizer and playguard_compare_design).

Module 1 — Metadata Cleaner: Removes fields irrelevant to layout: createdAt, updatedAt, creator, thumbnailUrl, pluginData, sharedPluginData, exportSettings, reactions, interactions, etc.

Module 2 — Invisible Layer Pruner: Recursively removes nodes where visible === false or opacity === 0.

Module 3 — Component Deduplication: Repeated instances of the same component are collapsed to a reference. 100 buttons → first full definition + 99 references with overrides only.

{ "type": "INSTANCE", "name": "Button/Primary", "_ref": "123:4", "overrides": [] }

Module 4 — SVG Refs: Replaces inline SVG geometry (fillGeometry) with { "_svgRef": "nodeId" }. The agent gets the shape identifier without thousands of path coordinates.

Module 6 — Layout Compressor: Removes absolute x/y from nodes inside Auto Layout containers — they are redundant because position is determined by layoutMode, gap, and padding.

Module 5 — Top-Level Metadata Trim: Drops metadata.thumbnailUrl (a signed, single-use preview URL) and metadata.lastModified from pre-simplified upstream shapes like Framelink's { metadata, nodes, globalVars } — fields Module 1 can't reach because it only walks document/children, not a sibling metadata key.

Module 8 — Framelink Shape Optimizer: Targets the pre-simplified { metadata, nodes[], globalVars.styles } shape (Framelink figma-developer-mcp), where Modules 2/4/6 find nothing to strip. 8a: sibling subtrees identical except for ids collapse to { id, name, _sameAs: firstId } (repeated cards/icons); structural copies that differ only in text/name/position collapse to the same stub plus a _textDiff map. 8b: no-op layout styles ({ mode: "none", sizing: {} }) are dropped from globalVars.styles along with node refs to them. 8c: float noise in styles is rounded to 2 decimals ("1.3999999364217122em" → "1.4em"); node text is never touched.

Module 7 — Budget Trim: If the optimized tree still exceeds FIGMA_TEXT_COMPACT, it's trimmed structurally instead of sliced as text. Budget is allocated depth-first, proportional to each branch's size; a branch that doesn't fit collapses to an {id, name, type, _stub:true} marker instead of being silently dropped, so every top-level section stays visible and the agent can re-fetch a stubbed branch by its id. Only a genuinely unparseable response (parseSkip) falls back to a raw text slice.


Official Figma MCP (desktop server)

Instead of Framelink you can route Figma's own MCP server through PlayGuard. No API key goes in the config: the Figma desktop app's login authorizes it.

"env": {
  "FIGMA_MCP_URL": "http://127.0.0.1:3845/mcp"
}
  1. Open the Figma desktop app (latest version), switch to Dev Mode (Shift+D) and click Enable desktop MCP server in the Inspect panel.

  2. Needs a Dev or Full seat on a paid plan — Figma's requirement, not PlayGuard's.

Its tools (get_design_context, get_metadata, get_variable_defs, get_screenshot, …) are merged into the tool list. They return generated code and XML rather than a JSON style tree, so PlayGuard passes them through whole — never sliced by FIGMA_TEXT_COMPACT. FIGMA_CACHE_TTL does not apply either: without a nodeId the server answers from the current selection in the app and reflects live edits. playguard_compare_design needs Framelink; it is not listed in this mode and says so if called. If the app is closed or started after PlayGuard, a Figma call returns how to turn the server on; once it is up, the next call connects, its tools are registered and the client is told to re-list (tools/list_changed). A server error for one call never drops the connection other calls are using; only a transport failure does, and a call is retried once only when it certainly never reached the server. Credentials in the URL are kept out of messages and logs. FIGMA_MCP_URL wins when FIGMA_MCP_CMD is also set.

Framelink (FIGMA_MCP_CMD)

Official desktop (FIGMA_MCP_URL)

Config

FIGMA_API_KEY

URL only, sign-in is the Figma app's

Figma plan

any, free included

Dev/Full seat on a paid plan

Returns

JSON node tree

React + Tailwind code, variables, screenshots

PlayGuard optimizer, playguard_compare_design

yes

no (passthrough)

Figma's remote server (https://mcp.figma.com/mcp, browser sign-in) can't go through PlayGuard: Figma only admits clients listed in its MCP catalog. Setting that URL is refused at startup with this explanation. Add it to your MCP client directly, next to PlayGuard (Claude Code: claude mcp add --transport http figma https://mcp.figma.com/mcp); its responses then skip PlayGuard. On the remote server, Figma limits Starter-plan users and View/Collab seats to 6 tool calls a month.


Design Diff

playguard_compare_design compares a Figma node against the live DOM element that implements it and reports the exact differences — no screenshots, no eyeballing.

[PlayGuard design diff: 42:1337 → [data-testid="login-btn"] 2 mismatches, 6 matches]

  fontSize:        Figma 16px              → Browser 14px              ⚠️  MISMATCH (+2.0px)
  color:           Figma rgb(255, 255, 255)→ Browser rgb(255, 255, 255) ✓  MATCH
  backgroundColor: Figma rgb(25, 118, 210) → Browser rgb(25, 118, 210) ✓  MATCH
  padding:         Figma 12px 24px 12px 24px → Browser 12px 16px 12px 16px ⚠️  MISMATCH (Δright:+8px, Δleft:+8px)
  borderRadius:    Figma 8px               → Browser 8px 8px 8px 8px   ✓  MATCH

Three modes:

Mode

Arguments

Use

Single

figmaNodeId + browserSelector

One element

Batch

pairs: [{figmaNodeId, browserSelector}, …]

Several at once; one bad selector doesn't sink the rest

Auto-map

figmaNodeId + autoMap: true

Point at a component; layers beneath are matched to DOM elements by data-figma-id, data-testid, id, class, or exact text

Notes on how it decides:

  • Properties are auto-selected per node. A TEXT layer gets its typography and colour; a container gets backgroundColor/padding/borderRadius/boxShadow. Pass properties[] to override.

  • Tolerances. Sizes compare within PLAYGUARD_DESIGN_DIFF_TOLERANCE_PX, colours within PLAYGUARD_DESIGN_DIFF_TOLERANCE_COLOR per channel. Box properties (padding, margin, borderRadius, borderWidth) compare side-by-side, so 8px 8px 0 0 never passes as 8px.

  • Unset ≠ unknown. A property Figma leaves unset but CSS zero-defaults (no shadow, no border) is still checked, so a stray browser value is caught. margin is the exception — Figma has no margin concept, so it is reported as unknown rather than as a defect.

  • Typography follows the label. A button's font size is read from its TEXT layer at any depth, and its background is never mistaken for its text colour.

  • width/height are never auto-selected — they depend on the viewport, not the design. Request them explicitly and a warning rides along.

  • Auto-map never guesses. A candidate selector matching zero or several elements is reported as unmapped, not silently attached to the wrong element.

  • A missing node is an error, not a substitute. A figmaNodeId the upstream response doesn't contain fails that pair, rather than falling back to whatever the response's root frame happens to be.

Both upstream shapes are handled: the raw Figma REST API node JSON (any upstream that returns it unchanged) and Framelink's pre-simplified globalVars.styles form (figma-developer-mcp).


Chrome DevTools MCP (optional)

Chrome DevTools MCP (chrome-devtools-mcp, by Google) is an opt-in third upstream. Its tools (take_snapshot, list_network_requests, get_network_request, list_console_messages, performance_start_trace, lighthouse_audit, get_css_styles, …) are merged into PlayGuard's tool list under their own names and routed to it.

Use it for what Playwright MCP does not cover well: request/response headers and bodies, performance traces and insights, Lighthouse audits, computed CSS. For clicking through a flow, keep using the browser_* tools — they have session recovery and the cheaper snapshot pipeline.

"env": {
  "DEVTOOLS_MCP_CMD": "npx -y chrome-devtools-mcp@latest",
  "DEVTOOLS_MCP_ARGS": "--headless --isolated"
}

PlayGuard starts the upstream lazily (on the first tools/list or call), so the first start can include an npx download. tools/list waits up to PLAYGUARD_LIST_WAIT_MS (6 s) for it, then lists without it and sends notifications/tools/list_changed once its tools are in. The same bounded wait applies to the Figma upstream. A DevTools failure only removes the DevTools tools — Playwright and Figma keep working.

Two browsers

chrome-devtools-mcp launches its own Chrome, separate from Playwright's. Pages, cookies, logins and element ids are not shared: DevTools uid= values only work with DevTools tools, [ref=] values only with browser_* tools, and a DevTools tool only sees pages opened with navigate_page/new_page. PlayGuard says this in the server instructions and on new_page, navigate_page and list_pages, and points the agent at browser_network_requests / browser_console_messages for a page driven by browser_* tools.

By default the upstream keeps a persistent profile in ~/.cache/chrome-devtools-mcp/. A second chrome-devtools-mcp instance on the same profile (another agent, another client) fails with The browser is already running for … Use --isolated to run multiple browser instances. — pass --isolated in DEVTOOLS_MCP_ARGS for a throwaway profile.

Advanced: one shared Chrome. To make both upstreams drive the same browser, start Chrome yourself with remote debugging and point both at it:

chrome --remote-debugging-port=9222 --user-data-dir=/tmp/pg-chrome
"env": {
  "PLAYWRIGHT_MCP_ARGS": "--cdp-endpoint http://127.0.0.1:9222",
  "DEVTOOLS_MCP_CMD": "npx -y chrome-devtools-mcp@latest",
  "DEVTOOLS_MCP_ARGS": "--browserUrl http://127.0.0.1:9222"
}

Pages and cookies are then shared, but ids still are not (each upstream numbers elements its own way). Nobody owns the browser any more: if it crashes or you close it, PlayGuard's Playwright session recovery cannot relaunch it, and neither can chrome-devtools-mcp. The flags exist in @playwright/mcp 0.0.76 (--cdp-endpoint) and chrome-devtools-mcp 1.12 (--browserUrl).

What PlayGuard changes

Tool

Change

Get the upstream response

tools/list

Drops $schema from every schema; adds the raw param; adds the two-browser note to the navigation tools and a summary note to list_network_requests

PLAYGUARD_DEVTOOLS_OPTIMIZE=false

take_snapshot (and any action with includeSnapshot: true)

Same-origin urls become paths, image urls are dropped (kept with verbose: true), text/image leaves that repeat their parent's name are dropped — every uid of an interactive node survives. A repeat of an unchanged page is one [PlayGuard: UNCHANGED …] line; a small change is ADDED:/REMOVED: lines only. Any non-read-only call clears the UNCHANGED shortcut

raw: true, or PLAYGUARD_COMPACT=false / PLAYGUARD_DELTA=false

list_network_requests

Without pageSize/pageIdx/resourceTypes/includePreservedRequests, a list longer than PLAYGUARD_DEVTOOLS_NETWORK_MAX (30) rows becomes a summary: the main document, failed (4xx/5xx/net::ERR_*) and pending rows, per-host counts, and how to page

Pass any of those params, e.g. pageSize: 100

get_network_request

Noisy headers (accept*, user-agent, sec-ch-ua*, sec-fetch-*, report-to, alt-svc, …) collapse to one line; a CSP over 200 chars is replaced by its length; a non-JSON body over PLAYGUARD_DEVTOOLS_BODY_MAX (3000) chars is cut with a pointer to responseFilePath. Cookies, location, content-type, cache-control, authorization and every x-* header are kept. JSON bodies are never cut (upstream already caps bodies at 10K)

raw: true

performance_start_trace / performance_stop_trace

Drops "example question" lines and markdown link targets; insight set ids and names stay

raw: true

take_screenshot

PLAYGUARD_SCREENSHOTS applies. In redirect mode the call becomes take_snapshot unless it passes visual: true, filePath, uid or fullPage

visual: true

Errors

Five opaque upstream errors (element not interactive, stale uid, no page selected, path outside the workspace, unknown reqid) get a one-line [PlayGuard] hint with the next step

PLAYGUARD_DEVTOOLS_OPTIMIZE=false

raw is a PlayGuard-only param on take_snapshot, get_network_request and the two trace tools; it is stripped before the call reaches the upstream. PLAYGUARD_DEVTOOLS_OPTIMIZE=false turns off every rewrite above (screenshot policy and the restart notices still apply).

Measured with chrome-devtools-mcp 1.12 on real sites (upstream → sent):

Response

Saving

list_network_requests

Shopify store 34K → ~1K chars (−97%); Wikipedia 7.6K → ~0.6K (−92–95%). Lists of ≤30 rows are untouched

get_network_request, HTML document

−63–72%

get_network_request, JSON body

~−20% (headers only)

take_snapshot

−3% (small clean page) to −40% (image-heavy store). A repeated unchanged snapshot: one ~40-char line instead of 10–46K. After a small change: only the added/removed lines

Performance trace

−13%

DevTools tools/list

26.4K vs 28.9K chars: −3% from schema cleanup, plus --no-page-id-routing removes pageId from 30 of 32 tools

Launch defaults and privacy

Unless the same option is already in DEVTOOLS_MCP_CMD/DEVTOOLS_MCP_ARGS (any spelling: --x, --no-x, --x=false, camelCase), PlayGuard appends:

  • --no-usage-statistics — upstream otherwise sends usage statistics to Google

  • --no-performance-crux — upstream otherwise sends every traced URL to Google's CrUX API

  • --no-page-id-routing — removes the required pageId param from 30 of 32 tools; a single agent behind one proxy does not need it

  • --workspace <project root> (plus --workspace <output dir>/.qa when PLAYGUARD_OUTPUT_DIR is outside the project) — unless --workspace, --filesystemRoot or --allowUnrestrictedPaths is already given. Without it, chrome-devtools-mcp only lets file tools touch the OS temp dir

To keep an upstream default, pass the flag yourself (e.g. --usage-statistics), or set PLAYGUARD_DEVTOOLS_DEFAULTS=false to append none of the above (workspace included). Options set in a --config JSON file are not detected, and the appended CLI flags win over them.

The upstream process also gets:

  • A filtered environment. Only the MCP SDK's default set (PATH, HOME, …) plus CHROME_DEVTOOLS_MCP_*, PUPPETEER_*, npm_config_*, XDG_*, LC_*, proxy (HTTP(S)_PROXY, NO_PROXY), display and temp-dir variables. API keys and cloud credentials in PlayGuard's environment (FIGMA_API_KEY, …) are not forwarded.

  • CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS=1, which turns off the upstream's own daily npm check. Set it to an empty string in PlayGuard's env to re-enable it.

Files, timeouts, recovery

  • Paths. The upstream runs with .playguard/.qa as its working directory, so a relative filePath/responseFilePath/requestFilePath lands there. Absolute paths must be inside the project or the OS temp dir.

  • Timeouts. lighthouse_audit, performance_start_trace, performance_stop_trace and take_heapsnapshot get a 180 s request timeout instead of the SDK's 60 s.

  • Upstream process died (between calls or mid-call): PlayGuard respawns it once, reopens the last URL opened with navigate_page/new_page, retries the call, and prepends a notice that cookies, logins and page state are lost.

  • Chrome restarted by the upstream (chrome-devtools-mcp relaunches a crashed Chrome itself): nothing to respawn, but cached snapshot state is cleared and the response names the last URL so the agent can navigate_page back.

Every DevTools call is logged with server: "devtools"; npm run analyze reports them in their own section.

Documentation MCPs: Shopify Dev and Context7 (optional)

Two read-only documentation upstreams, each enabled by its own command (stdio only; remote/HTTP endpoints are not supported yet):

"env": {
  "SHOPIFY_DEV_MCP_CMD": "npx -y @shopify/dev-mcp@latest",
  "CONTEXT7_MCP_CMD": "npx -y @upstash/context7-mcp@latest"
}

Optional SHOPIFY_DEV_MCP_ARGS / CONTEXT7_MCP_ARGS are appended the same way as DEVTOOLS_MCP_ARGS. Their tools are merged into the tool list and routed to them; both start lazily, and a failing one leaves every other upstream working; calling one of its tools then returns [PlayGuard] … MCP is not running: <error> with what to check. A tool name already taken by another upstream stays there (a warning goes to stderr).

  • Context7 needs Node.js ≥ 20.18.1, the same minimum as PlayGuard. Without a key it allows ~200 calls/day per IP; set CONTEXT7_API_KEY (free at context7.com/dashboard) to raise it.

  • Shopify Dev sends tool inputs and results to shopify.dev by default; PlayGuard starts it with OPT_OUT_INSTRUMENTATION=true unless you set that variable yourself.

  • Environment. Each gets the MCP SDK's default set plus npm, proxy, locale and temp-dir variables. Context7 also gets CONTEXT7_API_KEY/CONTEXT7_API_URL; Shopify gets LIQUID_VALIDATION_MODE, OPT_OUT_INSTRUMENTATION, DO_NOT_TRACK, STOREFRONT_WEB_COMPONENTS, USE_LEGACY_SEARCH, SHOPIFY_AI_TOOLKIT_OPT_OUT_FILE. Other API keys and host session ids (CLAUDE_CODE_*) are not forwarded. Keys never reach the call log or the cache.

What PlayGuard changes

Tool

Change

Measured on recorded output

search_docs_chunks

Indented JSON → ## title / url / content blocks; drops score/domain, exact duplicate chunks, and a byte-identical chunk whose URL differs only in the API version (its URL goes on an also: line; content that differs at all is kept). Content is never cut

−19% to −20%

learn_shopify_api

Escaped \` fences are unescaped. Always returned whole, even on a repeat: it is the routing guide

−254 chars on liquid

validate, validate_theme

Never rewritten, never cached

—

Shopify tools/list

Hides feedback (telemetry only), drops the telemetry params model/user_prompt, makes conversationId optional — PlayGuard fills in one id per process when it is omitted

26.6K → 21.3K chars (−20%)

resolve-library-id

One line per library: id, title, snippets, reputation, benchmark, versions (minus __branch__* junk), description cut to ~90 chars. Every id and the order are kept

−48% to −50%

query-docs

GitHub Source: URLs become org/repo@ref:path; an exact duplicate snippet is dropped. Split only on the 32-dash separator; code is never touched

−1% to −2%

Context7 tools/list + instructions

Shorter descriptions; all params, types, required lists and annotations kept

5.9K → 2.6K chars (−56%)

Every rewrite checks the format first and passes the response through unchanged when it doesn't recognise it (logged as parseSkip). PLAYGUARD_DOCS_OPTIMIZE=false turns all of them off; raw: true on search_docs_chunks, learn_shopify_api, resolve-library-id and query-docs skips them for one call (stripped before forwarding).

Cache

search_docs_chunks, resolve-library-id and query-docs results are cached on disk in .playguard/.cache/docs/ for PLAYGUARD_DOCS_CACHE_TTL seconds (24 h; a version-pinned Context7 id like /vercel/next.js/v16.2.9 keeps 7×). The key is the tool and its arguments (minus conversationId); errors are never cached. An identical call to one of these three repeated within the same PlayGuard process returns a one-line UNCHANGED note instead of the whole answer again. The process outlives /compact and /clear and is shared by subagents, so the note tells the agent to call again with raw: true if it no longer has the earlier answer. Cache files are written atomically; expired ones are not swept.

Every call is logged with server: "shopify-dev" or "context7" (cached: true on a cache hit, repeat: true on an UNCHANGED note); npm run analyze reports per-tool savings for each.


Requirements

  • Node.js 20.18.1+

  • npm


Installation

No install needed — run via npx. For local development instead:

git clone https://github.com/ZenyaDAR/PlayGuard.git
cd playguard
npm install
npm run build

Usage

Claude Code

Add to .mcp.json in your project root, or register the server for all projects with claude mcp add --scope user:

Playwright only:

{
  "mcpServers": {
    "playguard": {
      "command": "npx",
      "args": ["-y", "playguard@latest"],
      "env": {
        "PLAYGUARD_SCREENSHOTS": "redirect"
      }
    }
  }
}

Playwright + Figma (Framelink):

{
  "mcpServers": {
    "playguard": {
      "command": "npx",
      "args": ["-y", "playguard@latest"],
      "env": {
        "PLAYGUARD_SCREENSHOTS": "redirect",
        "FIGMA_MCP_CMD": "npx",
        "FIGMA_MCP_ARGS": "-y figma-developer-mcp --stdio",
        "FIGMA_API_KEY": "your-figma-api-key",
        "FIGMA_CACHE_TTL": "60000"
      }
    }
  }
}

Do not add Playwright MCP separately — PlayGuard spawns it automatically from its bundled dependency.

Running from a local clone instead? Use "command": "node", "args": ["/path/to/playguard/dist/index.js"].

Staying up to date

Keep the @latest in "args": ["-y", "playguard@latest"]. Without it, npx may keep running whatever copy it cached first (older npm versions never re-check the registry for a bare package name), and a global or project-local install of playguard is used instead of the npx copy. With @latest, npx checks npm on every start and fetches new releases.

New versions are picked up when the MCP client restarts the server: restart Claude Code / Cursor / Claude Desktop after an update.

On startup PlayGuard also asks npm for the latest version (in the background, 3 s timeout). If the running copy is older, it prints how to update to stderr and prepends the same notice to the first tool result, so the agent tells you. Disable with PLAYGUARD_UPDATE_CHECK=false.

Still on an old version? Remove stale copies, then restart the client:

npm rm -g playguard          # if it was ever installed globally
npx clear-npx-cache          # or delete the _npx folder in your npm cache

Claude Desktop

Same JSON format. Config file locations:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Windows paths

Use forward slashes in JSON strings. Spaces in paths do not need escaping:

"args": ["C:/Users/YourName/projects/playguard/dist/index.js"]

Environment Variables

Playwright

Variable

Default

Description

PLAYGUARD_SCREENSHOTS

warn

allow / warn / block / redirect

PLAYGUARD_COMPACT

true

Set false to disable compact snapshots and receive the raw accessibility tree

PLAYGUARD_DELTA

true

Set false to disable delta snapshots

PLAYGUARD_DELTA_THRESHOLD

0.4

Fraction of lines that must change to trigger a full snapshot instead of a delta (0–1)

PLAYGUARD_TOKEN_BUDGET

0

Max tokens per snapshot; truncates on a line boundary so [ref=] tags are never split. 0 = off

PLAYGUARD_HINT_THRESHOLD

4

After N consecutive snapshots without any action, inject a hint listing available interactive refs

PLAYGUARD_PREFETCH_SNAPSHOT

false

Set true to prefetch a snapshot in the background after browser_navigate

PLAYGUARD_CODEGEN

—

Set keep to stop PlayGuard adding --codegen none to the bundled Playwright MCP

PLAYGUARD_HINTS

true

Set false to stop appending batching hints to tool descriptions

PLAYGUARD_SMART_WAIT

false

Set 1/true to retry a snapshot that looks mid-load (few refs + a loading/spinner indicator) instead of returning it as-is

PLAYGUARD_SMART_WAIT_MS

1000

Delay between smart-wait retries, in ms

PLAYGUARD_SMART_WAIT_MAX_RETRIES

3

Max smart-wait retries before giving up and returning the snapshot with a warning

PLAYGUARD_SMART_WAIT_MIN_REFS

5

A snapshot with at least this many [ref=] elements is never considered "still loading"

PLAYGUARD_EVAL_CACHE_TTL

500

browser_evaluate cache TTL in ms. 0 = off

PLAYGUARD_EVAL_COMPACT

10000

Max characters for eval output. 0 = off

PLAYWRIGHT_MCP_CMD

bundled binary

Override the Playwright MCP command

PLAYWRIGHT_MCP_ARGS

—

Extra arguments passed to Playwright MCP (space-separated; wrap an argument in "..." or '...' if it contains a space, e.g. a path). Passing --output-dir here takes precedence over PLAYGUARD_OUTPUT_DIR

PLAYGUARD_OUTPUT_DIR

.playguard/

Where all on-disk artifacts go, split by origin: .playguard/.qa/ for everything from the site under test (screenshots, traces, PDFs, downloads, session) and .playguard/.figma/ for Figma image exports. Defaults to .playguard/ in the project root instead of the upstream's .playwright-mcp/

PLAYGUARD_PROJECT_ROOT

nearest .git/package.json above cwd

The root .playguard/ is created in. process.cwd() is whatever the MCP client spawned the server with, so it is not trusted; set this to pin the location outright

PLAYGUARD_LOG_DIR

logs/

Override the NDJSON analytics log directory

PLAYGUARD_UPDATE_CHECK

true

Set false to skip the startup check for a newer version on npm (offline, CI)

Figma

Variable

Default

Description

FIGMA_MCP_CMD

—

Figma MCP launch command (Framelink). Unset (and no FIGMA_MCP_URL) = Figma disabled

FIGMA_MCP_URL

—

Official Figma desktop MCP server, http://127.0.0.1:3845/mcp. No key needed; wins over FIGMA_MCP_CMD. mcp.figma.com is refused (catalog-only)

FIGMA_MCP_ARGS

—

Extra arguments for Figma MCP (space-separated; wrap an argument in "..." or '...' if it contains a space)

FIGMA_CACHE_TTL

0

Figma response cache TTL in ms. 0 = off. Framelink only

FIGMA_SVG_REFS

true

Set false to keep SVG geometry inline

FIGMA_TEXT_COMPACT

10000

Max characters for the Figma text response. When the optimizer parsed the tree, an over-budget response is trimmed structurally (Module 7) — branches collapse to {id,name,type} stubs, never silently disappear. Only falls back to a raw text slice if the response never parsed (parseSkip). 0 = off

FIGMA_API_KEY

—

Forwarded to the Figma MCP child process

OUTPUT_FORMAT

json

Output format requested from Framelink (figma-developer-mcp). Must be json or yaml for the optimizer and Design Diff to parse it

Chrome DevTools

Variable

Default

Description

DEVTOOLS_MCP_CMD

—

Chrome DevTools MCP launch command, e.g. npx -y chrome-devtools-mcp@latest. Unset = DevTools disabled

DEVTOOLS_MCP_ARGS

—

Extra arguments for chrome-devtools-mcp (space-separated; wrap an argument in "..." or '...' if it contains a space), e.g. --headless --isolated

PLAYGUARD_DEVTOOLS_OPTIMIZE

true

Set false for a pure passthrough: no response rewriting, no tools/list compaction, no error hints

PLAYGUARD_DEVTOOLS_DEFAULTS

true

Set false to stop appending --no-usage-statistics, --no-performance-crux, --no-page-id-routing and --workspace to the upstream argv

PLAYGUARD_DEVTOOLS_NETWORK_MAX

30

list_network_requests with more rows than this (and no paging/filter params) is summarized. 0 = off

PLAYGUARD_DEVTOOLS_BODY_MAX

3000

Max characters of a non-JSON request/response body in get_network_request. 0 = off

PLAYGUARD_LIST_WAIT_MS

6000

How long tools/list waits for an optional upstream (Figma, DevTools, Shopify Dev, Context7) before listing without it; its tools follow via tools/list_changed

PLAYGUARD_SCREENSHOTS, PLAYGUARD_COMPACT, PLAYGUARD_DELTA and PLAYGUARD_DELTA_THRESHOLD apply to DevTools take_screenshot/take_snapshot too.

Shopify Dev and Context7

Variable

Default

Description

SHOPIFY_DEV_MCP_CMD

—

Shopify Dev MCP launch command, e.g. npx -y @shopify/dev-mcp@latest. Unset = disabled

SHOPIFY_DEV_MCP_ARGS

—

Extra arguments for it

CONTEXT7_MCP_CMD

—

Context7 MCP launch command, e.g. npx -y @upstash/context7-mcp@latest (Node.js ≥ 20.18.1). Unset = disabled

CONTEXT7_MCP_ARGS

—

Extra arguments for it

CONTEXT7_API_KEY

—

Forwarded to Context7 only; raises the anonymous ~200 calls/day limit

PLAYGUARD_DOCS_OPTIMIZE

true

Set false for a pure passthrough of both: no response rewriting, no repeat notes, no tools/list compaction

PLAYGUARD_DOCS_CACHE_TTL

86400

Seconds a doc lookup stays in .playguard/.cache/docs/ (7× for a version-pinned Context7 id). 0 = off

Design Diff

Variable

Default

Description

PLAYGUARD_DESIGN_DIFF_TOLERANCE_PX

2

Size difference, in px, still counted as a match

PLAYGUARD_DESIGN_DIFF_TOLERANCE_COLOR

5

Per-channel RGB difference still counted as a match


Configuration Examples

Headless browser + Figma with cache:

"env": {
  "PLAYWRIGHT_MCP_ARGS": "--headless",
  "FIGMA_MCP_CMD": "npx",
  "FIGMA_MCP_ARGS": "-y figma-developer-mcp --stdio",
  "FIGMA_API_KEY": "your-figma-api-key",
  "FIGMA_CACHE_TTL": "120000"
}

Firefox + block screenshots:

"env": {
  "PLAYWRIGHT_MCP_ARGS": "--browser firefox",
  "PLAYGUARD_SCREENSHOTS": "block"
}

Persist cookies between sessions:

"env": {
  "PLAYWRIGHT_MCP_ARGS": "--storage-state /tmp/browser-state.json"
}

Browser + Chrome DevTools (network, performance, Lighthouse):

"env": {
  "PLAYGUARD_SCREENSHOTS": "redirect",
  "DEVTOOLS_MCP_CMD": "npx -y chrome-devtools-mcp@latest",
  "DEVTOOLS_MCP_ARGS": "--headless --isolated"
}

Redirect screenshots + hard token cap:

"env": {
  "PLAYGUARD_SCREENSHOTS": "redirect",
  "PLAYGUARD_TOKEN_BUDGET": "4000"
}

Analytics

All tool calls are logged to logs/YYYY-MM-DD.ndjson. Run the report:

npm run analyze

Sample output:

── Snapshot token savings ────────────────────────────────
  Cache hits:      12/47 snapshots (8 from prefetch)
  Bytes saved by cache:    84.3 KB  (~21 075 tokens)
  Bytes saved by compact:  312.1 KB (~78 025 tokens)
  Total saved:             396.4 KB (~99 100 tokens)
  Reduction vs raw:        83%  (82.1 KB sent vs 478.5 KB without PlayGuard)

── PlayGuard interceptions ───────────────────────────────
  Screenshot → snapshot: 5 redirected (2 from cache, ~18 400 tokens saved)
  Eval cache hits:       8/11 evaluates (~240ms saved at 30ms avg)

── Figma optimizer ───────────────────────────────────────
  Calls:       24 total  (6 cache hits, 18 optimized)
  Tokens in:   ~71 000  →  out: ~22 750  (saved ~48 250, -68%)

── Chrome DevTools MCP ───────────────────────────────────
  Calls:       38 total  (2 errors, 1 raw:true)
  Tokens in:   ~61 000  →  out: ~35 400  (saved ~25 600, -42%)
  Snapshots:   9  (3 UNCHANGED, 2 delta)
  network lists summarized   2
  request details trimmed    4

── Latency by tool ───────────────────────────────────────
| tool              | count | errors | err% | intercepted | avg ms | p50 | p95 |

Each NDJSON line includes all fields for downstream analysis:

  • Playwright: rawBytes, keptBytes, savedBytes, delta, cacheHit, prefetchHit, snapCount, scriptHash; on errors errText (≤200 chars) and thrown for exceptions; waitKind/waitTime for browser_wait_for; codeChars/codeHash/codeSleeps for browser_run_code_unsafe; fullPage/type for screenshots

  • Figma: fileKey, nodeId, inBytes, outBytes, savedTokens, metaKeysDeleted, invisiblePruned, svgRefsReplaced, instancesCollapsed, layoutCoordsRemoved

  • Chrome DevTools: server: "devtools", url, rawBytes/keptBytes (every rewritten response, not only snapshots), cacheHit, delta/deltaAdded/deltaRemoved, summarized/rows, trimmed, raw, redirected/visual/blocked, hinted, retried/revived, chromeRestarted, screenshotBytes

The DevTools counts in the sample are illustrative. For reference, one full end-to-end DevTools session on a pre-release build reported −42% — indicative, not a benchmark; your ratio depends on which tools the agent calls.


Benchmark

npm run bench

Measures proxy overhead, snapshot vs screenshot size, cache hit rate, and crash recovery time. No LLM in the loop — raw numbers only.


Tests

npm test

Covers collapseRuns, compactSnap (including token budget boundary), the full Figma optimizer pipeline (optimizeFigmaResponse), the structural budget trim (budgetTrimFigma), and the artifact-path plumbing — withOutputDir, figmaLocalPath, and resolveUpstream, which locates the Playwright MCP through the module graph — in test/playguard.test.mjs, plus dead() crash detection, splitArgs() quoting, the shared ttlCache() helper, the decideSnapshot() cache/delta/hint decision logic, the section/around/depth snapshot filtering, the looksLikeLoading() smart-wait heuristic, the sortDeep() Figma cache-key normalization, and the Module 8 Framelink-shape optimizations in test/playguard-core.test.mjs. test/design-diff.test.mjs covers the design-diff side: property extraction from both upstream Figma shapes (REST and Framelink), colour/border/typography normalization, node resolution including the not-found case, the comparison and tolerance rules, and the generated auto-map and eval scripts. test/proxy.test.mjs drives the built dist/index.js over stdio against a fake upstream (test/fixtures/fake-pw.mjs), covering the CallTool path end to end: call logging (errText, thrown, argument shape), the eval cache, snapshot compaction, dead-browser revive, screenshot redirect, prefetch on/off, --codegen handling and the batching hints.

The Chrome DevTools side is covered by test/devtools.test.mjs (launch defaults, tools/list rewrite, network summary, request trim, trace trim, error hints), test/devtools-snapshot.test.mjs (snapshot compaction keeps every interactive uid, UNCHANGED/delta decisions) and test/upstream.test.mjs (lazy spawn and single-flight revive), plus the devtools: cases in test/proxy.test.mjs against a fake upstream (test/fixtures/fake-devtools.mjs): routing, env allowlist, workspace/cwd, the bounded tools/list wait, timeouts, screenshot policy, revive and passthrough.

node bench/smoke-codegen.mjs (after npm run build, needs a browser, not part of npm test) checks against the real Playwright MCP that responses carry no codegen block and evaluate results still parse.

test/a11y.test.mjs runs the generated axe page script against a stub axe and checks the report; test/proxy.test.mjs covers the audit's routing and the official Figma upstream against an in-process HTTP fake (test/fixtures/fake-figma-http.mjs): routing, whole passthrough, the unreachable-app error and the refused remote URL. node bench/smoke-a11y.mjs (after npm run build, needs a browser) audits a local page with known violations through the real Playwright MCP and axe-core.

node bench/smoke-devtools.mjs (after npm run build, needs Chrome and network, not part of npm test) drives PlayGuard against the real chrome-devtools-mcp (DEVTOOLS_MCP_CMD, default npx -y chrome-devtools-mcp@1.12.0): it acts only on uids read from the compacted snapshot, fills and submits a form, checks UNCHANGED/delta, and prints upstream vs sent bytes per tool. Run it with PLAYGUARD_DEVTOOLS_OPTIMIZE=false for the passthrough baseline.

CI (GitHub Actions, .github/workflows/ci.yml) runs npm test on every push and pull request to main.


Comparison

Scenario

Without PlayGuard

With PlayGuard

Browser crashes

Error; session broken

Auto-recovery; agent never sees it

Concurrent calls during crash

Two failures or two browsers

One restart; both calls continue

browser_snapshot (full tree)

100–500 KB

10–80 KB after compact

Repeated browser_snapshot same page

Re-fetches every time

Cache hit or UNCHANGED

Figma file with 50+ components

200–500 KB JSON

60–150 KB after optimization

Several MCPs in agent config (Playwright, Figma, DevTools, Shopify Dev, Context7)

One server entry per MCP, each with its own tool list

One PlayGuard entry, one connection

Chrome DevTools list_network_requests on a busy page

7–34K chars

~0.6–1K chars summary

Repeated documentation lookup (Shopify Dev, Context7)

Network call every time

Disk cache or one-line UNCHANGED note

Failing optional upstream

Its own error, per server

Only its tools go away; the rest keep working

Proxy overhead

None

~1–3 ms per Playwright call


Project Structure

playguard/
├── src/
│   ├── index.ts                    MCP server: tool routing, proxying, caches
│   ├── config.ts                   Env-derived settings, resolved paths, upstream commands
│   ├── snapshot.ts                 Snapshot compaction and the cache/delta decision
│   ├── figma-optimize.ts           Figma response optimizer (Modules 1–8)
│   ├── design-diff.ts              Figma ↔ browser property extraction and diffing
│   ├── a11y.ts                     axe-core page script and report for playguard_a11y_audit
│   ├── devtools.ts                 Chrome DevTools launch defaults, tools/list, network/trace/error rewrites
│   ├── devtools-snapshot.ts        Chrome DevTools snapshot compaction and UNCHANGED/delta decision
│   ├── docs-optimize.ts            Shopify Dev / Context7 tools/list and response rewrites
│   ├── docs-cache.ts               On-disk cache for documentation lookups
│   └── upstream.ts                 Lazy, self-healing upstream client (DevTools, Shopify Dev, Context7)
├── dist/                           Compiled output (generated by npm run build)
├── bench/
│   ├── run.mjs                     Benchmark: latency, token savings, crash recovery
│   ├── analyze.mjs                 Analytics report from NDJSON logs
│   ├── smoke-codegen.mjs           Real-browser check that --codegen none keeps evaluate parseable
│   ├── smoke-a11y.mjs              Real-browser check of playguard_a11y_audit against a page with known violations
│   ├── smoke-devtools.mjs          Real chrome-devtools-mcp check: compacted uids, UNCHANGED/delta, bytes per tool
│   ├── smoke-docs.mjs              Real Shopify Dev / Context7 check: rewrites, cache, UNCHANGED repeats
│   ├── probe-shopify-dev.mjs       Records raw Shopify Dev MCP responses (fixtures, measurements)
│   └── probe-context7.mjs          Records raw Context7 responses (fixtures, measurements)
├── test/
│   ├── playguard.test.mjs          Compact/Figma optimizer, output-dir routing, upstream resolution
│   ├── playguard-core.test.mjs     Crash detection, arg parsing, caching, snapshot decision
│   ├── design-diff.test.mjs        Property extraction, comparison, auto-map
│   ├── devtools.test.mjs           DevTools launch defaults, tools/list, network/trace/error rewrites
│   ├── devtools-snapshot.test.mjs  DevTools snapshot compaction, UNCHANGED/delta
│   ├── upstream.test.mjs           Lazy spawn and revive of an upstream
│   ├── docs-optimize.test.mjs      Shopify Dev / Context7 rewrites against recorded responses, disk cache
│   ├── a11y.test.mjs               axe page script and report against a stub axe
│   ├── proxy.test.mjs              CallTool path end to end against fake upstreams
│   └── fixtures/                   fake-pw.mjs, fake-devtools.mjs, fake-docs.mjs, fake-figma-http.mjs, recorded upstream responses
├── .github/workflows/ci.yml        Runs npm test on push/PR
├── logs/                           Per-day NDJSON call logs (auto-created at runtime)
├── .playguard/                     Auto-created artifact root
│   ├── .qa/                        Screenshots, traces, PDFs, downloads from the site under test
│   └── .figma/                     Figma image exports
├── package.json
└── tsconfig.json

License

MIT

Available Tools

24 tools
browser_clickB
Destructive

Perform click on a web page

NameRequiredDescriptionDefault
buttonNoButton to click, defaults to left
targetYesExact target element reference from the page snapshot, or a unique element selector
elementNoHuman-readable element description used to obtain permission to interact with the element
modifiersNoModifier keys to press
doubleClickNoWhether to perform a double click instead of a single click

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false and destructiveHint=true, indicating the tool mutates state and can be destructive. The description adds no further behavioral disclosure, such as possible navigation or permission requirements. It does not contradict annotations, but provides no value beyond them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence with no filler. However, it is terse and omits useful context that could be added without bloat, such as the requirement for a snapshot target. It is appropriately sized but not front-loaded with any distinguishing information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (5 parameters, no output schema), the description alone is minimally sufficient because the schema fully describes parameters. However, it lacks context about return values, when to use click vs other actions, and potential side effects. The schema covers parameter semantics, so a score of 3 is appropriate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with every parameter documented (e.g., target is 'Exact target element reference from the page snapshot, or a unique element selector'). The description itself adds no additional parameter meaning, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Perform click') and the resource ('web page'), which is specific enough. However, it does not differentiate from sibling interaction tools like browser_hover or browser_drag; the agent must infer that 'click' is a distinct action without additional context. It is unambiguous about the core operation but lacks detail on the target element requirement.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It does not mention prerequisites such as needing a page snapshot to obtain a valid target, nor does it contrast with browser_hover or browser_drag. An agent receives no context for tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_closeB
Destructive

Close the page

NameRequiredDescriptionDefault

No parameters

TDQS

B3.3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotation already indicates destructiveHint=true, so the agent knows the action is destructive. The description simply repeats that by saying 'close' without adding any extra context about side effects (e.g., whether it closes just the current tab, loses unsaved state, or affects other tabs). It does not contradict the annotation, but it adds no new behavioral information.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise, using only three words. It communicates the essential action and target without any fluff, making it easy to parse and understand quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simplicity of the tool (no parameters, no output schema), the description is functional but lacks completeness. It leaves open the scope of 'close'—does it close the current page/tab, or the entire browser? In a context with sibling tools like browser_tabs, this ambiguity could be problematic. More detail would improve it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, so the schema coverage is trivially complete at 100%. The description does not need to explain any parameter semantics, and the baseline score of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Close the page' clearly states the action (close) and the resource (page), making it unambiguous. It is distinct from sibling tools like browser_navigate or browser_snapshot, so an agent can easily identify it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It does not mention potential alternatives (e.g., using browser_tabs to manage multiple pages) or any conditions that would make closing appropriate. The context is minimal and leaves the decision entirely to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_console_messagesB
Read-only

Returns all console messages

NameRequiredDescriptionDefault
allNoReturn all console messages since the beginning of the session, not just since the last navigation. Defaults to false.
levelYesLevel of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".info
filenameNoFilename to save the console messages to. If not provided, messages are returned as text.

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint true and destructiveHint false, so the description adds no conflicting side-effect information. It does not elaborate on behavior such as saving to a file, but that is covered by parameter descriptions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single clear sentence with no redundancy, front-loading the verb and object.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only tool with full schema coverage, the description is adequate, though it omits explicit use-case guidance and output format details beyond what the schema provides.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with detailed parameter descriptions, so the tool description itself does not add meaning beyond the schema, but no parameter meaning is missing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description uses a specific verb 'Returns' and identifies the resource 'console messages' with scope 'all', making the tool's purpose clear and distinct from sibling tools such as network request retrieval.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives like browser_network_requests or browser_snapshot, nor does it mention filtering or filename-saving scenarios.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_dragA
Destructive

Perform drag and drop between two elements

NameRequiredDescriptionDefault
endTargetYesExact target element reference from the page snapshot, or a unique element selector
endElementNoHuman-readable target element description used to obtain the permission to interact with the element
startTargetYesExact target element reference from the page snapshot, or a unique element selector
startElementNoHuman-readable source element description used to obtain the permission to interact with the element

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate destructiveHint=true and readOnlyHint=false, so the destructive nature is covered. The description adds no extra behavioral context such as side effects on the DOM or event firing, but it does not contradict the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence that front-loads the action and provides the core information without any irrelevant detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple action with well-described parameters and no output schema, the description is mostly complete. It does not mention any return values or wait behavior, but the lack of output schema makes this acceptable. It could have added a note about the order of start and end parameters, but the parameter names and descriptions handle that.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% with all four parameters described. The tool description adds no additional parameter semantics beyond what the schema already provides, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (perform drag and drop) and the resource (between two elements), distinguishing it from sibling tools like browser_click, browser_hover, and browser_drop. It is specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not provide any guidance on when to use this tool versus alternatives. It does not mention conditions such as needing to move an element or triggering drag-and-drop events, nor does it reference sibling tools like browser_drop or browser_click.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_dropA
Destructive

Drop files or MIME-typed data onto an element, as if dragged from outside the page. At least one of "paths" or "data" must be provided.

NameRequiredDescriptionDefault
dataNoData to drop, as a map of MIME type to string value (e.g. {"text/plain": "hello", "text/uri-list": "https://example.com"}).
pathsNoAbsolute paths to files to drop onto the element.
targetYesExact target element reference from the page snapshot, or a unique element selector
elementNoHuman-readable element description used to obtain permission to interact with the element

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already convey destructive and non-read-only behavior, so the description does not need to repeat that. The description adds useful semantic context about simulating an external drag, but does not disclose any side effects beyond the basic drop action.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, focused sentence that concisely explains both the action and the key parameter requirement. No filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description, combined with the detailed schema and annotations, provides enough information to invoke the tool correctly. There is no output schema, so return-value details are not required, and the parameter permission explanation is handled by the 'element' field description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

All parameters have clear schema descriptions. The tool description adds an important cross-parameter constraint that at least one of 'paths' or 'data' must be provided, which is not otherwise expressed in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action: dropping files or MIME-typed data onto an element. It uses a specific verb and resource, and distinguishes itself from in-page drag operations by specifying 'as if dragged from outside the page'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a useful usage constraint ('At least one of paths or data must be provided') and hints at external drag context, but it does not explicitly name sibling tools or state when to prefer this over browser_file_upload or browser_drag.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_evaluateA
Destructive

Evaluate JavaScript expression on page or element [PlayGuard] Each call waits ≥0.5–1.5 s for the page to settle. Combine related checks into one function that returns an object instead of calling this repeatedly.

NameRequiredDescriptionDefault
targetNoExact target element reference from the page snapshot, or a unique element selector
elementNoHuman-readable element description used to obtain permission to interact with the element
filenameNoFilename to save the result to. If not provided, result is returned as text.
functionYes() => { /* code */ } or (element) => { /* code */ } when element is provided

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false, destructiveHint=true and openWorldHint=true, so the mutation risk is covered structurally. The description adds genuinely non-obvious timing behavior (≥0.5–1.5 s settle per call), which is valuable. It stops short of describing execution context (main world vs isolated), serialization of return values, or error behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero padding, with the core action front-loaded before the PlayGuard constraint. Every sentence carries information an agent would otherwise lack.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a four-parameter tool with full schema coverage, annotations, and no output schema, the description covers what it does, the latency cost, and the batching strategy. The remaining gap is sibling disambiguation from browser_run_code_unsafe, which keeps it out of the top band.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all four parameters (including the target vs element distinction and the filename save option) are already documented in the schema. The description adds only a light implication that code may receive 'element' and return an object; baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Evaluate JavaScript expression on page or element.' An agent can tell it is a code-execution tool rather than a navigation or snapshot tool. However, it never distinguishes itself from the near-identical sibling browser_run_code_unsafe, which is the main ambiguity an agent faces.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The PlayGuard note gives real usage guidance — combine related checks into one function instead of calling repeatedly — but only for batching within this tool. There is no statement of when to prefer this over browser_run_code_unsafe, browser_snapshot, or browser_console_messages, so the sibling routing is left implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_file_uploadB
Destructive

Upload one or multiple files

NameRequiredDescriptionDefault
pathsNoThe absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled.

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already indicate destructive behavior and non-read-only semantics, so the description does not need to restate those. However, the description does not mention side effects beyond uploading, such as navigating away, opening or cancelling a file chooser, or potential overwrite behavior, though the parameter description partially covers the cancellation case.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise and free of filler. Every word contributes meaning, and the parameter description adds necessary details without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter tool with no output schema, the description and parameter comment cover the essential input and basic behavior. However, it does not describe what happens after the upload, what success or failure looks like, or how this fits into the broader browser automation workflow, leaving some practical context missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema only defines an array of strings, but the parameter description adds important meaning: paths must be absolute, multiple files are allowed, and omitting the parameter cancels the file chooser. This goes beyond the raw schema and helps an agent use the parameter correctly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Upload') and the resource ('files'), and specifies 'one or multiple'. It does not explicitly state the destination or context, but the tool name and sibling set make the browser file-upload intent sufficiently clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no guidance on when to use this tool versus alternatives such as browser_drop or other browser interaction tools. It also does not mention prerequisites like whether the file chooser must already be open or how paths should be formatted beyond 'absolute'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_fill_formB
Destructive

Fill multiple form fields

NameRequiredDescriptionDefault
fieldsYesFields to fill in

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint=false and destructiveHint=true, so the write and potentially destructive nature is covered. The description adds the 'multiple form fields' detail but does not disclose additional behavior such as overwriting values or submitting the form.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single clear sentence with no redundant words or boilerplate. It is appropriately short and easy to parse, though it sacrifices deeper context for brevity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The rich schema fully documents the nested field objects, including target references, type-specific value behavior, and permission-related element descriptions. Combined with the read-only and destructive annotations, the description is sufficient for correct invocation; only alternative-selection guidance is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%: the fields array and its nested target, name, type, value, and element properties all have descriptive text. The description itself does not add parameter-level meaning beyond what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Fill multiple form fields' clearly identifies the action and resource, and the word 'multiple' helps distinguish it from single-field tools like browser_type. It does not explicitly contrast with sibling tools, but the core purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided for when to use this tool instead of alternatives such as browser_type, browser_select_option, or browser_click. The term 'multiple' implies batch usage, but there is no explicit when-to-use or exclusion context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_handle_dialogC
Destructive

Handle a dialog

NameRequiredDescriptionDefault
acceptYesWhether to accept the dialog.
promptTextNoThe text of the prompt in case of a prompt dialog.

TDQS

C2.2/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations indicate readOnlyHint=false and destructiveHint=true, covering the main safety aspect. However, the description adds no detail about side effects, such as submitting a form or canceling a prompt, beyond what the parameter names imply. It does not clearly disclose the consequences of accepting or rejecting.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely short and lacks necessary detail, making it under-specified. It is terse but not informative enough to guide an agent effectively, similar to a placeholder like 'Process'.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema or additional context is provided. The description does not explain what happens after handling the dialog, how the parameters interact, or any post-conditions. This is insufficient for an agent to fully understand the tool's behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with clear descriptions for both 'accept' (boolean) and 'promptText' (string for prompt dialogs). The description does not add extra meaning, but the schema already provides adequate parameter semantics, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Handle a dialog' identifies the action and resource but is vague. It does not specify the type of dialog (alert, confirm, prompt) or the precise effect, though the 'accept' parameter implies accept/dismiss. It is distinct from sibling tools, but the purpose is under-specified.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided about when to use this tool, such as after a dialog appears or in contrast to other browser actions. The description gives no context for the appropriate situation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_hoverA
Destructive

Hover over element on page

NameRequiredDescriptionDefault
targetYesExact target element reference from the page snapshot, or a unique element selector
elementNoHuman-readable element description used to obtain permission to interact with the element

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint=false and destructiveHint=true, so the description doesn't need to re-state them. The description adds no extra behavioral detail about side effects (e.g., triggering JavaScript events, potential menu changes). With annotations present, the bar is lower, but there's still room for more transparency about outcomes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise—six words, no redundancy, and perfectly focused on the action. It follows a clear verb-object structure with no extra fluff, making it easy to parse and understand.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple interaction tool, the description, alongside the parameter schema and annotations, is adequate. It lacks details on return values (but no output schema exists) and does not mention any prerequisites or setup. Given the simplicity, it covers essential context without being overlong.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Both parameters (target and element) have descriptions in the schema, achieving 100% coverage. The tool description does not add further meaning beyond the schema; it simply restates the action. Since schema coverage is complete, a baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (hover) and the target (element on page). It distinguishes itself from sibling tools like click, drag, and type by specifying the hover action. However, it lacks detail about what the hover does (e.g., showing tooltips or hover menus), but the core purpose is clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no explicit guidance on when to use hover versus other interactions. While it's intuitive that hover is for mouse-over actions, the description doesn't mention edge cases or alternative tools (e.g., use click for activating, hover for previews). It provides minimal implicit usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_navigateC
Destructive

Navigate to a URL

NameRequiredDescriptionDefault
urlYesThe URL to navigate to

TDQS

C2.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description does not elaborate on side effects such as discarding the current page, waiting for load, or handling errors. While annotations indicate destructiveHint=true, the description adds no extra context about the navigation behavior beyond the literal action.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, direct sentence with no unnecessary words. It is appropriately sized for the simple action, though it could benefit from some additional context without becoming verbose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description lacks important context such as whether navigation waits for page load, how errors are reported, or the impact on the current browsing session. Given the tool modifies state (as indicated by destructiveHint), more context about the navigation behavior would be expected.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema fully describes the only parameter 'url' with the same text as the description. Since schema coverage is 100%, the description adds no additional meaning or constraints (e.g., format, absolute vs. relative) beyond what is already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action 'Navigate to a URL' with a specific resource. It distinguishes from sibling tools like browser_navigate_back by implying forward navigation to a new URL, though it could be more explicit about the effect on the current page.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool over alternatives. It does not mention scenarios where browser_navigate is preferred over browser_navigate_back or other navigation methods, nor any prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_navigate_backA
Destructive

Go back to the previous page in the history

NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description and annotations are consistent; destructiveHint is true, matching the navigation action which changes the current page. The description adds minimal behavioral context beyond the annotations, but does not contradict them. It does not mention potential side effects like what happens if there is no history or whether it affects new tabs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, concise sentence with no redundant wording. It directly states the action and target without unnecessary detail, which is ideal for a tool with no parameters.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple, parameterless tool, the description is largely complete. It communicates the essential operation. It could be improved by noting the behavior when there is no previous history entry, but the given context is sufficient for most use cases.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the description does not need to explain parameter semantics. The empty input schema is fully covered by the lack of parameters, making this baseline appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action: 'Go back to the previous page in the history.' It identifies the resource (browser history) and the direction (back), making the core purpose unambiguous. It does not explicitly differentiate from sibling tools like browser_navigate or browser_tabs, but the meaning is clear enough in context.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use the tool: when the user wants to go back to the previous page in history. However, it provides no explicit guidance on when not to use it, such as when a direct URL navigation via browser_navigate would be more appropriate, or what to do if no prior history exists.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_network_requestA
Read-only

Returns full details (headers and body) of a single network request, or a single part if part is set. Use the number from browser_network_requests.

NameRequiredDescriptionDefault
partNoReturn only this part of the request. Omit to return full details.
indexYes1-based index of the request, as printed by browser_network_requests.
filenameNoFilename to save the result to. If not provided, output is returned as text.

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, covering safety. The description adds that it returns headers and body and can filter to a part, but does not elaborate on output structure or edge cases. Given annotation coverage, a 3 is appropriate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no redundant wording. The main purpose is front-loaded, the part option is explained, and the source of the index is given. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only retrieval tool with full schema coverage and annotations, the description is sufficient. It explains the return type (full details or part) and the index source. It lacks explicit output format details, but the enum values for 'part' are self-explanatory.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with descriptions for all parameters, so the schema already documents them. The description adds critical value by explaining that 'index' comes from browser_network_requests, which is not in the schema, enhancing clarity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns full details of a single network request, with an optional 'part' parameter to narrow down. It distinguishes from the sibling tool browser_network_requests by referencing its index, making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The instruction 'Use the number from browser_network_requests' explicitly guides when to use this tool—after obtaining a listing. It does not explicitly mention alternatives or exclusions, but the context is clear enough for an agent to decide.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_network_requestsA
Read-only

Returns a numbered list of network requests since loading the page. Use browser_network_request with the number to get full details.

NameRequiredDescriptionDefault
filterNoOnly return requests whose URL matches this regexp (e.g. "/api/.*user").
staticYesWhether to include successful static resources like images, fonts, scripts, etc. Defaults to false.
filenameNoFilename to save the network requests to. If not provided, requests are returned as text.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, covering the safety profile. The description adds valuable context about the temporal scope ('since loading the page') and the numbered list format, which goes beyond annotations without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences with no redundancy. The primary action is front-loaded, and the companion tool reference is placed efficiently at the end. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description tells the agent what the tool returns (a numbered list) and how to proceed for details. Since there is no output schema, this is essential. It does not describe potential error cases or edge scenarios, but for a read-only list tool with strong schema coverage, it is sufficiently complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

All three parameters have schema descriptions covering 100% of the input schema. The tool description does not add any additional meaning to the parameters themselves, so the baseline of 3 applies; it neither enhances nor detracts from what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns a numbered list of network requests since page load, and explicitly differentiates from the sibling tool browser_network_request by instructing to use that tool for details. This is a specific verb+resource with a clear scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives an explicit usage pattern: first call this tool to get a list, then use browser_network_request with the number for full details. This is clear guidance on when to use it versus its companion, though it does not mention any other alternatives or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_press_keyB
Destructive

Press a key on the keyboard

NameRequiredDescriptionDefault
keyYesName of the key to press or a character to generate, such as `ArrowLeft` or `a`

TDQS

B3.2/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description does not mention any side effects, such as triggering keyboard events or potential destructive behavior. The annotations indicate destructiveHint: true, but the description adds no additional behavioral context beyond that.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise — a single sentence that directly conveys the action. No unnecessary information or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple action with a single well-defined parameter, the description is adequate. It lacks mention of when to use this tool (e.g., for shortcuts or navigation keys) but is otherwise complete for basic invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The parameter 'key' is fully described in the schema with examples, and schema coverage is 100%. The description adds minimal extra meaning ('on the keyboard') but doesn't clarify key formats or special keys, which is already covered by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (press) and the resource (key on the keyboard). It is distinct from broader actions like navigate or click, though it does not explicitly differentiate from browser_type (typing) or browser_click (clicking).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus its siblings (e.g., browser_type for text entry or browser_click for mouse actions). The description lacks any contextual cues for choosing this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_resizeB
Destructive

Resize the browser window

NameRequiredDescriptionDefault
widthYesWidth of the browser window
heightYesHeight of the browser window

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description does not disclose any behavioral traits beyond the bare action. Annotations already indicate it is not read-only and is destructive, but the description adds no extra context about side effects like layout reflows or potential viewport changes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, short declarative sentence with no unnecessary words. It is perfectly concise and well-structured for the simple action it describes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool, the description is adequate, but it lacks any extra context such as units (e.g., pixels), constraints, or when resizing might be appropriate. Given the simplicity and schema coverage, it is minimally complete but not rich.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already provides descriptions for both parameters (width and height), achieving 100% coverage. The description adds no additional meaning about the parameters, so it stays at the baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (resize) and the resource (browser window), making it distinct from sibling tools like navigate or click. However, it lacks any nuance about whether it resizes the viewport or the entire window, but the core purpose is clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided about when to use this tool versus alternatives. It does not mention scenarios like adjusting viewport for responsive testing or any conditions that would make it preferable to other navigation tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_run_code_unsafeA
Destructive

Run a Playwright code snippet. Unsafe: executes arbitrary JavaScript in the Playwright server process and is RCE-equivalent. [PlayGuard] Each call waits for the page to settle. Prefer one script that loops over viewports/states over several calls.

NameRequiredDescriptionDefault
codeNoA JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: `async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }`
filenameNoLoad code from the specified file. If both code and filename are provided, code will be ignored.

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false, destructiveHint=true, and openWorldHint=true, so the safety bar is partly pre-cleared; the description still adds real signal by naming the risk as "RCE-equivalent" and by disclosing the per-call settle-wait, which has latency and batching implications. It stops short of saying whether page state persists between calls or what the snippet returns.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences plus a bracketed operational note, with the highest-stakes fact (RCE-equivalent) placed early rather than buried. Every sentence carries distinct information: capability, risk, wait behavior, batching advice.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 2-parameter tool with no output schema and annotations covering the safety profile, the description supplies the risk model and the operational constraint an agent needs before calling. The remaining gap is return-value behavior and cross-call page state, which the absent output schema pushes onto the description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with both `code` and `filename` fully documented in the schema, including the precedence rule that `code` is ignored when `filename` is supplied. The description adds nothing about parameters beyond the word "snippet", so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Run a Playwright code snippet") and immediately qualifies it with the execution context ("executes arbitrary JavaScript in the Playwright server process"). That context implicitly separates it from page-scoped siblings like browser_evaluate, but no sibling is named, so the differentiation is left to inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The PlayGuard note gives one concrete usage rule — batch loops into a single script rather than making several calls — which is genuine guidance. However, it never says when to reach for this versus browser_evaluate, browser_snapshot, or the granular interaction tools, which is the central selection decision for an agent facing ~23 siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_select_optionB
Destructive

Select an option in a dropdown

NameRequiredDescriptionDefault
targetYesExact target element reference from the page snapshot, or a unique element selector
valuesYesArray of values to select in the dropdown. This can be a single value or multiple values.
elementNoHuman-readable element description used to obtain permission to interact with the element

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate destructive=true, so the description does not contradict that. But the description adds no further behavioral detail (e.g., side effects on the page). With annotations present, the bar is lower, and the description is neutral.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence with no redundant words. It is well-structured and to the point.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple and the schema covers all parameters. The description is short but sufficient for a basic understanding. It does not specify whether values are labels or values, but that is a minor omission given the context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with clear parameter descriptions for target and values. The tool description itself does not explain parameters beyond what the schema already provides, so it adds no extra semantic value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (select) and the target (option in a dropdown), distinguishing it from more general actions like click or type. However, it does not explicitly contrast with sibling tools, so it misses a bit of distinction.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It does not mention when selecting is appropriate or when other actions (e.g., click, type) might be preferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_snapshotB
Read-only

Capture accessibility snapshot of the current page, this is better than screenshot

NameRequiredDescriptionDefault
boxesNoInclude each element's bounding box as [box=x,y,width,height] in the snapshot. Coordinates are viewport-relative, in CSS pixels (Element.getBoundingClientRect)
depthNoMax depth of the returned subtree. Use with section/around to get a high-level overview first.
aroundNoReturn only the landmark subtree containing this ref number. Use when you know a specific element's ref.
targetNoExact target element reference from the page snapshot, or a unique element selector
sectionNoReturn only the subtree under this landmark (e.g. 'form', 'main', 'navigation "Footer"'). Reduces output by 90%+.
filenameNoSave snapshot to markdown file instead of returning it in the response.

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false, openWorldHint=true, so the safety profile is covered. The description adds nothing beyond that — no mention that output can be very large, that it returns a text/ref tree rather than an image, or that filename diverts output to disk. Not contradictory, just thin.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler. The trailing comparison is informal and unsubstantiated, which keeps it from a 5, but nothing is bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a six-parameter, zero-required, read-only inspection tool with no output schema, the description should explain what gets returned and the cost of full snapshots. The schema covers the scoping options, so the agent can call it, but the return-value shape is undocumented.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with all six optional parameters (boxes, depth, around, target, section, filename) documented in detail, so the schema carries this dimension. The description adds no parameter meaning of its own; baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: "Capture accessibility snapshot of the current page." It also implicitly differentiates from browser_take_screenshot by claiming superiority, though it never names that sibling directly or says what an accessibility snapshot actually contains.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"This is better than screenshot" is the only usage signal, and it's an unsupported preference rather than a condition. There is no guidance on when to prefer browser_take_screenshot, browser_evaluate, or the a11y audit sibling, nor on the depth/section/around workflow the schema itself suggests.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_tabsA
Destructive

List, create, close, or select a browser tab.

NameRequiredDescriptionDefault
urlNoURL to navigate to in the new tab, used for new.
indexNoTab index, used for close/select. If omitted for close, current tab is closed.
actionYesOperation to perform

TDQS

A3.5/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and readOnlyHint=false, so the safety profile is known. However, the description adds no behavioral context beyond what the schema provides (e.g., index omission for close closes current tab is in schema). No mention of side effects like tab switching or browser-level changes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence that efficiently lists all supported actions. It is front-loaded with verbs and avoids unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has multiple actions and a required action parameter. The description lists the actions but does not explain action-specific requirements (e.g., URL required for new, index for close/select) or edge cases like closing the last tab. Given the schema covers parameter details, the description is adequate but not rich. With no output schema, the agent must infer return values from the action.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% with each parameter having a description (url, index, action with enum). The tool description adds no parameter-level detail, so it does not go beyond schema. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs (list, create, close, select) and a specific resource (browser tab). It distinguishes from siblings like browser_close (closes the entire browser) and browser_navigate (navigates current tab) by focusing on tab operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage through the list of actions but provides no explicit when-to-use guidance or exclusions. Given siblings like browser_close (closes the entire browser) and browser_navigate (navigates current tab), an agent might benefit from explicit differentiation, but the actions themselves are clear enough that usage is implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_take_screenshotA
Read-only

Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.

NameRequiredDescriptionDefault
typeYesImage format for the screenshot. Default is png.png
targetNoExact target element reference from the page snapshot, or a unique element selector
elementNoHuman-readable element description used to obtain permission to interact with the element
filenameNoFile name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory.
fullPageNoWhen true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds behavioral context beyond annotations: it states that you cannot perform actions based on the screenshot, which is not covered by readOnlyHint or destructiveHint. This is valuable for an AI agent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Very concise: two sentences with no wasted words. The critical guidance is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity and the rich schema/annotations, the description is mostly complete. However, it could mention the return format (e.g., base64 image or file) since there is no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description does not add additional meaning to the parameters beyond what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (take a screenshot) and the resource (current page), and distinguishes it from the sibling tool browser_snapshot by specifying that you cannot perform actions on the screenshot.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells when to use the tool (taking a screenshot) and when not to (for actions), and provides an alternative (browser_snapshot). This is high-quality guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_typeB
Destructive

Type text into editable element

NameRequiredDescriptionDefault
textYesText to type into the element
slowlyNoWhether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.
submitNoWhether to submit entered text (press Enter after)
targetYesExact target element reference from the page snapshot, or a unique element selector
elementNoHuman-readable element description used to obtain permission to interact with the element

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotation destructiveHint=true and readOnlyHint=false already signal that this tool can modify state, so the description does not contradict that. However, the main description adds no additional behavioral context beyond the annotations, such as whether typing overwrites existing content or triggers page events. The parameter descriptions do mention key-handler triggering and Enter submission, but the main description itself stays shallow.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, focused sentence with no filler or repetition. It front-loads the verb and object, making the tool's primary purpose immediately obvious.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with several closely related siblings (fill_form, press_key, click), the description is too thin to fully orient an agent. It lacks any mention of when typing is appropriate, how the target is resolved, or what side effects to expect, so the agent must rely on parameter descriptions and external context to make a confident choice.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3 because the input schema already documents all five parameters. The main description, 'Type text into editable element,' adds no meaning beyond the schema's parameter descriptions, which already define text, slowly, submit, target, and element clearly enough.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear action ('Type text') and a clear target ('editable element'), which distinguishes it from many sibling tools at a high level. It is slightly vague about whether 'editable element' includes textareas, contenteditable regions, or only input fields, but the core purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no guidance on when to prefer this tool over sibling tools such as browser_fill_form or browser_press_key. It does not mention exclusions, prerequisites, or scenarios where typing would be inappropriate, leaving the agent to infer usage from the tool name alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_wait_forA
Read-only

Wait for text to appear or disappear or a specified time to pass [PlayGuard] Prefer waiting for text over a fixed time.

NameRequiredDescriptionDefault
textNoThe text to wait for
timeNoThe time to wait in seconds
textGoneNoThe text to wait for to disappear

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false and openWorldHint=true, so the safety profile is covered structurally. The description adds that this is a blocking wait and nudges toward text conditions, but says nothing about timeout behavior, what happens if no parameter is supplied (all three are optional), or whether a timeout raises an error versus returning a negative result.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the capability and followed by the actionable preference. Zero filler. Slightly terse given the tool's subtleties, but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a three-optional-parameter blocking tool with no output schema, an agent still needs to know the timeout ceiling, the behavior when zero parameters are passed, and how a failed wait is reported. None of that is present, though the core semantics and the recommended usage pattern are covered.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so each of text/textGone/time is already documented precisely. The description only restates the same three modes at a higher level and adds no syntax, units, or mutual-exclusivity detail beyond the schema. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('wait for') with the exact three trigger conditions (text appears, text disappears, time passes), which makes it unmistakable among browser_* siblings that act rather than block. It does not explicitly contrast itself with any sibling, but no other tool in the list is a wait primitive, so disambiguation is effectively achieved.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The PlayGuard note gives explicit preference guidance: use text-based waiting over fixed-duration waiting. That is a real decision rule an agent can apply. What's missing is guidance on when waiting is needed at all versus when a snapshot/click already settles the page, so it stops short of full when/when-not coverage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

playguard_a11y_auditA
Read-only

Run an axe-core accessibility audit on the page currently open in the browser_* browser. Returns WCAG violations grouped by rule, most severe first, with CSS selectors, a short html excerpt and the fix for each element. Navigate first; re-run after a click/state change to audit dialogs or menus.

NameRequiredDescriptionDefault
tagsNoaxe rule tags to run. Replaces the default (wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa) — to add "best-practice" or "wcag2aaa", pass the defaults plus it.
selectorNoAudit only this part of the page (CSS selector, e.g. 'main', '#cart-drawer'). Default: whole page.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only say readOnlyHint=true; the description adds the return shape (violations grouped by rule, severity-ordered, with selectors, html excerpt, and fix) and the state dependency of the audit. It doesn't mention runtime cost or that results reflect only the current DOM state beyond the re-run hint, so a 4 rather than 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the action and return contract, then the workflow caveat. Every sentence carries information the agent needs; nothing is repeated from the schema or annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and zero required parameters, the description carries the return-value burden and does so precisely, plus the navigate/re-run workflow. An agent has everything needed to call it correctly and interpret results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both `tags` and `selector` are already well documented, including the default tag set and the replace-vs-add pitfall. The description adds nothing about either parameter, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Run an axe-core accessibility audit on the page currently open in the browser_* browser.' That is clearly distinct from siblings like browser_snapshot, browser_evaluate, or browser_console_messages, which gather different artifacts from the same page.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit sequencing guidance — 'Navigate first; re-run after a click/state change to audit dialogs or menus' — which tells the agent the precondition and the re-invocation pattern. It does not name an alternative tool or state when not to audit, so it falls short of a 5.

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.

  1. 24 tool updatesv0.10.1
    • First observedbrowser_click
    • First observedbrowser_close
    • First observedbrowser_console_messages
    • First observedbrowser_drag
    • First observedbrowser_drop
    • First observedbrowser_evaluate
    • First observedbrowser_file_upload
    • First observedbrowser_fill_form
    • First observedbrowser_handle_dialog
    • First observedbrowser_hover
    • First observedbrowser_navigate
    • First observedbrowser_navigate_back
    • First observedbrowser_network_request
    • First observedbrowser_network_requests
    • First observedbrowser_press_key
    • First observedbrowser_resize
    • First observedbrowser_run_code_unsafe
    • First observedbrowser_select_option
    • First observedbrowser_snapshot
    • First observedbrowser_tabs
    • First observedbrowser_take_screenshot
    • First observedbrowser_type
    • First observedbrowser_wait_for
    • First observedplayguard_a11y_audit

TDQS

B3.2/5.0

Scored across 24 tools

Disambiguation4/5

Most tools target clearly distinct browser actions, and descriptions help separate close pairs like screenshot vs snapshot and network request list vs detail. A few overlaps remain, especially browser_evaluate vs browser_run_code_unsafe and drag vs drop, but these are mostly resolvable from context.

Naming Consistency4/5

Nearly all tools use a consistent browser_prefix with snake_case naming, and the one playguard_ prefix is understandable as a product-specific audit tool. The set mixes verb-first names with noun-phrase names like browser_console_messages and browser_tabs, so it is not perfectly uniform.

Tool Count3/5

The 24-tool surface is broad and mostly justified by browser automation needs, but it sits at the heavy end of the scale and includes many granular primitives. Some consolidation, such as grouping network inspection helpers or runtime evaluation tools, would make the set easier to manage.

Completeness4/5

The server covers the main browser automation lifecycle: navigation, interaction, forms, tabs, dialogs, network inspection, console access, screenshots, and accessibility auditing. Minor gaps remain around forward navigation, storage/cookie management, request interception, and richer accessibility-audit configuration or reporting.

Maintenance

ActivityMaintained
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Enables direct browser control via Chrome DevTools Protocol, supporting navigation, interaction, content extraction, and screenshots through a single MCP tool.
    1
    368
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server that lets agents drive your real Chrome browser with existing logins and sessions via an outbound-only WebSocket extension. It exposes Playwright-compatible browser tools for navigation, clicking, typing, and snapshots.
    1
    Apache 2.0