chrome-mcp
This server lets an MCP agent drive the user's real, already-logged-in Chrome browser through a required companion extension, with a default-deny security policy.
Tab management: list, select, open (foreground/background), close, and multi-tab operations via
batch(parallel or serial).Navigation: navigate, back, forward, reload, with optional wait conditions and auth-wall detection.
Interaction: click, type, press, hover, scroll, select dropdown options, and fill forms — targeting by CSS selector, snapshot ref, or ARIA role + accessible name;
trusted: truefor real OS-level input.Page reads: get visible text, HTML, accessibility snapshots (with diff mode), extract links, read as markdown, and evaluate JavaScript (opt-in).
Waiting & inspection: wait for selectors/text, list iframes, read console logs, network requests, and intercept/answer native dialogs (opt-in observers).
Session & state: read cookies, read/write localStorage/sessionStorage, check for sign-in walls (
auth_check), and detect expired sessions.Screenshots & PDFs: capture page/element/full-page screenshots (JPEG/PNG) and render pages to PDF via Chrome's print pipeline.
Downloads & uploads: download files (sanitized/path-traversal protected) and set local files on file inputs (opt-in, exfiltration-gated).
Multi-profile & task organization: manage multiple Chrome profiles, name them, switch active profile, create tasks, and track artifact folders.
Status & observability: report backend/session status, paired browsers, active profile/task, and artifact storage paths.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@chrome-mcpnavigate to example.com and get the page title"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
chrome-mcp
Let Claude use the Chrome you are already logged into. Not a fresh automated browser that greets every site as a stranger — your Chrome, with your sessions, your cookies, your 2FA already done. If you can see a page in your browser, your agent can read it, without logging in again and without pasting credentials anywhere.
Most browser MCP servers launch their own Chromium and hand your agent a
signed-out window. chrome-mcp does the opposite: an MV3 extension dials into a
localhost WebSocket server and drives the browser you already have open, through
chrome.scripting/chrome.tabs. Works with Claude Code, Claude Desktop, and any
other MCP host.
Distributed as an npx CLI (the MCP server) plus a load-unpacked extension.
This build is extension-only. It never launches or attaches a Chromium of its own, so the extension is required, not optional — without it, no tool can run. The CDP flags (
--cdp-fallback,--no-cdp-fallback,--cdp-endpoint,--prefer) are still accepted for back-compat but are ignored.
Full design:
docs/BLUEPRINT.md— architecture, wire protocol, the complete tool surface, the extension manifest, the security model, and the phased build plan.
Quickstart
Up and running in one paste
Hand this to your AI agent (Claude Code, Cursor, Windsurf, anything MCP) and it installs the server, wires it into the client, and walks you through the two steps that must happen inside Chrome:
Set up chrome-mcp on this machine by fetching and following
https://raw.githubusercontent.com/Mehmoodqureshi/chrome-mcp/main/SETUP.md
exactly, step by step. Work autonomously and verify each step.Prefer to read before you run an agent on your machine? SETUP.md
is the exact file the agent follows. The manual steps are below.
1. Register the MCP server with your host.
claude mcp add chrome-mcp -s user -- \
npx -y @mehmoodqureshi/chrome-mcp \
--allow-domain example.com --enable-mutations --persist-tokenEverything before -- belongs to Claude Code; everything after it is this
server's command and flags. Keep the -- or --allow-domain gets read as a
Claude Code option.
-s user registers it for every project on your machine. Use -s local (the
default) for just the current project, or -s project to write a .mcp.json
your team can commit.
Check it came up with claude mcp list. After upgrading the server, reconnect it
with /mcp inside a session — no restart needed.
{
"mcpServers": {
"chrome-mcp": {
"command": "npx",
"args": ["-y", "@mehmoodqureshi/chrome-mcp",
"--allow-domain", "example.com", "--enable-mutations",
"--persist-token"]
}
}
}By default everything is deny-all (no domains, no eval, no mutations). Grant
exactly what you need with --allow-domain <glob> (repeatable), --enable-mutations,
--enable-downloads, --enable-uploads, --unsafe-enable-eval, or --unsafe-all-domains.
--enable-uploadspermitsupload_file(setting local file(s) on a page's file<input>). It is off by default because sending local files to a page is an exfiltration risk; it is also gated by the destination-domain allowlist. Pair it with--uploads-dir <path>to restrict uploads to files inside that directory (..traversal is blocked) — strongly recommended for unattended use.
Pair once, never again. Both examples above include --persist-token, which
is what makes the pairing survive a restart — drop it if you'd rather have the
stricter default described next.
Without --persist-token a fresh token is minted every boot (the secure
default), which means re-pairing the extension on each restart. With it, the
token is stored 0600 at ~/.chrome-mcp/token and reused; the extension's
keepalive auto-reconnects with no manual step. CHROME_MCP_TOKEN pins the token
explicitly (and is never written to disk).
2. Load the extension — required; the server can drive nothing without it.
Two ways to get it:
Install from the Chrome Web Store — one click, no Developer mode, and Chrome keeps it updated. The Web Store build is reviewed before each release, so it can trail the npm package by a version; it pairs with any server and simply skips features it predates.
Load the bundled folder (below) — always matches the npm package you just installed, and the right choice when you want the newest behaviour.
The extension ships prebuilt inside the npm package, and every time the server boots it copies it to a plain folder right under your home directory:
~/chrome-mcp-extension (macOS / Linux)
%USERPROFILE%\chrome-mcp-extension (Windows)So after step 1 has started the server once (restart your client, or /mcp in
Claude Code), the folder is already there. To create it without a client, or
to print the exact path:
npx -y @mehmoodqureshi/chrome-mcp --extension-pathThen chrome://extensions → enable Developer mode → Load unpacked →
pick chrome-mcp-extension in your home folder. After upgrading the package the
server refreshes the files on its next boot and the extension reloads itself
within 30 seconds; nothing to click. CHROME_MCP_EXTENSION_DIR moves the
folder somewhere else. (Working from a git clone instead? Run
npm install && npm run build:ext first — extension-dist/ is gitignored, and
the server mirrors it to the same home folder.)
3. Pair it — usually nothing to do. Every time the server boots it writes
pairing.json (mode 0600, never shipped in the tarball) into the very
chrome-mcp-extension folder you just loaded. The extension reads that file
from its own folder on startup and pairs itself, so the toolbar badge turns
green with no token to paste. Load the extension before the server has ever
run? It re-checks every 30 seconds and pairs as soon as the file appears.
Where to see the badge: it sits on the extension's icon in Chrome's
toolbar, not on the chrome://extensions page. Chrome hides new extensions
behind the puzzle-piece button at the right of the address bar, so click that,
find MCP Extension for Chrome, and click the pin next to it once; the icon then
stays in the toolbar. Hover it for the status in words.
Badge | Meaning |
green dot | paired and connected |
yellow dots | connecting |
grey circle | not paired yet (no server has run, or no pairing file) |
red exclamation mark | token rejected; the server rotated it, re-pairs by itself in a moment |
Manual fallback (a copied folder, a read-only home): run
npx -y @mehmoodqureshi/chrome-mcp --print-pairing, open the extension's
Options page, and paste the port + token from
~/.chrome-mcp/handshake.json. Values saved there take precedence over the
bundled file.
Running more than one session
Every MCP host session (each Claude terminal, tab or window) starts its own chrome-mcp, and they all share your Chrome at once. The first one to start owns the bridge port and the extension connections — the hub. Each later session finds the port held by a live chrome-mcp and joins it as a peer: its tool calls are relayed through the hub to the same browsers, so every session keeps working side by side. Nobody is disconnected.
When the hub's session ends, its peers race for the port; one takes it over
(with the same token, so the extension re-pairs by itself within a few seconds)
and the rest join the new hub. A call that was in flight at that moment fails
once with EXTENSION_DISCONNECTED and is retried automatically when it is safe
to repeat.
Peers authenticate with the pairing token from the 0600 handshake file, so only your own OS user can join. A chrome-mcp too old to share the port is replaced as before: it is verified to be chrome-mcp, then stopped. Anything that isn't a verified chrome-mcp is never touched — a port held by some other program is reported, never killed.
Sessions share one browser, so they also share its tabs: two sessions driving
the same tab at the same moment will step on each other. Give each session its
own tabs (tab_new), or its own Chrome profile (below).
Each session can also drive several browsers at once: load the extension
in each Chrome profile and they all pair to the same server, each under its own
profile name. Tools act on the active profile — pick it with --profile <name>
at startup or the profile_use tool at runtime.
Naming is automatic. Chrome won't tell an extension which profile it runs in, so
each install keeps a random id and the server names it: the first browser is
default, the next profile-2, then profile-3, and so on. Names are stored in
~/.chrome-mcp/profiles.json, so a browser keeps its name across restarts.
chrome_status lists every paired browser (with its active tab as a hint), and
profile_rename gives one a friendly name (profile-2 → work). To pin a name
yourself instead, type it into the extension's Options → Profile; that always wins.
Without --port, each server binds an ephemeral port (no conflict ever), but the
port changes every boot — so you'd re-pair the extension each time. Pin --port
plus --persist-token for a pair-once setup.
Windows
WSL2 is not required — native Windows works. One config change is, though:
on Windows npx is npx.cmd, a batch shim, and MCP hosts spawn the server
without a shell, which cannot execute a .cmd. So "command": "npx" fails to
start. Wrap it in cmd /c:
{
"mcpServers": {
"chrome-mcp": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@mehmoodqureshi/chrome-mcp",
"--allow-domain", "example.com", "--enable-mutations",
"--persist-token"]
}
}
}Or from Claude Code: claude mcp add chrome-mcp -- cmd /c npx -y @mehmoodqureshi/chrome-mcp --allow-domain example.com
Everything else is the same — load %USERPROFILE%\chrome-mcp-extension and pair
as above.
The tools cover tabs, navigation, interaction (click/type/press/hover/
scroll/select_option), reads (get_text/get_html/screenshot/eval/wait_for),
an accessibility snapshot (interactive elements with stable refs the model can
target instead of guessing CSS selectors), session access (get_cookies/storage),
helpers (extract_links/read_as_markdown/fill_form/download_file/upload_file),
and chrome_status. upload_file sets local file(s) on a file <input> without the
OS dialog (requires --enable-uploads).
click/type accept trusted: true for real OS-level input (works on
React/Vue controlled inputs); interactions auto-wait for the target to appear.
Driving several tabs at once — batch
batch runs many tool calls in one request — parallel (default) or
serial (with optional stopOnError). Each sub-op goes through the same
policy gate, rate limit, and error handling as a direct call (no bypass,
no nesting). Use it to fan work out across tabs:
// open three product pages (background, so they don't fight for focus)…
{ "name": "batch", "arguments": { "ops": [
{ "tool": "tab_new", "args": { "url": "https://a.example/p" } },
{ "tool": "tab_new", "args": { "url": "https://b.example/p" } },
{ "tool": "tab_new", "args": { "url": "https://c.example/p" } }
]}}
// …then read them all at once (wall-clock ≈ the slowest one, not the sum)
{ "name": "batch", "arguments": { "ops": [
{ "tool": "get_text", "args": { "tabId": "<a tabId>" } },
{ "tool": "get_text", "args": { "tabId": "<b tabId>" } },
{ "tool": "get_text", "args": { "tabId": "<c tabId>" } }
]}}In parallel mode, tab-scoped ops must pass an explicit tabId — the
active-tab default is unsafe under concurrency, so it's rejected rather than
silently mis-routed. (tab_new, tabs_list, chrome_status are exempt.)
tab_newfocuses the new tab by default (so "open X" behaves like opening a link, instead of replacing your current page — usetab_new, notnavigate, to open without losing the current tab). Passactive: falseto open in the background; parallel batches do this automatically.
Reaching into iframes and shadow roots
A selector that "should" match but doesn't almost always means the element is
somewhere your selector cannot reach: inside an <iframe> (checkout widgets,
OAuth consent screens, embedded editors) or inside a web component's shadow root.
Shadow roots are handled for you — every selector and every ref now resolves
through open shadow roots, so anything snapshot shows you is something you can
click. (It used to show you elements no click could reach: the snapshot walked
shadow roots, the actions did not.)
Frames are opt-in, because reaching into one is a decision:
frames_list {} // what frames exist, and their URLs
click { "selector": "#pay", "allFrames": true } // find it in whichever frame has it
get_text { "frameId": 7 } // pin one frameEvery frame is authorized against its own URL before anything runs in it, so an allowlisted page embedding a third-party iframe does not become a way to read that third party. Frames whose origin isn't on your allowlist are skipped.
Seeing why a page broke — console_logs, network_log, dialogs
Reading the DOM tells you what a page looks like after it failed, not why. With
--enable-observers, an in-page hook records console output, uncaught errors,
and fetch/XMLHttpRequest traffic, and intercepts native dialogs:
console_logs { "level": "error" } // the exception the page swallowed
network_log { "failedOnly": true } // the 500 behind the blank screen
dialogs { "policy": "accept" } // answer confirm() with true from here onIt is off by default and deliberately so: the hook patches console,
fetch, XMLHttpRequest and the dialog functions on every allowlisted page in
your real browser. When it's on, it is registered only for the domains on your
allowlist, at document_start (so it catches load-time failures), and nothing it
records leaves the page until a tool call reads it — through the same gate as any
other page read.
Dialog interception is also a fix, not just an observation: alert/confirm/
beforeunload block the renderer, so a click that opened one used to hang every
injected script until the command timed out and reported TIMEOUT with nothing
to point at. With observers on, the dialog is answered (dismiss by default:
confirm → false, prompt → null) and recorded.
What
network_logsees: the requests page code makes —fetchandXMLHttpRequest, with method, URL, status and duration — plus Resource Timing entries (scripts, images, styles) when you ask for them. Not the document request, redirects, or headers. That is the cost of not holding a debugger session open on your browser.
Only what changed — snapshot { diff: true }
A snapshot is the most expensive read in the tool surface, and the loop that uses it most (snapshot → click → snapshot) re-sends a page that is mostly identical every time. Ask for the delta instead:
snapshot { "diff": true } // added / removed / changed only
click { "selector": "#save", "snapshotAfter": true } // what the click changedNodes are matched across snapshots by role + accessible name, not by ref —
refs renumber in document order on every snapshot, so diffing on them would
report an unchanged button as removed-and-re-added the moment anything above it
appears.
Targeting by role and name
Actions accept a locator instead of a CSS selector, so you don't need a snapshot first just to learn a ref:
click { "role": "button", "name": "Sign in" }
type { "role": "textbox", "name": "Email", "text": "a@b.com" }Resolution is server-side and refuses to guess: an ambiguous locator fails with
the candidates listed rather than acting on the first one (pass nth to pick).
Did the session expire? — auth_check and failOnAuthWall
Reusing a signed-in Chrome removes the login step, but a session cookie can
still expire mid-run. Without a distinct signal the next step fails as
SELECTOR_NOT_FOUND or TIMEOUT, and an eval harness scores the run as an
agent failure when it was an auth failure. Every snapshot now carries an
authWall verdict when the page looks like a sign-in wall, and there is a
dedicated probe:
auth_check {} // { authRequired, confidence, signals }
auth_check { "failOnAuthWall": true } // [AUTH_REQUIRED] error instead
navigate { "url": "https://app.example.com/dashboard", "failOnAuthWall": true }
snapshot { "failOnAuthWall": true }For a harness, set it once instead of per call:
npx -y @mehmoodqureshi/chrome-mcp --allow-domain app.example.com --enable-mutations --fail-on-auth-wallWith the flag on, every step that can move the tab (navigate, click, type,
select_option, press, fill_form, back, forward, reload) checks the
page it landed on and fails with [AUTH_REQUIRED] if that page is a sign-in
wall, and a wait_for that times out on such a page reports [AUTH_REQUIRED]
instead of [TIMEOUT]. Each guarded step costs one extra snapshot round-trip;
with the flag off the cost is zero. [AUTH_REQUIRED] is where a harness pauses
for a human to sign in again in the same Chrome, then retries the step. chrome-mcp
never re-authenticates on its own: it holds no credentials, by design.
Detection reads only what the snapshot already has: the URL (sign-in routes,
identity-provider hosts such as accounts.google.com, login.microsoftonline.com,
Okta, Auth0), the title, password fields, and sign-in controls. high
confidence needs two independent cues (a password field plus a sign-in button,
say); a lone password field or a bare /auth/... URL is medium. A header
"Sign in" link on an ordinary page never counts. failOnAuthWall fires only on
high, so a harness can bucket [AUTH_REQUIRED] separately from every other
failure while a settings page with a "current password" field carries on.
Printing — print_pdf
print_pdf { "landscape": true }Renders through Chrome's own print pipeline and saves to the task's results/
dir, returning the path and size. The bytes themselves are never returned — a
PDF is megabytes of base64 no model can read.
Paying less per turn — --tools
Every MCP server you connect costs context before you ask it anything: the host sends the whole tool catalog to the model on every turn. chrome-mcp's 39 tools are 27 KB of JSON Schema, about 6.9k tokens, on each one.
Most runs need a handful of them. --tools advertises only those:
npx -y @mehmoodqureshi/chrome-mcp \
--allow-domain app.example.com --enable-mutations \
--tools tabs_list,tab_new,navigate,snapshot,click,type,get_textThat surface is 6.0 KB, ~1.5k tokens — an 82% cut against the full catalog, for a run that was never going to print a PDF or upload a file.
Comma-separated and repeatable:
--tools navigate,get_text --tools click.A tool left out is hidden from
tools/listand refused if called — abatchop naming it fails the same way an unknown tool does. Hiding a tool is a real restriction, not a display filter. It is not a substitute for the policy gate, though:--tools evalstill does nothing without--unsafe-enable-eval.An unknown name fails at startup and prints the catalog, so a typo can never quietly drop
clickfrom the surface.chrome-mcp --helpprints the full catalog of 39 names to pick from.
Related MCP server: Browser Tools for Claude Code
Status
v0.5.0 — safe multi-tab concurrency. Adds the batch fan-out tool, makes
parallel tab automation race-free (explicit-tabId guard; per-tab
chrome.debugger serialization; collision-free tab_new), captures screenshots
via chrome.debugger (a specific tab without stealing focus — plus true
full-page and element capture), and focuses newly opened tabs by default. 111
automated tests + a gated headed extension smoke.
v0.2.0 — all six build phases complete and green. End-to-end working:
npx chrome-mcp ⇄ bridge ⇄ extension ⇄ your real Chrome, with a Playwright CDP
fallback. v0.2 adds the accessibility snapshot + element refs, auto-wait,
cookies/storage/select_option, trusted input (chrome.debugger), a toolbar
status badge, and a stable pairing token (--persist-token).
Phase 0 — Contracts & skeleton:
shared/protocol.ts(wire contract),src/executor/types.ts(Executor interface),src/security/policy.ts(default-deny policy + capability gates),src/config.ts(CLI/env/policy resolution), build + test harness.Phase 1 — MCP server + StubExecutor:
mcp/server.ts(clean-stdout stdio),mcp/tools.ts(28-tool catalog + never-throw dispatch + drift-check), validators/envelopes/helpers,ExecutorManager+StubExecutor,cli.ts. Point an MCP host atnode dist/src/cli.jstoday.Phase 2 — WebSocket bridge + auth:
bridge/server.ts(loopback WS, hello-token gate, welcome/unauthorized, displacement),bridge/auth.ts(per-boot 256-bit token, atomic-0600 handshake, SHA-256timingSafeEqual),bridge/connection.ts(id-correlation, method-aware timeouts, backpressure, reject-all-on-close, heartbeat).Phase 3 — ExtensionExecutor + CdpExecutor + selection:
executor/extension-executor.ts(Executor over the bridge),executor/cdp-executor.ts(Playwright connect/launch + lock recovery + tab resolution),executor/select.ts(extension-if-ping-responsive else CDP). CLI now starts the bridge, writes the 0600 handshake, and serves a real backend. Addsplaywright.Phase 4 — MV3 extension:
extension/—manifest.json,sw/ws-client.ts(dial + hello/welcome + pong),sw/executor.ts(chrome.scripting/chrome.tabs command impls),sw/router.ts(never-throw + drift),sw/background.ts(top-level listeners + 25s keepalive/reconnect), options page (manual pairing), esbuild build →extension-dist/. Verified by a live--load-extensionsmoke (pair → navigate → get_text). Addsesbuild+@types/chrome.Phase 5 — Helpers, downloads, HITL: hardened
download_file(shared/download.ts— path-traversal/dangerous-ext sanitize + size cap, wired into both backends), richerread_as_markdown, and a human-in-the-loop harness (hitl/—npm run test:hitl [-- --include-mutating]) with pure, unit-tested gating. 50 automated tests.Phase 6 — Packaging & docs:
fileswhitelist (shipsdist/src,dist/shared,extension-dist, LICENSE, blueprint — not source/tests),prepackbuild,bin, quickstart +.mcp.jsonsnippet. Verified by a tarball install smoke (npm pack→ install → MCPtools/list).
Security posture (default)
Deny-all safe mode. With no policy configured: empty domain allowlist,
eval off, downloads off, mutating tools off. Opt in explicitly:
chrome-mcp --allow-domain example.com --enable-mutations
chrome-mcp --policy ./policy.json # see policy.example.json
chrome-mcp --unsafe-all-domains # loud footgun
chrome-mcp --enable-observers # console/network/dialog capture (patches page globals)
chrome-mcp --redact # scrub secret-shaped strings out of page readsWhat comes back is gated too. The allowlist decides which pages may be read; it says nothing about what is on them. A logged-in page routinely renders a session token into a script tag or an API key onto a settings screen.
Password field values are always suppressed — in
get_html, and insnapshot, where the field still appears (so you can type into it) flaggedsecret: truewith no value. No flag, no opt-in: nobody wants those characters.--redactadditionally scrubs secret-shaped strings — JWTs, AWS/GitHub/Slack/ Google keys,Bearerheaders, private-key blocks — out ofget_text,get_html,read_as_markdownandeval. It is opt-in because a pattern will eventually fire on something you actually wanted.--redact-pattern <regex>adds your own (and implies--redact); an invalid one fails at startup rather than silently never matching.Redaction runs before the output cap, so a truncated read cannot leak what a full one would have hidden.
Every call is recorded to the task's history.jsonl with the URL it touched, the
policy verdict (allowed/denied), how long it took, how many bytes came back,
and how many secrets were scrubbed — so "what did the agent do in my browser" has
an answer after the fact.
The per-boot 256-bit token in ~/.chrome-mcp/handshake.json (mode 0600) is the
only trust boundary; it is never written to stdout/stderr. On POSIX the mode is
re-verified after every write and the server fails closed if the file ends up
group/other-accessible. Windows has no such bits — chmod there only toggles the
read-only attribute — so the check is skipped and the token's confidentiality
rests on the per-user ACL of %USERPROFILE%\.chrome-mcp.
Telemetry
The chrome-mcp server sends anonymous usage statistics to PostHog, so the project can see how many installs are active, which versions and platforms are in use, and which tools fail most. A notice is printed the first time it runs.
What is sent: a random install id (kept in ~/.chrome-mcp/telemetry.json), the
chrome-mcp version, OS, CPU architecture and Node major version, whether the
session owns the bridge port or shares it, how many browsers are paired, and
per-tool call and error counts with error codes — batched every 10 minutes.
What is never sent: URLs, domains, tool arguments, page content, screenshots, cookies, profile names, tokens, file paths, or anything you type. Events are personless and GeoIP lookup is disabled.
The browser extension sends nothing — it only ever talks to 127.0.0.1.
Turn it off with any of:
CHROME_MCP_TELEMETRY=0 # or false / off
DO_NOT_TRACK=1
--no-telemetry # server flagDevelop
npm install
npm run typecheck # server/test sources
npm run typecheck:ext # extension sources (@types/chrome)
npm run build:ext # bundle the extension → extension-dist/
npm test # builds, then runs node --test on dist/test
RUN_EXT_SMOKE=1 node --test dist/test/extension-smoke.test.js # live, headedThe extension
Published on the Chrome Web Store as
MCP Extension for Chrome.
Extension versions move only when extension/ changes, so the listed build can
sit a release behind the npm package; the two negotiate capabilities on
connect, so an older extension loses features rather than breaking.
extension/ builds (esbuild) to extension-dist/, loaded via
chrome://extensions → Load unpacked → select ~/chrome-mcp-extension, the
mirror the server refreshes from extension-dist/ on every boot (loading
extension-dist/ directly also works). It pairs itself from the pairing.json
the server writes into that folder; the Options page paste of port +
token from ~/.chrome-mcp/handshake.json (run
npx -y @mehmoodqureshi/chrome-mcp --print-pairing to get the path) is only
the fallback.
Reads/interaction use
chrome.scripting/chrome.tabs— no "is being debugged" banner, CSP-safe reads (isolated world), testable under Playwright.chrome.debuggeris used only where it's needed and worth it:trusted: trueinput (real OS-level events on React/Vue inputs) andscreenshot(captures a specific tab without activating it — safe under parallelbatch— with true full-page and element capture). Those ops show the debug banner while attached; the session lingers 1.5s after the last op so a burst of them attaches once. Screenshots are JPEG (quality 70) at CSS-pixel size by default — passformat: "png",quality, orscaleto change that.
Available Tools
20 toolsauth_checkA
Is the tab sitting on a sign-in wall? Reads the page (URL, title, password fields, sign-in controls) and returns { authRequired, confidence, signals }. Use it after a navigate, or whenever a step fails unexpectedly, to tell "the session expired" apart from "the agent got lost". Pass failOnAuthWall:true to get an [AUTH_REQUIRED] error instead of a verdict, so a harness can bucket the run as an auth failure.
| Name | Required | Description | Default |
|---|---|---|---|
| tabId | No | Tab id (default: active tab) | |
| frameId | No | Frame id from frames_list | |
| allFrames | No | Act on the first match in ANY frame (the element may be in an iframe) | |
| failOnAuthWall | No | Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral disclosure burden. It discloses that the tool reads the page, returns a verdict object with authRequired/confidence/signals, and that failOnAuthWall changes success into an [AUTH_REQUIRED] error. This is strong behavioral transparency, though it doesn't mention side effects or permissions, which are minor for a read-only diagnostic.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Each of the three sentences earns its place: the purpose/return shape, the recommended invocation timing, and the fail-fast mode. The description is front-loaded and avoids redundant restatements of the name or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given there is no output schema, the description compensates by providing the return shape, use case, and error behavior. This is sufficient for an agent to select and invoke the tool correctly. A small gap is that the semantics of the 'signals' field and the behavior when authRequired is false are not expanded, but they are inferable from the context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds meaningful semantics for failOnAuthWall by explaining the error mode versus a normal verdict and the harness-bucketing purpose. It doesn't add detail for tabId/frameId/allFrames, but the schema already documents those adequately.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise question and immediately states the behavior: reads page content (URL, title, password fields, sign-in controls) and returns an auth verdict. It clearly differentiates this from the sibling navigation and page-interaction tools by focusing on auth-wall detection.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The tool gives explicit context for when to invoke it: after a navigate, or whenever a step fails unexpectedly, to distinguish expired sessions from navigation errors. It does not explicitly name alternatives or state when not to use it, but the guidance is clear enough for an agent to route reasonably.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
batchA
Run multiple tool calls in one request — parallel (default) or serial. Each op is { tool, args } and goes through the same policy gate, rate limit, and error handling as a direct call. In parallel mode, tab-scoped ops MUST pass an explicit tabId (the active-tab default is unsafe under concurrency). Use to drive several tabs at once (e.g. open tabs, then batch get_text across them). Cannot be nested.
| Name | Required | Description | Default |
|---|---|---|---|
| ops | Yes | Operations to run; each is a tool name + its args. | |
| mode | No | Default "parallel". | |
| stopOnError | No | Serial mode only: stop after the first failing op (the rest are skipped). | |
| maxConcurrency | No | Parallel mode: max ops in flight at once (default 6). | |
| maxResultBytes | No | Total payload budget across all ops (default 1048576). Ops past the budget are replaced by a one-line summary instead of their content, so a 50-op screenshot/get_html batch cannot flood the caller. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses that ops go through the same policy gate, rate limit, and error handling as direct calls, warns about concurrency safety for tab-scoped ops, and explains the payload budget behavior (replacing oversized results with summaries). This is thorough and prevents misuse.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded with the core purpose. Each sentence adds value: mode explanation, op format, policy/rate limit note, concurrency warning, usage example, and nesting prohibition. No fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a meta-tool with no output schema, the description covers the critical behavioral aspects: modes, error handling, concurrency, nesting, and payload budget. It does not explicitly describe the return format (e.g., array of results per op), but given the complexity and the fact that it's a wrapper around other tools, this is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful context beyond the schema: it explains the shape of each op ({ tool, args }), the concurrency requirement for tabId, and the budget replacement behavior. This elevates it above the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states clearly that the tool runs multiple tool calls in one request, with parallel or serial modes. It distinguishes itself from sibling tools by being a meta-tool, and the 'Cannot be nested' constraint adds further specificity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage guidance: 'Use to drive several tabs at once (e.g. open tabs, then batch get_text across them)' and warns about the need for explicit tabId in parallel mode. It also explains the stopOnError and maxResultBytes behaviors, making it clear when to use each mode.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
chrome_statusA
Report backend/session status: paired browser profiles and how each was named, and flags for switched-off capabilities.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. The word 'Report' implies a read-only operation and the sentence does indicate the kind of information returned, but it does not explicitly state side-effect freedom, backend interaction, or how capabilities are determined.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single well-structured sentence with the verb and resource front-loaded, followed by concrete output details. Every phrase adds useful content and there is no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-parameter status tool, the description covers the major expected output categories: paired profiles, profile naming, and disabled capability flags. It does not describe the return structure or formatting, but the low complexity and absent output schema make the description largely sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the schema is empty, so there is nothing meaningful for the description to add. Per the baseline for zero-parameter tools, this is handled appropriately without parameter documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses the verb 'Report' with a specific resource, 'backend/session status', then enumerates what is included: paired browser profiles, how they were named, and capability flags. This clearly distinguishes it from the sibling status/auth/profile tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance about when to use this tool relative to similarly named siblings such as auth_check or task_status, and no exclusions or alternatives are mentioned. The only implication is that it provides status information, which is too weak to route an agent correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
extract_linksB
Extract anchors from the page or a subtree. dedupe=true collapses links sharing an href (nav/footer noise); limit caps the count.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| tabId | No | Tab id (default: active tab) | |
| dedupe | No | ||
| frameId | No | Frame id from frames_list | |
| selector | No | ||
| allFrames | No | Act on the first match in ANY frame (the element may be in an iframe) | |
| sameOriginOnly | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral transparency burden. It does disclose meaningful behavior around dedupe (collapses links sharing href) and limit (caps count). But it does not mention read-only status, same-origin filtering behavior, frame/allFrames semantics, or what happens when no anchors are found.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence: it states the core function first, then provides two concise parameter clarifications. Every clause earns its place and there is no boilerplate or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 7 parameters, no output schema, and no annotations, the description covers only dedupe and limit meaningfully. It is missing return format details, sameOriginOnly semantics, how to specify the subtree via selector, and behavior across frames, making it incomplete for reliable invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 43%, so the description must compensate. It adds real meaning for dedupe and limit, and 'subtree' hints at selector usage. However, sameOriginOnly is left completely unexplained and the selector parameter is not clearly described as the mechanism for selecting the subtree, leaving important parameters ambiguous.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific action ('Extract anchors') and a scope ('the page or a subtree'), which distinguishes it from sibling content tools like get_text or get_html. It could more explicitly say that it returns anchor URLs/hrefs, 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives useful context for when to enable dedupe ('nav/footer noise') and limit, and mentions the option of a subtree. However, it does not explicitly contrast this tool with alternatives like get_html, read_as_markdown, or get_text, nor does it explain when to use sameOriginOnly or frame targeting.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
frames_listA
List the tab's frames (the top document plus every iframe the extension can reach), with each frame's id and URL. Use it when a selector that should match does not: the element is probably in one of these frames, and you can then pass frameId (or allFrames:true) to act inside it.
| Name | Required | Description | Default |
|---|---|---|---|
| tabId | No | Tab id (default: active tab) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden. It discloses that only frames the extension can reach are included, which is a behavioral boundary. It also states what output to expect (id and URL), but doesn't explicitly state it's read-only, though that's inferable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences: the first states the core function and output, the second gives a practical use case. No fluff, information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one optional parameter and no output schema, the description covers purpose, output format, and a use case. It lacks explicit return type details (e.g., array of objects) but provides enough for an agent to understand the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter tabId is fully described in the schema with default behavior, so the description adds no new information about parameters. It does reference frameId and allFrames:true, but those are for other tools, not this one.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists the tab's frames, including the top document and reachable iframes, with each frame's id and URL. It uses a specific verb 'List' and resource 'frames', distinguishing it from sibling tools which are actions like click or type.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells when to use it: when a selector doesn't match, the element might be in an iframe, and then to use the result to pass frameId or allFrames:true. This gives a concrete scenario and guidance on how to proceed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_cookiesA
Read cookies visible to the tab's URL (or a given url).
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | ||
| tabId | No | Tab id (default: active tab) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the burden. It does disclose a read-only, non-mutating operation and URL-scoped visibility, but it does not mention return shape, potential restrictions (e.g., HttpOnly/secure cookies), or whether url and tabId interact. Useful but thin.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence with no filler. The core action and the optional override are stated in the fewest words needed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-optional-param read tool, the core behavior is covered, but with no output schema or annotations the agent is left to infer return format and call semantics. The description is adequate but not fully self-sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema only documents tabId; description adds meaning to url ('or a given url'), showing it can override the active tab context. However, it does not clarify URL format, precedence when both url and tabId are supplied, or any constraints, and schema coverage is only 50%, so compensation is partial.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Read'), a specific resource ('cookies'), and an explicit scoping rule (cookies visible to the tab's URL or a provided url). This clearly separates it from sibling tools like storage, network_log, or DOM-read tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The expected use is implied by 'Read cookies', but there is no statement about when to prefer this over alternatives nor any exclusions or prerequisites. It tells the agent what it does but not when-to-use vs. when-not-to-use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_htmlA
Get HTML of the page or an element. Output is capped (see maxBytes) and cut at a tag boundary; narrow it with selector rather than raising the cap when you can. Password field values are always blanked.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Element ref from a snapshot | |
| outer | No | ||
| tabId | No | Tab id (default: active tab) | |
| frameId | No | Frame id from frames_list | |
| maxBytes | No | Cap returned content at N UTF-8 bytes (default 262144); the full payload still lands in results/ | |
| selector | No | CSS selector (or pass ref) | |
| allFrames | No | Act on the first match in ANY frame (the element may be in an iframe) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the disclosure burden. It reveals output truncation at tag boundaries, the maxBytes cap, and password field blanking—behaviors beyond what the input schema states. It does not mention read-only nature or auth, but for a fetch operation this is a solid level of transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no waste: purpose first, then practical guidance, then a privacy-relevant warning. Each sentence earns its place and is front-loaded with the most important information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers core behavior for a 7-parameter tool with no annotations and no output schema: purpose, cap behavior, selector advice, and password masking. It omits explicit output format and how to choose between page vs element targeting, but the raw-HTML return is intuitive and schema descriptions cover most parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 86%, so parameters are mostly documented. The description adds semantic value by relating selector and maxBytes ('narrow it with selector rather than raising the cap'), but it offers no clarification for outer and doesn't enrich the meaning of ref, tabId, frameId, or allFrames beyond schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: retrieving HTML of a page or an element. This clearly distinguishes it from sibling tools like get_text, snapshot, or read_as_markdown, which produce different representations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides actionable usage guidance ('narrow it with selector rather than raising the cap when you can') but does not explicitly say when to choose this tool over siblings like get_text or snapshot. The intended context is implied rather than explicitly contrasted.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_textB
Get visible text of the page or an element.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Element ref from a snapshot | |
| tabId | No | Tab id (default: active tab) | |
| frameId | No | Frame id from frames_list | |
| maxBytes | No | Cap returned content at N UTF-8 bytes (default 262144); the full payload still lands in results/ | |
| selector | No | CSS selector (or pass ref) | |
| allFrames | No | Act on the first match in ANY frame (the element may be in an iframe) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description must carry the full burden of behavioral disclosure. It only says 'visible text' without defining what counts as visible, how hidden or rendered text is handled, whether it returns plain text or formatted content, or any side effects. The behavior is underspecified.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence with no wasted words. It is front-loaded with the core purpose, though its brevity sacrifices necessary detail—this is more a completeness issue than a conciseness one.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given six parameters, no output schema, and no annotations, the description is severely incomplete. It fails to explain how parameters like ref, selector, and allFrames interact, what the output format is, or when to prefer this over related tools. Agents lack essential context to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all six parameters are already documented in the schema with meaningful descriptions. The tool description adds no additional parameter semantics, so the baseline score of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb ('Get') and resource ('visible text of the page or an element'), which distinguishes it from siblings like get_html or read_as_markdown. It conveys the core function unambiguously.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description offers no guidance on when to use this tool versus alternatives such as get_html, read_as_markdown, or snapshot. There is no mention of context, exclusions, or criteria for selection, leaving the agent to infer usage from the name and schema alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
print_pdfA
Render the page to PDF through Chrome's own print pipeline and save it to the task's results/ dir. Returns the path and size, not the bytes - a PDF is not something to spend context on.
| Name | Required | Description | Default |
|---|---|---|---|
| scale | No | 0.1 - 2.0 | |
| tabId | No | Tab id (default: active tab) | |
| landscape | No | ||
| pageRanges | No | e.g. '1-3, 5' | |
| paperWidth | No | Inches | |
| paperHeight | No | Inches | |
| printBackground | No | Include background graphics (default true) | |
| preferCSSPageSize | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden and does substantial work: it discloses the side effect (file saved to results/ dir), the return contract (path and size, not bytes), and even the rationale for that contract. It stops short of mentioning failure modes or tab-state requirements, but the core behavioral profile is well covered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, ~40 words, with the action front-loaded and zero filler. The second sentence earns its place by explaining the return shape and the design rationale, which are essential for the agent to know before invoking.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema and no annotations, the description covers the three things an agent must know: what happens (PDF rendered via print pipeline), where output goes (results/ dir), and what comes back (path and size). The main gap is explicit guidance distinguishing this from screenshot for page-visual needs, and minor details like error behavior or the meaning of the returned path.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 75%, so the schema already documents six of eight parameters with ranges and defaults. The description adds no parameter-level detail, but it doesn't need to for the covered parameters; the two uncovered ones (landscape, preferCSSPageSize) are self-descriptive booleans. This lands at the baseline: schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb (render/save), a specific resource (the page → PDF), and the mechanism (Chrome's print pipeline). It clearly differentiates from visual siblings like screenshot by specifying PDF format and the print pipeline, and from download_file by describing where the output lands.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The second sentence implies usage context: use this to produce a PDF artifact for the user, not to consume content yourself ('a PDF is not something to spend context on'). However, it never explicitly names alternatives like screenshot or read_as_markdown or states when to prefer one over the other, leaving the routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
profile_renameA
Rename an automatically named browser profile (e.g. "profile-2" -> "work"). The name sticks across restarts; its saved artifacts move with it.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | New profile name | |
| from | Yes | Current profile name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It explicitly states the name persists across restarts and that saved artifacts move with the profile, which are important side effects. However, it does not mention error conditions, permissions, or reversibility, so it is informative but not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no fluff. The action is front-loaded, and the second sentence adds key behavioral context without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple rename tool with two string parameters and no output schema, the description covers the purpose and two important behavioral aspects. It is sufficient for an agent to understand how to call it, though it does not cover edge cases like non-existent profiles or naming constraints.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Both parameters are fully described in the schema ('from' and 'to'), and the description provides a concrete example that clarifies expected formats. Since schema coverage is 100%, the description adds marginal value beyond the schema, but the example helps grounding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool renames browser profiles, with a concrete example ('profile-2' -> 'work'). It identifies the resource (browser profile) and the action (rename), distinguishing it from sibling profile_use which selects profiles rather than renames them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for renaming automatically named profiles, but does not explicitly state when to use it over alternatives or any exclusions. It provides context (names stick, artifacts move) but lacks direct guidance on selection criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
profile_useA
Switch the active browser profile (identity). Subsequent downloads, results, screenshots, and the action log are stored under profiles//. Resets the active task to "default" unless you then call task_new.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Profile name (becomes a folder; sanitized to a safe path segment). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without annotations, the description carries the full burden. It discloses two behavioral traits: storage location change and task reset. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with purpose. Every sentence adds value with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with no output schema, the description covers purpose, storage effect, and task side effect. It is sufficiently complete for an AI agent to use correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema has 100% coverage, so baseline is 3. The description adds no additional meaning about the 'name' parameter beyond what the schema already provides (folder creation, sanitization).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb 'Switch' and resource 'active browser profile (identity)', clearly distinguishing it from other browser actions like navigate or click. The purpose is immediately clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains when to use the tool by stating that subsequent actions are stored under profiles/<name>/ and that it resets the active task to 'default' unless task_new is called. It does not explicitly state when not to use it, but the guidance is adequate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_as_markdownC
Read the page (or subtree) as readable markdown.
| Name | Required | Description | Default |
|---|---|---|---|
| tabId | No | Tab id (default: active tab) | |
| frameId | No | Frame id from frames_list | |
| maxBytes | No | Cap returned content at N UTF-8 bytes (default 262144); the full payload still lands in results/ | |
| selector | No | ||
| allFrames | No | Act on the first match in ANY frame (the element may be in an iframe) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description must carry behavioral disclosure, but it only states that the action is a read and the output is markdown. It does not describe side effects, persistence, auth requirements, failure modes, or frame-related behavior. The maxBytes parameter description mentions results/, but the tool description itself does not explain that behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single short sentence with the core action and output format front-loaded. There is no filler or redundant context.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has five parameters, no annotations, and no output schema, yet the description offers only a high-level capability. It does not explain the return value, what 'subtree' means for selector, how maxBytes affects results, or when to choose this over siblings. An agent would need to inspect the schema and infer execution semantics on its own.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 80%, so the schema already documents tabId, frameId, maxBytes, and allFrames; the high-coverage baseline applies. The description adds no parameter-level detail and does not clarify the undocumented 'selector' parameter, so it neither improves nor harms parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Read'), a resource ('the page (or subtree)'), and an output format ('readable markdown'), so an agent can tell this is a content-extraction tool. It does not explicitly compare with get_text/get_html/snapshot, but the markdown format is distinct enough to differentiate from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description contains no when-to-use language, prerequisites, or alternatives. It does not say 'use this instead of get_text/get_html when you want markdown,' and it offers no exclusions. An agent must infer use cases solely from the tool name and sibling list.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
screenshotA
Capture a screenshot (page or element). Default is JPEG (quality 70) at CSS-pixel size, which is several times smaller than PNG and reads fine. Pass format:"png" for lossless, quality 1-100 for JPEG, scale 2 for device pixels on a Retina display or 0.5 to shrink.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Element ref from a snapshot | |
| scale | No | ||
| tabId | No | Tab id (default: active tab) | |
| format | No | ||
| frameId | No | Frame id from frames_list | |
| quality | No | ||
| fullPage | No | ||
| selector | No | CSS selector (or pass ref) | |
| allFrames | No | Act on the first match in ANY frame (the element may be in an iframe) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden fall. It usefully discloses the default JPEG quality 70, CSS-pixel size, file-size tradeoff, and how to adjust format, quality, and scale. However, it does not state what the tool returns (file path, base64, etc.) or the default for fullPage, which is a notable gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, with the purpose front-loaded and no repeated schema content. The phrase 'reads fine' is slightly vague and could be removed, but the overall structure is efficient and informative.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 9 optional parameters and no output schema, the description covers the core format/quality/scale behavior but omits important execution context: fullPage default, ref-vs-selector precedence, and return value format. The schema fills some gaps (tabId, frameId, allFrames), but an agent cannot fully predict the tool's output or default behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers only 56% of parameters (5 of 9 have descriptions). The description compensates by explaining format ('png' for lossless), quality (1-100 for JPEG), and scale (2 for Retina, 0.5 to shrink) — all of which lack schema descriptions. It does not explain fullPage, but that parameter is relatively self-explanatory.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Capture a screenshot (page or element).' This clearly defines the tool's function and distinguishes it from siblings like print_pdf or snapshot. The additional details about format and scale further clarify that this is a visual pixel capture tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
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 a screenshot is needed) and gives usage hints for scale and format, but it does not explicitly contrast with alternatives such as print_pdf for PDF output or snapshot for DOM extraction. Exclusion criteria are absent, so the guidance is only implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
snapshotA
Accessibility snapshot: interactive elements with refs to target by ref (more reliable than guessing CSS selectors). Pass diff:true to get only what changed since the last snapshot of this tab - far cheaper in a click/read loop. Password fields appear as secret:true with no value.
| Name | Required | Description | Default |
|---|---|---|---|
| max | No | ||
| diff | No | Return added/removed/changed elements since the previous snapshot of this tab instead of the whole tree | |
| tabId | No | Tab id (default: active tab) | |
| frameId | No | Frame id from frames_list | |
| allFrames | No | Act on the first match in ANY frame (the element may be in an iframe) | |
| failOnAuthWall | No | Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session) | |
| interactiveOnly | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral disclosure burden. It adds valuable details: refs are more reliable than CSS selectors, diff mode is cheaper, and password fields appear as secret:true with no value. It does not mention side effects or failure modes, but as a snapshot operation those are less critical.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise, front-loaded sentences cover purpose, ref targeting, diff use case, and password redaction. Every sentence adds a distinct piece of information with no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The core usage and security behavior are explained, but with no output schema and no annotations, the description leaves gaps around the full return shape and the undocumented `max` and `interactiveOnly` parameters. It is adequate for basic invocation but not fully complete for all 7 parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds useful meaning to the diff parameter by explaining its cost benefit, but it does not illuminate the `max` or `interactiveOnly` parameters, which lack schema descriptions. With 71% schema coverage and some added value, this sits at the baseline rather than above it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool as an accessibility snapshot returning interactive elements with stable `ref` identifiers, and distinguishes it from other inspection tools by emphasizing refs over CSS selectors. This gives an agent a precise sense of what the tool produces and why it is valuable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides clear context for when to use the tool (to get stable refs for interactive elements, especially in click/read loops) and when to use diff mode (to reduce cost). It stops short of explicitly naming sibling alternatives or stating when not to use snapshot, so it lacks the full when-not-to-use guidance of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
storageC
Read/write localStorage (or sessionStorage). op: get|set|remove|clear.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | ||
| key | No | ||
| tabId | No | Tab id (default: active tab) | |
| value | No | ||
| session | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description carries the full behavioral burden. It communicates the basic operation set but does not disclose side effects, whether clear/remove affect only the current tab, serialization behavior, return shape, or what 'sessionStorage' means in terms of the session parameter.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: the primary operation and storage target appear first卫生. It is not bloated, but the terse syntax leaves out parameter relationships that would make it more useful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This tool has five parameters, low schema coverage, no annotations, and no output schema severe. The short description does not cover essential behaviors like how tabId scoping works, what value types are expected, or what the result of 'get' looks like. An agent would likely need to experiment or consult external docs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 20%, and the description only partially compensates by listing op values and mentioning sessionStorage. The key, value, and session parameters remain functionally undocumented in both the schema and the description, so an agent cannot confidently construct a correct call beyond op.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Read/write') and a concrete resource ('localStorage (or sessionStorage)'), and immediately enumerates the supported operations: get|set|remove|clear. This is enough for an agent to understand the tool's core purpose and distinguish it from navigation, tab, and script-execution siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
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 eval, and does not mention any exclusions or prerequisites. It is left entirely to the agent to infer that this is for browser storage access.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tabs_listA
List open browser tabs.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description must disclose behaviors. It mentions listing tabs but does not specify the format or content of the output (e.g., titles, URLs, IDs). The agent lacks insight into what the list contains.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise (5 words) and front-loaded. Every word is necessary and informative.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple list tool with no output schema, the description provides the basic function but fails to clarify what data is returned. It is marginally complete given the tool's simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so the schema is empty. The description adds value by stating the purpose, which is sufficient for a parameterless tool. Baseline 4 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description 'List open browser tabs' clearly identifies the verb (list) and resource (open browser tabs). It distinguishes from sibling tools like tab_new, tab_close, and tab_select, which perform different actions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for retrieving open tabs but provides no explicit guidance on when to use this tool versus alternatives (e.g., tab_select, tab_new). No when-not-to-use scenarios are mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
task_newA
Start a new task (run) under the active profile. Creates profiles//tasks// with downloads/, results/, screenshots/ and makes it the active task so all captured artifacts land there.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Task name (becomes a folder; sanitized to a safe path segment). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses directory creation and active task setting, but lacks details on overwriting behavior, error conditions, or required permissions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no redundant information. The first sentence front-loads the action, and the second provides essential behavioral details concisely.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple single-parameter tool and no output schema, the description adequately explains the creation process and side effects. It could mention duplicate name behavior for completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% with a clear parameter description. The tool description reinforces the folder creation context but adds minimal value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Start a new task (run) under the active profile,' specifying the verb and resource. It distinguishes from sibling tools like tasks_list (listing) and task_status (status) by focusing on creation and activation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance on when to use this tool versus alternatives (e.g., batch or tab_new). The description does not provide when-not conditions or alternative tool references.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tasks_listA
List every task across all profiles under the data dir, with sizes and download counts.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description must carry full burden. It merely states the action and output fields, but omits behavioral traits: performance implications, permission requirements, potential for large data volumes, or edge cases (e.g., no tasks found).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, front-loaded with verb and resource. Every word adds value: 'every task', 'across all profiles', 'with sizes and download counts'. No fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, no-output-schema tool, the description covers the essential purpose and output. However, it lacks any mention of the 'data dir' location, performance scale, or relationship to sibling task tools, missing some completeness for context-rich decision-making.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters in the schema; baseline 4 applies. The description adds no parameter info, but none is needed. Schema coverage is 100% by virtue of being empty.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the verb 'List', the resource 'tasks', and the scope 'across all profiles under the data dir'. Specifies included data: sizes and download counts. Distinguishes from sibling tools like task_new and task_status by focusing on listing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus alternatives like task_new (create) or task_status (check status). Does not mention any contextual triggers or exclusions, leaving the agent to infer usage without support.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
task_statusA
Report the active profile/task and the folder paths where this run's artifacts are stored.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description should explicitly state read-only behavior. It says 'report', implying no side effects, but does not confirm it does not modify state or have rate limits. Barely adequate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, front-loaded with purpose, no extraneous words. Excellent conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the key information reported (active profile/task and folder paths). No output schema exists, but the return values are implied. Slightly vague on what 'active' means, but sufficient for a simple status tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters and 100% coverage, so the description does not need to add parameter details. Baseline 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool reports the active profile/task and artifact folder paths, using a specific verb 'report' and specifying the exact resources. This distinguishes it from sibling tools like tasks_list which list all tasks.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus alternatives such as tasks_list or chrome_status. The description does not mention preconditions or when it is appropriate to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
wait_forB
Wait for a selector or text to appear/disappear.
| Name | Required | Description | Default |
|---|---|---|---|
| gone | No | ||
| tabId | No | Tab id (default: active tab) | |
| frameId | No | Frame id from frames_list | |
| selector | No | ||
| allFrames | No | Act on the first match in ANY frame (the element may be in an iframe) | |
| timeoutMs | No | ||
| textContains | No | ||
| failOnAuthWall | No | Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, but it only states the core wait behavior. It does not disclose default timeout behavior, what happens on timeout, whether 'appear' means visible or attached, or how multiple conditions interact.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no filler or redundancy. Every word contributes to the core meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter tool with no annotations and no output schema, the description is too thin. An agent cannot determine required parameter combinations, default timeout, what 'gone' means precisely, or what happens when the wait condition is not met.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds some semantic meaning by connecting 'selector' and 'text' to the selector/textContains parameters and 'disappear' to gone. However, it leaves timeoutMs, frame targeting, and condition-combination behavior unexplained, and schema coverage is only 50%.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Wait'), names the exact resources ('selector or text'), and captures the two outcomes ('appear/disappear'). It clearly differentiates this from sibling tools like click, type, and navigate.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
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, such as before interacting with a page, after navigation, or in polling scenarios. The description only states what it does, not when it should be chosen.
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.
20 tool updates
v0.9.11- Removed
back - Removed
click - Removed
console_logs - Removed
dialogs - Removed
download_file - Removed
eval - Removed
fill_form - Removed
forward - Removed
hover - Removed
navigate - Removed
network_log - Removed
press - Removed
reload - Removed
scroll - Removed
select_option - Removed
tab_close - Removed
tab_new - Removed
tab_select - Removed
type - Removed
upload_file
30 tool updates
v0.9.5- Changed
auth_check4 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
back2 fields changed- changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
click10 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / name / descriptionPrevious value: -"Target by accessible name/visible label (pairs with role)"New value: +"Accessible name / visible label (pair with role)" - changed
Input schema / properties / nth / descriptionPrevious value: -"Pick the nth (0-based) match when a role+name locator is legitimately ambiguous"New value: +"0-based index when role+name is ambiguous" - changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / role / descriptionPrevious value: -"Target by ARIA role (e.g. button, link, textbox) - alternative to selector/ref"New value: +"ARIA role, e.g. button|link|textbox (pair with name)" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or ref, or role+name)" - changed
Input schema / properties / snapshotAfter / descriptionPrevious value: -"Return what CHANGED on the page after this action (added/removed/changed elements vs the last snapshot) instead of making you re-read the page"New value: +"Return what CHANGED on the page after this action instead of making you re-read it" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
console_logs3 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
dialogs3 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
download_file3 fields changed- changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or pass ref)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
eval3 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
extract_links3 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
fill_form4 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
forward2 fields changed- changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
frames_list1 field changed- changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
get_cookies1 field changed- changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
get_html6 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / maxBytes / descriptionPrevious value: -"Cap the returned content at this many UTF-8 bytes (default 262144). A truncated result reports truncated/totalBytes/returnedBytes. The full payload is still written to the task's results/ dir."New value: +"Cap returned content at N UTF-8 bytes (default 262144); the full payload still lands in results/" - changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or pass ref)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
get_text6 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / maxBytes / descriptionPrevious value: -"Cap the returned content at this many UTF-8 bytes (default 262144). A truncated result reports truncated/totalBytes/returnedBytes. The full payload is still written to the task's results/ dir."New value: +"Cap returned content at N UTF-8 bytes (default 262144); the full payload still lands in results/" - changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or pass ref)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
hover9 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / name / descriptionPrevious value: -"Target by accessible name/visible label (pairs with role)"New value: +"Accessible name / visible label (pair with role)" - changed
Input schema / properties / nth / descriptionPrevious value: -"Pick the nth (0-based) match when a role+name locator is legitimately ambiguous"New value: +"0-based index when role+name is ambiguous" - changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / role / descriptionPrevious value: -"Target by ARIA role (e.g. button, link, textbox) - alternative to selector/ref"New value: +"ARIA role, e.g. button|link|textbox (pair with name)" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or ref, or role+name)" - changed
Input schema / properties / snapshotAfter / descriptionPrevious value: -"Return what CHANGED on the page after this action (added/removed/changed elements vs the last snapshot) instead of making you re-read the page"New value: +"Return what CHANGED on the page after this action instead of making you re-read it" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
navigate2 fields changed- changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
network_log3 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
press2 fields changed- changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
print_pdf1 field changed- changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Added
profile_rename - Changed
read_as_markdown4 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / maxBytes / descriptionPrevious value: -"Cap the returned content at this many UTF-8 bytes (default 262144). A truncated result reports truncated/totalBytes/returnedBytes. The full payload is still written to the task's results/ dir."New value: +"Cap returned content at N UTF-8 bytes (default 262144); the full payload still lands in results/" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
reload2 fields changed- changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
screenshot5 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or pass ref)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
scroll5 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or pass ref)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
select_option10 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / name / descriptionPrevious value: -"Target by accessible name/visible label (pairs with role)"New value: +"Accessible name / visible label (pair with role)" - changed
Input schema / properties / nth / descriptionPrevious value: -"Pick the nth (0-based) match when a role+name locator is legitimately ambiguous"New value: +"0-based index when role+name is ambiguous" - changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / role / descriptionPrevious value: -"Target by ARIA role (e.g. button, link, textbox) - alternative to selector/ref"New value: +"ARIA role, e.g. button|link|textbox (pair with name)" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or ref, or role+name)" - changed
Input schema / properties / snapshotAfter / descriptionPrevious value: -"Return what CHANGED on the page after this action (added/removed/changed elements vs the last snapshot) instead of making you re-read the page"New value: +"Return what CHANGED on the page after this action instead of making you re-read it" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
snapshot4 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
storage1 field changed- changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
type10 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / name / descriptionPrevious value: -"Target by accessible name/visible label (pairs with role)"New value: +"Accessible name / visible label (pair with role)" - changed
Input schema / properties / nth / descriptionPrevious value: -"Pick the nth (0-based) match when a role+name locator is legitimately ambiguous"New value: +"0-based index when role+name is ambiguous" - changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / role / descriptionPrevious value: -"Target by ARIA role (e.g. button, link, textbox) - alternative to selector/ref"New value: +"ARIA role, e.g. button|link|textbox (pair with name)" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or ref, or role+name)" - changed
Input schema / properties / snapshotAfter / descriptionPrevious value: -"Return what CHANGED on the page after this action (added/removed/changed elements vs the last snapshot) instead of making you re-read the page"New value: +"Return what CHANGED on the page after this action instead of making you re-read it" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
upload_file3 fields changed- changed
Input schema / properties / ref / descriptionPrevious value: -"Element ref from a prior read (exactly one of selector|ref)"New value: +"Element ref from a snapshot" - changed
Input schema / properties / selector / descriptionPrevious value: -"CSS selector (exactly one of selector|ref)"New value: +"CSS selector (or pass ref)" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
- Changed
wait_for4 fields changed- changed
Input schema / properties / allFrames / descriptionPrevious value: -"Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)"New value: +"Act on the first match in ANY frame (the element may be in an iframe)" - changed
Input schema / properties / failOnAuthWall / descriptionPrevious value: -"Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way."New value: +"Error with [AUTH_REQUIRED] if this lands on a sign-in wall (expired session)" - changed
Input schema / properties / frameId / descriptionPrevious value: -"Act inside this frame (ids come from frames_list)"New value: +"Frame id from frames_list" - changed
Input schema / properties / tabId / descriptionPrevious value: -"Target tab id (defaults to the active tab)"New value: +"Tab id (default: active tab)"
13 tool updates
v0.9.3- Added
auth_check - Changed
back1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
click1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
fill_form1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
forward1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
navigate1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
press1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
reload1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
screenshot3 fields changed- added
Input schema / properties / formatAdded value: +{ + "enum": [ + "jpeg", + "png" + ], + "type": "string" +} - added
Input schema / properties / qualityAdded value: +{ + "type": "number" +} - added
Input schema / properties / scaleAdded value: +{ + "type": "number" +}
- Changed
select_option1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
snapshot1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
type1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
- Changed
wait_for1 field changed- added
Input schema / properties / failOnAuthWallAdded value: +{ + "description": "Fail with [AUTH_REQUIRED] when the page this call lands on is a high-confidence sign-in wall (session expired). Off by default unless the server runs with --fail-on-auth-wall; snapshot still reports the verdict as `authWall` either way.", + "type": "boolean" +}
20 tool updates
v0.7.0- Changed
batch1 field changed- added
Input schema / properties / maxResultBytesAdded value: +{ + "description": "Total payload budget across all ops (default 1048576). Ops past the budget are replaced by a one-line summary instead of their content, so a 50-op screenshot/get_html batch cannot flood the caller.", + "type": "number" +}
- Changed
click6 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +} - added
Input schema / properties / nameAdded value: +{ + "description": "Target by accessible name/visible label (pairs with role)", + "type": "string" +} - added
Input schema / properties / nthAdded value: +{ + "description": "Pick the nth (0-based) match when a role+name locator is legitimately ambiguous", + "type": "number" +} - added
Input schema / properties / roleAdded value: +{ + "description": "Target by ARIA role (e.g. button, link, textbox) - alternative to selector/ref", + "type": "string" +} - added
Input schema / properties / snapshotAfterAdded value: +{ + "description": "Return what CHANGED on the page after this action (added/removed/changed elements vs the last snapshot) instead of making you re-read the page", + "type": "boolean" +}
- Added
console_logs - Added
dialogs - Changed
eval2 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +}
- Changed
extract_links2 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +}
- Changed
fill_form4 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - removed
Input schema / properties / fields / additionalProperties / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "boolean" - } -] - added
Input schema / properties / fields / additionalProperties / typeAdded value: +[ + "string", + "boolean" +] - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +}
- Added
frames_list - Changed
get_html3 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +} - added
Input schema / properties / maxBytesAdded value: +{ + "description": "Cap the returned content at this many UTF-8 bytes (default 262144). A truncated result reports truncated/totalBytes/returnedBytes. The full payload is still written to the task's results/ dir.", + "type": "number" +}
- Changed
get_text3 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +} - added
Input schema / properties / maxBytesAdded value: +{ + "description": "Cap the returned content at this many UTF-8 bytes (default 262144). A truncated result reports truncated/totalBytes/returnedBytes. The full payload is still written to the task's results/ dir.", + "type": "number" +}
- Changed
hover6 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +} - added
Input schema / properties / nameAdded value: +{ + "description": "Target by accessible name/visible label (pairs with role)", + "type": "string" +} - added
Input schema / properties / nthAdded value: +{ + "description": "Pick the nth (0-based) match when a role+name locator is legitimately ambiguous", + "type": "number" +} - added
Input schema / properties / roleAdded value: +{ + "description": "Target by ARIA role (e.g. button, link, textbox) - alternative to selector/ref", + "type": "string" +} - added
Input schema / properties / snapshotAfterAdded value: +{ + "description": "Return what CHANGED on the page after this action (added/removed/changed elements vs the last snapshot) instead of making you re-read the page", + "type": "boolean" +}
- Added
network_log - Added
print_pdf - Changed
read_as_markdown3 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +} - added
Input schema / properties / maxBytesAdded value: +{ + "description": "Cap the returned content at this many UTF-8 bytes (default 262144). A truncated result reports truncated/totalBytes/returnedBytes. The full payload is still written to the task's results/ dir.", + "type": "number" +}
- Changed
screenshot2 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +}
- Changed
scroll2 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +}
- Changed
select_option6 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +} - added
Input schema / properties / nameAdded value: +{ + "description": "Target by accessible name/visible label (pairs with role)", + "type": "string" +} - added
Input schema / properties / nthAdded value: +{ + "description": "Pick the nth (0-based) match when a role+name locator is legitimately ambiguous", + "type": "number" +} - added
Input schema / properties / roleAdded value: +{ + "description": "Target by ARIA role (e.g. button, link, textbox) - alternative to selector/ref", + "type": "string" +} - added
Input schema / properties / snapshotAfterAdded value: +{ + "description": "Return what CHANGED on the page after this action (added/removed/changed elements vs the last snapshot) instead of making you re-read the page", + "type": "boolean" +}
- Changed
snapshot3 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / diffAdded value: +{ + "description": "Return added/removed/changed elements since the previous snapshot of this tab instead of the whole tree", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +}
- Changed
type6 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +} - added
Input schema / properties / nameAdded value: +{ + "description": "Target by accessible name/visible label (pairs with role)", + "type": "string" +} - added
Input schema / properties / nthAdded value: +{ + "description": "Pick the nth (0-based) match when a role+name locator is legitimately ambiguous", + "type": "number" +} - added
Input schema / properties / roleAdded value: +{ + "description": "Target by ARIA role (e.g. button, link, textbox) - alternative to selector/ref", + "type": "string" +} - added
Input schema / properties / snapshotAfterAdded value: +{ + "description": "Return what CHANGED on the page after this action (added/removed/changed elements vs the last snapshot) instead of making you re-read the page", + "type": "boolean" +}
- Changed
wait_for2 fields changed- added
Input schema / properties / allFramesAdded value: +{ + "description": "Search every frame of the tab and act on the first that matches - use when a selector should match but does not (the element is in an iframe)", + "type": "boolean" +} - added
Input schema / properties / frameIdAdded value: +{ + "description": "Act inside this frame (ids come from frames_list)", + "type": "number" +}
1 tool update
v0.6.6- Changed
extract_links2 fields changed- added
Input schema / properties / dedupeAdded value: +{ + "type": "boolean" +} - added
Input schema / properties / limitAdded value: +{ + "type": "number" +}
33 tool updates
v0.6.2- First observed
back - First observed
batch - First observed
chrome_status - First observed
click - First observed
download_file - First observed
eval - First observed
extract_links - First observed
fill_form - First observed
forward - First observed
get_cookies - First observed
get_html - First observed
get_text - First observed
hover - First observed
navigate - First observed
press - First observed
profile_use - First observed
read_as_markdown - First observed
reload - First observed
screenshot - First observed
scroll - First observed
select_option - First observed
snapshot - First observed
storage - First observed
tab_close - First observed
tab_new - First observed
tab_select - First observed
tabs_list - First observed
task_new - First observed
task_status - First observed
tasks_list - First observed
type - First observed
upload_file - First observed
wait_for
TDQS
Scored across 20 tools
Most tools map to a distinct resource/action, and prefixes like task_, profile_, and get_ reduce confusion. The page-reading tools (read_as_markdown, get_text, get_html, snapshot) are close but their output formats are clearly differentiated, so only minor ambiguity remains.
Names are uniformly snake_case and often resource-prefixed, but conventions vary: task_new uses an adjective, tasks_list puts the verb last, and standalone names like storage, screenshot, and batch break the pattern. The set is readable but does not follow one consistent verb_noun convention.
At 20 tools, the server is on the heavy end of the typical range. Each tool has a plausible purpose and browser automation is broad, but the count feels more like a full feature list than a tightly scoped set.
The toolset strongly covers observation and capture: text, HTML, screenshots, PDFs, cookies, frames, tabs, and task/profile state. However, it lacks fundamental browser-driving operations like navigation, tab creation/activation, and element interaction, which is a significant gap for an automation-oriented server.
Maintenance
Related MCP Connectors
Real Chrome for agents: start a browser, read pages as numbered markdown, click, type, hand off.
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Hyperbrowser MCP — wraps the Hyperbrowser AI-agent browsing API
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to control the Google Chrome browser through a Node.js WebSocket bridge and a dedicated browser extension. It provides tools for capturing screenshots, executing JavaScript, managing tabs, and extracting page content via the MCP protocol.2-
- FlicenseNot gradedqualityDmaintenanceEnables browser automation (navigate, screenshot, click, type, etc.) for Claude Code via MCP protocol, with a Chrome extension for configuration.2-
- AlicenseNot gradedqualityDmaintenanceEnables browser automation through the Claude Chrome Extension, allowing agents to navigate websites, fill forms, take screenshots, and debug web apps via standard MCP protocols.1MIT
- AlicenseNot gradedqualityBmaintenanceChrome extension + MCP bridge that gives Claude control over your real browser via CDP, enabling navigation, clicking, typing, scrolling, screenshots, and JS execution with a visible cursor and tab-bring-to-front.1MIT