Skip to main content
Glama

Public Browser

GitHub Release npm version Tool definitions < 5k tokens License: MIT Node >= 20

Lets Claude Code, Cursor and any MCP client drive Chrome. In a blind benchmark on a 30-test page, five runs each, Public Browser 3.0 passed 30/30 in every run and used a median of 3.0M session tokens where agent-browser 0.38.1 used 4.5M — a third fewer tokens, a third less cost, a quarter fewer tool calls and 48% faster (261 s against 386 s) (Benchmarks, including where it loses). Measured 2026-09-23/24 with Claude Code 2.1.281, driver Claude Opus 5 and Chrome 153; agent-browser ran through its CLI with its official skill file, and the test page is our own. Direct CDP, a11y-tree refs, several steps per call with run_plan — 2,700+ TypeScript tests, 280+ Python tests.

Built for Claude Code, Cursor, and any MCP-compatible client — and, without an LLM in the loop, for decision models like Jev.

Looking for an alternative to agent-browser, Playwright MCP, Chrome DevTools MCP or Browser MCP? Public Browser is an MCP server that talks to Chrome directly over the DevTools Protocol — no Playwright dependency, no extension bridge, no shell command per step. One command to install, zero config. See the benchmark comparison below.

Why Public Browser?

  • Fewer tokens per task. Every tool call makes the model re-read the conversation so far, so the session total is what you pay for. On the benchmark page Public Browser 3.0 needed 3.0M tokens (median of five runs) where agent-browser needed 4.5M. In the field run a day earlier, with Public Browser still at 2.10.6, Playwright MCP needed 7.8M and Chrome DevTools MCP 10.3M. The lead comes from fewer, denser steps: run_plan executes several actions with variables and conditions in one call, and since 3.0 the responses carry less repetition — diffs show only what changed, text the parent line already shows is not repeated, a tip appears once per session.

  • Loud failures instead of silent ones. A CSS selector that matches several elements does nothing and returns the candidates with their refs. Refs are kept per tab and never reused, so a ref from a page you left reports stale ref instead of clicking whatever node now has that number. drag answers Drag not confirmed when nothing reacted, and a click that opens a tab names the new tab.

  • Nested cross-origin iframes and shadow DOM. Clicks reach elements in a cross-origin iframe that sits inside another cross-origin iframe — agent-browser 0.38.1 reads one level (#1784). Open and closed shadow roots are read as well.

  • Two ways in without an LLM. A Node library (createSession()) and a Python client (pip install publicbrowser) run the same tool handlers as the MCP server.

What agent-browser does better: it can copy your Chrome profile so its logins come along (Public Browser's profile mode does not carry site logins on macOS, see Chrome Profiles), records HAR files and intercepts requests, saves PDFs and video, and drives iOS Safari. If your agent works from the shell rather than through an MCP client, it is a strong choice.

Blind benchmark, median of 5 runs each

Public Browser 3.0

agent-browser 0.38.1 (CLI)

Passed (30 scored tests)

30/30 in 5 of 5 runs

29/30 in 5 of 5 runs — misses T5.2, a navigator.webdriver check

Session tokens, whole run

3.02M (2.47–3.15M)

4.53M (4.40–5.33M)

Cost per run, Opus 5 list price

$2.40

$3.56

Tool calls

79

104

Time to finish, wall clock

261 s

386 s

Tool-response volume

78.9k chars

84.4k chars

Average tool response

1,040 chars

754 chars

2026-09-23/24, Claude Code 2.1.281, driver claude-opus-5, Chrome 153.0.8010.53. Method, per-run table and the rest of the field: Benchmarks.

Related MCP server: chrome-devtools-slim

Quick Start

Install in Claude Code

One command — installs globally for all projects:

claude mcp add --scope user public-browser -- npx -y public-browser@latest

Important: after claude mcp add you must fully quit and reopen Claude Code. /mcp reconnect is not enough — Claude Code reads the mcpServers config only at session start and caches it. After the restart, the first tool call auto-launches Chrome visible (no headless, no port setup). Done.

To enable parallel Python Script API access, add --script to the args: claude mcp add --scope user public-browser -- npx -y public-browser@latest -- --script

Install in Cursor

Add to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "public-browser": {
      "command": "npx",
      "args": ["-y", "public-browser@latest"]
    }
  }
}

For parallel Python Script API access, use "args": ["-y", "public-browser@latest", "--", "--script"]

Install in Cline

Add to your cline_mcp_settings.json:

{
  "mcpServers": {
    "public-browser": {
      "command": "npx",
      "args": ["-y", "public-browser@latest"]
    }
  }
}

Install in other MCP clients

Any client that supports stdio MCP servers: npx -y public-browser@latest with no arguments.

Try it — your first prompt

After installing, paste this into your AI coding assistant:

Open mcp-test.second-truth.com, read the page, and fill the contact form with Name "Test User" and Email "test@example.com".

This exercises three core tools in sequence: navigate loads the page, view_page reads the accessibility tree with stable element refs, and fill_form fills multiple fields in one call. You should see Chrome open, the page load, and the form filled — all without writing a single line of code.

Uninstall

claude mcp remove --scope user public-browser

Chrome Profiles

By default, Public Browser starts Chrome with a fresh temp profile — no cookies, no logins, no extensions. You can also start Chrome with one of your own Chrome profiles.

List available profiles

npx public-browser profiles

Launch with a profile

Three ways — pick whichever fits your setup:

# CLI flag
npx public-browser --profile "Work"

# Environment variable
PUBLIC_BROWSER_PROFILE="Work" npx public-browser

# MCP tool (call BEFORE any browser interaction)
configure_session({ profile: "Work" })

Chrome refuses remote control on its default data directory, so Public Browser creates a lightweight wrapper directory with a symlink to your profile folder and starts Chrome on that. The wrapper is removed when Public Browser closes Chrome, and wrappers left behind by a crash are removed on the next start; your profile folder itself is never deleted.

What carries over, and what does not. Bookmarks, history, extensions and Chrome's own Google sign-in come along, so Google sites are signed in. Other sites are not, at least on macOS: current Chrome (tested with 153) does not load the profile's cookies through the symlink — the sandbox of Chrome's network service only allows paths below the wrapper — so sites start logged out, and logins made during the session are not saved to your profile. Linux and Windows are untested. Copying the profile at start, as agent-browser does, is planned.

No open debugging port. A real profile is driven over --remote-debugging-pipe: CDP runs through a pipe that only Public Browser holds, and nothing listens on a TCP port — other programs on your machine cannot take over your browser. The flip side: --attach and the Script API escape hatch (page.cdp) do not work with a real profile. Should Chrome ever refuse the pipe, Public Browser restarts it with a random debugging port (never 9222) and says so on stderr and once in the next tool response: while that Chrome runs, the profile is reachable for local programs.

If Chrome is already open

Public Browser detects this via lock-file inspection. If Chrome is running with remote debugging enabled, it attaches via CDP. If not, it shows a clear error asking you to close Chrome first. A profile can be open in only one Public Browser at a time: a second instance stops with an error naming the PID of the Chrome that holds it.

Perfect for Jev — a decision model needs a menu, Public Browser hands it one

Jev (TypeSafe AI, announced 15 September 2026, early access) is not a chat model. It takes program state plus a bounded set of options and returns one typed choice with calibrated probabilities in 70–500 ms, at $0.042 per million input tokens with free output — it cannot produce free text, so it cannot invent a selector that does not exist. TypeSafe calls this a "System One model". Two browser agents already run on it: browser-use/jev-ultrafast (Google Flights search in 7.1 s, $0.0039, 91% fewer browser-protocol calls) and jev-browser (1.5× faster and 1.6× cheaper than Playwright MCP on a 12-task suite, 97% autonomous success at ~$0.0005 per task).

Every one of those loops needs the same three things from the browser side, and they are exactly what Public Browser is built around:

Jev needs

Public Browser delivers

A bounded menu of actions, not a screenshot or a raw DOM

view_page (filter: "interactive") — the a11y-tree elements an agent can act on, each with a stable e-ref. Ø 1.2–1.3k chars per view in the September benchmark, well inside Jev's ~32k-token page budget and 255-option choice cap.

Refs that survive the action so the chosen option can be executed and verified

e-refs are cached across calls and survive scrolls and DOM re-renders; click/type/fill_form return a DOM diff (NEW/REMOVED/CHANGED) that serves as the deterministic verification signal Jev-style loops use instead of a second model call.

A programmatic driver without an LLM in the loop

The Script API (Python) over HTTP and the Node Library API in-process — same tool handlers as the MCP server, one Chrome per Jev worker, headless or with one of your Chrome profiles.

Measured, not claimed. examples/jev-loop.mjs is that loop in ~150 lines on the Node Library: view_page on the test card → one Jev choice over the card's refs (plus a boolean "already done?") → click / type / fill_form → repeat. Jev cannot write text, so when it picks a "type" action, gpt-4.1-nano writes the literal value for that one field — the same split browser-use/jev-ultrafast uses. Run on the six Level-1 cards of the public benchmark page, two runs, 2026-09-18, Jev via Vercel AI Gateway, headless Chrome:

Run 1

Run 2

Cards passed

6/6

6/6

Steps = Jev calls (one decision per step)

23

21

Text-model calls (form fields, secret code, table sum)

8

7

Wall-clock, all six cards

20.2 s

16.4 s

Cost, all six cards (Jev $0.042/M in, nano $0.10/M in, $0.40/M out)

$0.0012

$0.0011

Per card that is ~3 s and ~$0.0002. The same six cards inside the LLM-driven September runs above (Opus 5 over MCP, 30 cards in 281–296 s for $3.35–3.41) come to roughly 9–10 s and $0.11 per card — a different setup (a frontier model reads the whole page and plans; Jev only picks from a menu), so read it as "what the cheap path costs", not as a benchmark of equals. Level 1 is the easy tier; whether a Jev-only loop survives Level 2–4 (observe, shadow DOM, canvas, races) is the open question, and the harness for asking it is in the repo. Raw data: test-hardest/results/jev-loop-run1.json, run2. Setup: npm i ai @ai-sdk/openai public-browser, AI_GATEWAY_API_KEY + OPENAI_API_KEY, node examples/jev-loop.mjs.

Script API (Python) — perfect for Jev loops

A second way to use Public Browser — deterministic browser automation from Python, without an LLM in the loop. Scripts use the same tool implementations as the MCP server (Shared Core) — every improvement to click, navigate, fill_form etc. automatically benefits your scripts too. The MCP server handles AI-driven workflows; the Script API is for repeatable scripts you write yourself.

How fast that is without an LLM: a scripted run of the 24-test version of the benchmark suite finished the whole suite in 21 seconds (type: mcp-scripted, 2026-04-04). That number says what deterministic scripting costs, not how Public Browser compares to other MCP servers — every cross-server comparison in Benchmarks is LLM-driven on both sides.

Installation

pip install publicbrowser

Or, from a source checkout, install the local package:

python -m pip install ./python

Chrome.connect() auto-starts the Public Browser server as a subprocess via a local public-browser binary or the npx fallback — no manual Chrome launch or port setup needed.

Legacy single-file alternative: For quick prototyping you can copy python/publicbrowser_standalone.py into your project. This uses v1 direct CDP and does not benefit from server-side improvements — use the local publicbrowser package for the full Shared Core experience.

How it works

Python Script                        Escape Hatch (Power User)
    |                                    |
    v                                    v
HTTP POST /tool/{name}              WebSocket (CDP)
Port 9223                           Port 9222
    |                                    |
    v                                    |
Public Browser Server                    |
    |                                    |
    v                                    |
registry.executeTool()                   |
    |                                    |
    v                                    |
Tool Handler                             |
(click.ts, navigate.ts, ...)             |
    |                                    |
    v                                    v
Chrome <------------ CDP --------------->

Your script sends HTTP requests to the Public Browser server on port 9223. The server executes the exact same tool handlers that the MCP server uses — one codebase, one test suite (2,700+ tests), two access paths.

Auto-Start

Chrome.connect() finds and starts the server automatically:

  1. Running server — asks GET /health on port 9223 and connects only if a Public Browser server answers and accepts the key; any other program on that port is reported, never used

  2. PATH binary — finds public-browser in PATH, starts it with --script

  3. npx fallback — runs npx -y public-browser@latest -- --script

  4. Explicit path — Chrome.connect(server_path="/path/to/public-browser") for custom setups

Access key

The Script API only answers requests that carry its key (Authorization: Bearer <key>), so web pages and programs running under another user account cannot drive your browser through it. Programs running under your own user account can read the key file, just as they can read your browser profile — the key does not protect against them. You rarely see the key:

  • When Chrome.connect() starts the server itself, it generates a key and hands it over in the PUBLIC_BROWSER_SCRIPT_TOKEN environment variable.

  • A server started with --script (for example from your MCP config) generates its own key and writes it to ~/.public-browser/script-api-<port>.token, readable only by your user. Chrome.connect() reads it from there.

  • To use a key of your own, set PUBLIC_BROWSER_SCRIPT_TOKEN for both sides or pass Chrome.connect(token=...).

Two scripts that call Chrome.connect() at the same moment while no server runs each start a server with their own key. One of them gets the port, the other gets a PermissionError. Connect once and open one page per task from that connection (chrome.new_page() can be called from several threads), or start the server beforehand with public-browser --script, so that every script reads the same key file.

Requests without the key get 401. Requests from a browser (with an Origin header) or with a Host other than 127.0.0.1:<port> / localhost:<port> get 403 — that blocks web pages and DNS rebinding even if they guess the port.

Upgrading: the server and the publicbrowser Python client go together: publicbrowser 2.0.0 needs Public Browser 3.0.0 or newer, and publicbrowser 1.0.0 does not work with 3.0.0 — it does not send the key, so it reports ConnectionError: Public Browser server not reachable although the server runs. An MCP config with npx -y public-browser@latest -- --script picks up the new server on its next start — update the client at the same time (pip install -U publicbrowser).

Example: Login + Data Extraction

from publicbrowser import Chrome

chrome = Chrome.connect()

with chrome.new_page() as page:
    page.navigate("https://shop.example.com/login")
    page.fill({"#email": "me@example.com", "#password": "***"})
    page.click("button[type=submit]")
    page.wait_for("text=Dashboard")

    for cat in ["electronics", "furniture", "toys"]:
        page.navigate(f"https://shop.example.com/orders/{cat}")
        rows = page.evaluate(
            "[...document.querySelectorAll('tr')].map(r => r.textContent)"
        )
        save_csv(cat, rows)

chrome.close()

Methods

Method

Description

Chrome.connect()

Connect to or auto-start the Public Browser server

chrome.new_page()

Context manager — opens a new tab, auto-closes on exit

page.navigate(url)

Navigate and wait for load

page.click(selector)

Click by CSS selector (must match exactly one element), visible text ("text=Sign in") or ref ("e12")

page.type(selector, text)

Type text into an input

page.fill({"sel": "val"})

Fill multiple form fields at once

page.wait_for(condition)

Wait for page text ("text=..."), a ref, a CSS selector (#, ., [), "network_idle" or a JS condition

page.evaluate(expression)

Run JavaScript, return result

page.download()

Wait for pending downloads, return the download report (JSON or a notice)

page.close()

Close the tab (auto-called by context manager)

page.cdp.send(method, params)

Escape Hatch — direct CDP access via WebSocket (see below)

Escape Hatch: Direct CDP Access

For use cases the high-level API doesn't cover — network interception, console log subscriptions, performance tracing, cookie management — you can drop down to raw CDP commands:

with chrome.new_page() as page:
    page.navigate("https://example.com")

    # Enable network tracking
    page.cdp.send("Network.enable")

    # Get all cookies
    cookies = page.cdp.send("Network.getAllCookies")

    # Performance tracing
    page.cdp.send("Tracing.start", {"categories": "-*,devtools.timeline"})

The Escape Hatch communicates directly with Chrome via WebSocket (port 9222), bypassing the server. It connects lazily on the first send() call and reuses the connection for subsequent calls. Each page gets its own WebSocket routed to the correct tab. It needs Chrome's debugging port, so it is not available when the server drives a real profile (--profile), which runs without one: /session/create then returns cdp_ws_url: null plus a cdp_ws_note, and page.cdp raises RuntimeError.

MCP Coexistence

When the MCP server and Python scripts need to run at the same time, add --script to the MCP config. Chrome.connect() handles the rest automatically — each script works in its own tab, MCP tabs are never touched.

Enabling --script in MCP Config

Claude Code:

claude mcp add --scope user public-browser -- npx -y public-browser@latest -- --script

Cursor / Cline (mcp.json):

{
  "mcpServers": {
    "public-browser": {
      "command": "npx",
      "args": ["-y", "public-browser@latest", "--", "--script"]
    }
  }
}

See python/README.md for the full API reference and advanced examples.

Node Library API (multiple instances in one process) — perfect for Jev

The MCP server and the Python Script API both drive exactly one Chrome per process. When you need several browsers at once — say a read-only research browser and a separate action browser per agent — spawning one npx public-browser per instance costs 4–6 s of start-up each. createSession() runs the same session inside your own Node process instead:

import { createSession } from "public-browser";

const research = await createSession({
  cdpUrl: "http://127.0.0.1:9333",          // or cdpPort: 9333
  userDataDir: "/var/agents/a1/research",   // created if missing
  headless: true,
  stealth: false,                            // stay identifiable — see below
  downloadDir: "/var/agents/a1/quarantine",  // never deleted by us
  downloadHash: true,                        // adds sha256 to every download
  downloadNaming: "suggested",               // real filenames, not GUIDs
  cortexDir: "/var/agents/a1/cortex",        // per-instance pattern store
  inheritEnv: ["HTTPS_PROXY"],               // opt in — see Environment below
});

const action = await createSession({ cdpPort: 9334, userDataDir: "/var/agents/a1/action" });

await research.callTool("navigate", { url: "https://example.com" });
const page = await research.callTool("view_page", {});

await research.close();
await action.close();

callTool(name, params) takes the same tool names and parameters as the MCP tools (navigate, view_page, click, type, fill_form, run_plan, download, ...) and routes through the identical handlers (Shared Core).

Isolation. Each session runs in its own worker thread by default, so the module-level caches (element refs, selector cache, viewport state, stealth flag, cortex matcher) exist once per session rather than once per process — two sessions can never hand each other stale element refs.

Measured on macOS with isolation: "process", attaching to a Chrome started outside Public Browser (a worker thread saves ~40 ms):

Median

createSession() launches its own headless Chrome

~0.9 s

attach to a running Chrome, up to the first tool response

~0.7 s

...through to a real page navigated and read

~1.8 s

Most of the attach cost is Chrome starting a renderer for the tab Public Browser opens for itself — an attached session never takes over tabs that belong to someone else.

A thread is not a security boundary: same process memory, same file descriptors. isolation: "process" forks one OS process per session instead — separate heap, separate descriptors, separate crash domain — for integrators whose trust model draws the line there. isolation: "inline" skips isolation altogether and is only correct when the thread runs exactly one session.

isolation

Boundary

Startup

Use when

"worker" (default)

thread — private module caches

~1 s

several sessions in one trusted process

"process"

OS process — private memory + descriptors

~1 s

the sessions must not share a process with the host

"inline"

none — the calling thread

fastest

exactly one session per thread

No listening CDP port (transport: "pipe"). By default Chrome is launched with --remote-debugging-port, which is what makes --attach, the Script API and reconnect-after-crash possible — and which also means every other process on the machine can drive that browser. For a session holding real logins that is a way around any permission check you perform yourself.

const action = await createSession({
  transport: "pipe",                      // no --remote-debugging-port at all
  userDataDir: "/var/agents/a1/action",
  headless: true,
});

CDP then travels over the child's stdio pipe, which only Public Browser holds: lsof shows nothing listening and a second process finds no way in. The price is everything the port paid for — no reconnect after a Chrome crash, no second client and no attach; combining "pipe" with attach fails at createSession() rather than at the first tool call. A named profile always runs over the pipe, whatever transport says — "pipe" only forbids the random-port fallback Public Browser would otherwise use if Chrome refused the pipe. session.transport reports the actual connection, and session.cdpPort is undefined when nothing listens — reporting the default would name whatever Chrome the user has open on 9222.

Environment. A session does not start from the host environment. It starts from a documented minimum and you widen it deliberately — an orchestrator holding cloud credentials, API keys and tokens should not hand them to a browser session just because the two share a process tree.

What a session always gets is ESSENTIAL_ENV_VARS: PATH, HOME, the temp dir, CHROME_PATH, locale/timezone, the Linux display variables and the Windows process basics. Everything else is opt-in:

// PATH/HOME/CHROME_PATH plus the proxy — and nothing else from the host.
await createSession({ inheritEnv: ["HTTPS_PROXY", "NO_PROXY"] });

// Full inheritance, the pre-2.8 behaviour.
await createSession({ inheritEnv: true });

Proxy variables are deliberately not essential: a proxy URL can carry credentials, so it is allowlisted on purpose rather than inherited by accident.

On top of that, a session never inherits Public Browser's own SILBERCUE_* / PUBLIC_BROWSER_* configuration variables — in any inheritEnv mode. Each of them has an option here, and a host-level variable, usually set for the host's own Chrome, silently redirecting a configured session is a bug, not a feature: with SILBERCUE_CHROME_HOST=10.9.9.9 in the orchestrator's environment, a session created with cdpPort: 9450 still talks to 127.0.0.1:9450. Use env to set one back deliberately.

Shutdown. close() resolves only once Chrome is actually gone — SIGTERM, SIGKILL after 5 s — so the port and the user-data-dir are free for the next launch instead of racing a process that was merely asked to exit.

One session per Chrome. Some CDP settings are browser-wide rather than per-session, Browser.setDownloadBehavior among them: two sessions attached to the same Chrome share one download directory, and whichever connected last wins.

This fails silently and it corrupts the record: the losing session keeps reporting paths under its downloadDir, but the file was written to the other one. path then points at nothing, with no error to notice. Give each session its own Chrome — its own port (or transport: "pipe") and its own user-data-dir — whenever downloadDir matters.

Option

Default

Description

cdpUrl

—

http://host:port, host:port or a bare port. Wins over cdpPort/cdpHost

cdpPort / cdpHost

9222 / 127.0.0.1

CDP endpoint this session drives. session.cdpPort is undefined when nothing listens (transport: "pipe", or a named profile)

userDataDir

—

Chrome --user-data-dir for auto-launch. One directory per instance

profile

—

Named Chrome profile instead of a raw directory. Runs over the pipe — no CDP port. On macOS, site logins do not carry over (Chrome Profiles)

headless

false

Launch Chrome headless

stealth

true

false disables all navigator.webdriver masking

attach

false

Never auto-launch; attach to a running Chrome and fail fast if there is none

downloadDir

temp dir

Where downloads land. A directory you supply is never deleted

downloadHash

false

Report sha256 for every completed download

downloadNaming

"guid"

"suggested" renames finished files to the server-supplied name

cortexDir

~/.public-browser/cortex

Per-instance cortex store

transport

"port"

"pipe" launches Chrome with no listening CDP port (no attach/reconnect)

inheritEnv

false

Essentials only. Array = essentials + allowlist, true = whole host env

env

—

Extra environment variables for the session, applied last

isolation

"worker"

"process" for an OS-process boundary, "inline" for none

eager

false

Launch/attach during createSession() instead of on the first call

startupTimeoutMs

30000

Budget for the session thread/process to report ready

Multiple instances via the CLI

The same thing without a Node host — one process per Chrome, each on its own port:

public-browser --port 9333 --profile research --download-dir /q/research
public-browser --port 9334 --profile action   --download-dir /q/action

With --profile Chrome runs over the pipe and --port stays unused; only if Chrome refused the pipe would it get a random port, with a warning.

--profile <name> uses one of your real Chrome profiles. For a throwaway per-agent Chrome, point at a raw directory instead — it is created if missing:

public-browser --port 9335 --user-data-dir /var/agents/a3/chrome

--attach connects to an already-running Chrome on the configured port instead of launching one. SILBERCUE_CHROME_PORT and SILBERCUE_SCRIPT_PORT are the environment equivalents of --port and --script-port and are part of the stable public contract.

Identifiable automation (--no-stealth)

By default Public Browser masks navigator.webdriver (it reports undefined) and launches Chrome with --disable-blink-features=AutomationControlled. That is the right default for consumer automation, but the wrong one when your integration must be transparently identifiable as a bot — compliance-driven crawling, internal agent fleets, or sites whose terms require honest signalling.

Turn the masking off completely:

public-browser --no-stealth
# or
SILBERCUE_STEALTH=0 npx public-browser
await createSession({ stealth: false });

With stealth off, navigator.webdriver stays true and keeps its native getter (Object.getOwnPropertyDescriptor(Navigator.prototype, "webdriver").get still reports [native code]) — permanently, across navigations and tab switches, with no post-correction needed on your side. No masking script is injected at any point and the launch flag is omitted.

Downloads

Downloads land in a per-session temp directory that is removed on shutdown. Point them at a directory of your own — a quarantine dir, a shared volume — with --download-dir / PUBLIC_BROWSER_DOWNLOAD_DIR / downloadDir. A directory you supply is created if missing and never deleted by Public Browser.

With --download-hash (or downloadHash: true) every completed download also carries a sha256, so the download tool returns path, size and digest:

{"filename":"report.pdf","path":"/q/research/A1B2...","size":48213,"sizeKb":48,
 "url":"https://example.com/report.pdf","sha256":"9f86d081884c7d659a2f..."}

Filenames. Chrome writes downloads under their internal GUID, so the file on disk is called A1B2... and only the filename field carries the real name. That is fine when you read the JSON, and useless when something else has to walk the directory. --download-naming suggested (or downloadNaming: "suggested", PUBLIC_BROWSER_DOWNLOAD_NAMING=suggested) renames each finished file to the server-supplied name:

{"filename":"report.pdf","path":"/q/research/report.pdf","size":48213,"sizeKb":48,
 "url":"https://example.com/report.pdf","sha256":"9f86d081884c7d659a2f..."}

The name is sanitised before it touches the disk — basename only, no control characters, never hidden, length-capped — and a collision gets a -1, -2, ... suffix rather than overwriting an existing file. filename always reports the name the file actually has, so join(downloadDir, filename) equals path. If the rename fails, the GUID path and the raw server name are kept and reported; a download is never lost to a naming problem.

Timing. action: "status" waits up to 250 ms for a download to start before reporting that there is none, because Chrome fires downloadWillBegin a few milliseconds after the click that triggers it — without the window, the first call after a click misses a file that is already on its way. Adjust it per call with settle ({"action":"status","settle":0} for an instant check, 5000 for a slow server). Once a download has started, status waits for it to finish, bounded by timeout.

For polling loops use action: "list" — it returns the full session history immediately and never waits, for either a start or a completion.

Tool Overview

Tool

Description

Reading & Observation

view_page

A11y-tree with stable e-refs — primary way to understand the page. filter: "interactive" (default) returns the elements an agent can act on; filter: "all" adds headings, paragraphs and other static text.

capture_image

WebP screenshot, max 800px, <100KB. For visual verification only — refs come from view_page.

console_logs

Browser console output with level/pattern filters

network_monitor

Start/stop/query network requests with filtering

observe

Watch DOM changes: collect (buffer over time) or until (wait for condition, then auto-click)

wait_for

Wait for element visible, page text, URL, network idle, or JS expression. assert: true checks once and fails with a typed code instead of waiting

tab_status

Active tab's cached URL/title/ready/errors (0ms)

virtual_desk

Lists all tabs with stable IDs. Call first in every session.

dom_snapshot

Bounding boxes, computed styles, paint order. For spatial questions view_page cannot answer.

Interaction

click

Real CDP mouse events by ref, selector, text, or coordinates. The answer names the element it hit (Clicked [e12] button "Save"). The DOM diff (NEW/REMOVED/CHANGED) arrives with the next page action, or in this one with wait_for_diff: true.

type

Type into an input by ref/selector

fill_form

Fill a complete form in one call — text, <select>, checkbox, radio. Per-field status.

press_key

Real CDP keyboard events — Enter, Escape, Tab, arrows, shortcuts (Ctrl+K, etc.)

scroll

Scroll page, element into view, or inside a specific container

file_upload

Upload file(s) to <input type="file">

handle_dialog

Configure alert/confirm/prompt handling before triggering actions

drag

Native CDP drag & drop between elements

download

Wait for pending downloads or list downloaded session files

Navigation

navigate

Load a URL. First call per session auto-redirected to virtual_desk to prevent overwriting the user's tab.

switch_tab

Open, switch to, or close tabs by ID from virtual_desk

Scripting

run_plan

Multi-step batch execution with variables, conditions, saveAs, error strategies, suspend/resume.

configure_session

View/set session defaults (tab, timeout) and accept auto-promote suggestions

batch_evaluate

Visit multiple URLs sequentially and run the same JavaScript expression on each page.

set_page_data

Write large payloads to window.__pb_data[key] via server-side chunking for data that is too large for a single CDP message.

evaluate

Execute JS in page context. Anti-pattern scanner warns on querySelector/.click().

Selectors are strict. Where a tool takes a CSS selector (click, type, fill_form, press_key, scroll, drag, file_upload, observe), it has to match exactly one element in the page's main document. With several matches the call does nothing and returns up to five candidates with their refs — use one of the refs or a narrower selector.

Why an MCP server and not a CLI?

Several browser-automation projects ship a CLI and tell coding agents to call it from the shell — agent-browser and Playwright CLI among them. A CLI adds no tool definitions to the context, and in the field run on 2026-09-23 both CLIs were ahead of Public Browser 2.10.6 on session tokens: agent-browser 4.22M and Playwright CLI 4.61M against 4.89M (medians of three runs). That result is what 3.0 was built to answer. Against agent-browser, 3.0 now needs a third fewer tokens (3.02M against 4.53M, five runs each); Playwright CLI was not re-run.

It gets there while still paying for an MCP surface. Its 25 tool definitions take about 4,837 tokens of context as delivered over the wire (characters / 4 of the tools/list response, npm run token-count; a test keeps them under 4,990, so they cannot creep back up). agent-browser's skill file costs about 900 tokens by the same measure — Public Browser pays more up front and wins it back through fewer, denser steps. That is what run_plan is for: N steps in one call, executed server-side with variables, conditions and suspend/resume, where agent-browser's batch takes a flat list of commands and leaves the control flow to the model. In the five 3.0 runs the model used run_plan 28–43 times per run.

Whether models are more fluent with an MCP tool surface or with a CLI's --help output is an open question. One practitioner's side-by-side of Chrome DevTools MCP and the agent-browser CLI found the MCP surface better and the models "do not seem deeply fluent with it yet" (Pasi Huuhka, 28 Jan 2026) — one comparison, not a study.

Coming from Browser MCP?

Browser MCP (@browsermcp/mcp) has had no release since 0.1.3 on 11 April 2025, and its extension bridge works on one tab. If you picked it for its four promises, here is where Public Browser stands on each: Fast — talks to Chrome directly over CDP, no extension bridge, no cloud hop; Private — runs on your machine, no telemetry; Logged In — only partly: one of your Chrome profiles brings bookmarks, extensions and Chrome's own Google sign-in, but on macOS other sites start logged out (see Chrome Profiles); Stealth — not in the bot-evasion sense: navigator.webdriver is not true by default and clicks are real CDP mouse events, but serious bot detection still sees an automated browser, and --no-stealth makes it identifiable on purpose. Install with one command (Quick Start). Tool names differ: browser_snapshot → view_page, browser_click → click, browser_type → type; view_page returns the refs that click and type take. Multi-tab works.

Benchmarks

Four data sets, all measured on our own page https://mcp-test.second-truth.com — 35 tests, 30 scored (T5.3–T5.6 can only be started by the page's own runner; T4.7 grades a self-reported token count and is dropped for everyone): Public Browser 3.0 against agent-browser on 2026-09-24 (current), the whole field on 2026-09-23, Public Browser 2.10.1 against Playwright MCP on 2026-09-03, and the April 2026 runs kept for history. Compare rows only inside one data set — except the two September sets, which share harness, page, model and Chrome one day apart (see Public Browser 3.0 against the 2026-09-23 field). The page source is in test-hardest/page/index.html; every run records the page hash (suite.html_sha256 = 81e4b7aa…bed2 for all September runs, the hash of that file) and the test IDs. Raw run JSONs and the full method: test-hardest/README.md.

Every September run is one fresh blind Claude Code session in print mode with driver model claude-opus-5 and an identical prompt. An MCP participant is the only MCP server of its session, with the built-in tools cut down to Write; a CLI participant may use Bash only for its own command (a PreToolUse hook, test-hardest/cli-guard.mjs, blocks everything else) and gets the tool's official skill file as extra system prompt. Everything is counted post-hoc from the session transcript — nothing is self-reported by the participants. Session tokens are input + output + cache writes + cache reads, each API message counted once (tokens.dedup: "message.id"); cost is the Opus 5 list price.

2026-09-24 (current): Public Browser 3.0 vs agent-browser 0.38.1

Claude Code 2.1.281, Chrome 153.0.8010.53, five scored runs per side. agent-browser ran through its CLI; its MCP mode was not measured. Output of node test-hardest/blind-run.mjs compare over the ten runs:

MCP

Version

Model

Date

Run

Status

Passed

Duration

Rounds

Tokens

MCP calls

Response total

Ø response

P95

Snapshot tool Ø

agent-browser

0.38.1

claude-opus-5

2026-09-23

agent-browser-run4

ok

29/30

333s

107

4.40M

104

76k

732

1982

2131 (2×)

agent-browser

0.38.1

claude-opus-5

2026-09-23

agent-browser-run6

ok

29/30

624s

123

5.33M

121

85k

702

2036

388 (19×)

agent-browser

0.38.1

claude-opus-5

2026-09-23

agent-browser-run7

ok

29/30

303s

101

4.48M

99

84k

852

4041

2281 (1×)

agent-browser

0.38.1

claude-opus-5

2026-09-23

agent-browser-run8

ok

29/30

349s

104

4.53M

102

85k

837

2733

2139 (2×)

agent-browser

0.38.1

claude-opus-5

2026-09-23

agent-browser-run10

ok

29/30

372s

112

4.97M

110

83k

754

2506

2018 (5×)

Public Browser

2.10.6

claude-opus-5

2026-09-24

public-browser-run18

ok

30/30

223s

68

2.47M

66

69k

1040

5597

4150 (3×)

Public Browser

2.10.6

claude-opus-5

2026-09-24

public-browser-run19

ok

30/30

245s

83

3.15M

81

79k

973

5049

1344 (11×)

Public Browser

2.10.6

claude-opus-5

2026-09-24

public-browser-run20

ok

30/30

223s

83

3.03M

81

117k

1445

5602

1909 (12×)

Public Browser

2.10.6

claude-opus-5

2026-09-24

public-browser-run21

ok

30/30

235s

81

2.99M

79

69k

867

5372

2855 (7×)

Public Browser

2.10.6

claude-opus-5

2026-09-24

public-browser-run22

ok

30/30

223s

76

3.02M

74

129k

1748

5866

2590 (11×)

Medians: 3.02M against 4.53M session tokens (−33%), $2.40 against $3.56 (−33%), 79 against 104 tool calls (−24%), 261 s against 386 s wall clock (−32% time, which is 48% faster: 386 / 261 = 1.48; 223 s against 349 s on the page's own timer, the Duration column). The Public Browser rows say 2.10.6 because they ran against the local build at commit 366c194 before the version bump (test-hardest/results-local/, acceptance report acceptance-stage2-366c194.json); that commit's code is what ships as 3.0.0 — later commits changed documentation, help texts, metadata and release tooling, nothing on the benchmark path. Two more agent-browser runs (agent-browser-run5, run9) were aborted by the harness because the session used a tool outside the allowlist (Read) and are not counted; both had 29/30.

Where Public Browser loses. Its single responses are larger: Ø 1,040 chars against 754 and P95 5,597 against 2,506 (medians). It pays more context up front — tool definitions and handshake instructions against a skill file (both are inside the session totals). The pass-rate gap is T5.2 alone, a navigator.webdriver check rather than a browser capability. And agent-browser has features Public Browser lacks (see Why Public Browser?).

Before and after 3.0. Public Browser 2.10.6, measured the same evening under the same conditions, came to 4.31M tokens (median of five, 30/30 each) against agent-browser's 4.53M (baseline-2026-09-aufschliessen.json) — a near tie, and in the morning series below agent-browser was ahead. The 3.0 changes (loud errors, shorter responses) moved Public Browser to 3.02M. A probe on real sites (Hacker News, Wikipedia, a demo shop; Public Browser only, two runs per task) passed every task before and after the changes (real-sites-probe.mjs).

Public Browser 3.0 against the 2026-09-23 field

The 2026-09-24 and 2026-09-23 data sets ran one day apart under the same conditions: same harness, same page (suite.html_sha256 = 81e4b7aa…bed2), same driver model claude-opus-5, same Chrome 153.0.8010.53; only Claude Code moved from 2.1.280 (2026-09-23) to 2.1.281 (2026-09-24). The agent-browser row of the 2026-09-24 comparison already comes from runs made on 2026-09-23. The table sets the Public Browser 3.0 medians (30/30 ×5, 79 tool calls, 3.02M session tokens, $2.40, 261 s) against each participant's medians; a negative number means Public Browser 3.0 needs less. For agent-browser it uses the five-run medians from 2026-09-24, not the three-run row of the field table.

Participant

Version

Passed

Tool calls

Session tokens

Cost

Wall clock

agent-browser

0.38.1

29/30 ×5 (T5.2)

104 (−24%)

4.53M (−33%)

$3.56 (−33%)

386 s (−32%)

Playwright CLI

0.1.21

30/30 ×3

107 (−26%)

4.61M (−34%)

$3.52 (−32%)

454 s (−43%)

Playwright MCP

0.0.82

30/30 ×3

162 (−51%)

7.84M (−61%)

$5.22 (−54%)

494 s (−47%)

Chrome DevTools MCP

1.9.0

29/30 ×3 (T5.2)

169 (−53%)

10.33M (−71%)

$6.78 (−65%)

535 s (−51%)

browser-use

0.13.10

24/30, 26/30

384 (−79%)

63.68M (−95%)

$36.06 (−93%)

2,187 s (−88%)

Limits: Public Browser 3.0 has five runs, the other participants three (browser-use two), and none of them was measured again after 2026-09-23. Run files: Public Browser 3.0 in test-hardest/results-local/ (public-browser-run18–22), everything else in test-hardest/results/ as listed in the two sections around this one.

2026-09-23: the whole field (Public Browser 2.10.6)

Claude Code 2.1.280, Chrome 153.0.8010.53, three runs per participant (two for browser-use), medians:

Participant

Version

Via

Passed

Session tokens

Cost

Tool calls

Wall clock

Public Browser

2.10.6

MCP

30/30 ×3

4.89M

$3.75

90

323 s

agent-browser

0.38.1

CLI

29/30 ×3 (T5.2)

4.22M

$3.39

93

385 s

Playwright CLI

0.1.21

CLI

30/30 ×3

4.61M

$3.52

107

454 s

Playwright MCP

0.0.82

MCP

30/30 ×3

7.84M

$5.22

162

494 s

Chrome DevTools MCP

1.9.0

MCP

29/30 ×3 (T5.2)

10.33M

$6.78

169

535 s

browser-use

0.13.10

MCP

24/30, 26/30

63.68M

$36.06

384

2,187 s

The two CLIs were ahead of Public Browser 2.10.6 on tokens — that is what 3.0 set out to change. Playwright CLI, Playwright MCP, Chrome DevTools MCP and browser-use were not re-run against 3.0. browser-use missed T3.3, T3.6 and T4.4 in both runs and T4.2 in one. Run files in test-hardest/results/: public-browser-run3–5, agent-browser-run1–3, playwright-cli-run2–4, playwright-mcp-run7–9, chrome-devtools-mcp-run5–7, browser-use-run7–8.

2026-09-03: Public Browser 2.10.1 vs Playwright MCP 0.0.80

Claude Code 2.1.259, two runs each for Public Browser 2.10.1, Playwright MCP 0.0.80 and Chrome DevTools MCP 1.8.0, one run for browser-use 0.12.5. Output of node test-hardest/blind-run.mjs compare over these seven runs:

MCP

Version

Model

Date

Run

Status

Passed

Duration

Rounds

Tokens

MCP calls

Response total

Ø response

P95

Snapshot tool Ø

browser-use

0.12.5

claude-opus-5

2026-09-03

browser-use-run6

ok

24/30

2023s

278

42.46M

276

15800k

57244

321033

102819 (18×)

Chrome DevTools MCP

1.8.0

claude-opus-5

2026-09-03

chrome-devtools-mcp-run3

ok

29/30

547s

158

9.67M

156

149k

954

5676

4718 (12×)

Chrome DevTools MCP

1.8.0

claude-opus-5

2026-09-03

chrome-devtools-mcp-run4

ok

29/30

558s

174

9.71M

172

120k

696

5271

3593 (14×)

Playwright MCP

0.0.80

claude-opus-5

2026-09-03

playwright-mcp-run5

ok

30/30

468s

139

6.20M

137

101k

740

3617

1911 (17×)

Playwright MCP

0.0.80

claude-opus-5

2026-09-03

playwright-mcp-run6

ok

30/30

493s

153

7.03M

151

99k

656

1587

2269 (14×)

Public Browser

2.10.1

claude-opus-5

2026-09-03

public-browser-run1

ok

30/30

281s

85

4.53M

84

109k

1298

6077

2841 (16×)

Public Browser

2.10.1

claude-opus-5

2026-09-03

public-browser-run2

ok

30/30

296s

88

4.49M

86

104k

1214

6479

3398 (16×)

Public Browser needed 84 and 86 tool calls where Playwright MCP needed 137 and 151 and Chrome DevTools MCP 156 and 172, and it finished the page in 281 s and 296 s against 468/493 s and 547/558 s (page timer). Session tokens were 4.53M and 4.49M against 6.20M and 7.03M for Playwright MCP (−32%), cost $3.41 and $3.35 against $4.28 and $4.78 (−25%), at 30/30 in all four runs. Playwright MCP returned the smaller responses (Ø 740 and 656 chars against 1,298 and 1,214). Earlier versions of this README quoted 6.3M/6.5M against 8.8M/9.6M tokens for these runs: that count added a message's usage once per content block; the recount per API message changed the totals, not the ratio (−30% before, −32% now). Chrome DevTools MCP's only miss was T5.2; browser-use-run6 is incomplete (two tests never started).

Measured on the same page against the 35-test version of the suite (April 2026) — 5 levels (Basics, Intermediate, Advanced, Hardest, Community Pain Points). Four of the 35 tests are runner-only and are excluded from every score, so all pass rates in this section are out of 31 scorable tests. An extended 42-test version exists locally and is not yet published; the numbers here are not measured against it. Driver model was Claude Opus 4.6 and competitor versions were not recorded. Each run is independent, values on the benchmark page are randomized per page-load, all runs started in a fresh Claude Code session out of /tmp (no project context bias), and all metrics measured post-hoc from the session JSONL via test-hardest/measure-tool-calls.sh — no self-reporting, no MCP-side instrumentation, just counting tool_use blocks and tool_result char lengths.

April 2026 data, 24- and 35-test suites, superseded by the September 2026 rerun above.

Head-to-Head (24-test suite, April 2026 — historical suite version)

All rows LLM-driven by Claude Opus 4.6 on the same test page, one recorded run each. Public Browser ran 2026-04-05, the other servers 2026-04-02. This is the older 24-test version of the suite — do not compare these rows against the 31-scorable-test numbers below.

MCP Server

Tests Passed

Duration

Tool Calls

Speed vs PB

Public Browser

24/24

350s

71

--

Playwright MCP

24/24

570s

138

1.6x slower

browser-use skill

24/24

725s

117

2.1x slower

claude-in-chrome

24/24

772s

193

2.2x slower

browser-use

16/24

1813s

124

5.2x slower

In this one April 2026 run each (24-test suite, Opus 4.6), Public Browser needed 71 tool calls where Playwright MCP needed 138 — roughly half the roundtrips for the same 24 passes. The September 2026 runs above are the current figures. Raw data: test-hardest/benchmark-*.json (Public Browser row: benchmark-silbercuechrome_mcp-llm-2026-04-05.json, type: llm-driven).

Pass Rate + Duration (31 scorable tests, LLM-driven)

Every row is one recorded run; the run id is named so each number is traceable to a single run JSON in test-hardest/results/. No averaging across runs.

MCP

Passed

Duration

Run

Public Browser

30/31 (97%)

598s

Run 5

Playwright MCP

29/31 (94%)

563s

Run 2

Playwright CLI

28/31 (90%)

376s

Run 1

Chrome DevTools MCP (Google)

27/31 (87%)

535s

Run 2

browser-use

21/31 (68%)

1870s

Run 5

Browser MCP (browsermcp)

6/31 (19%)

294s, aborted

Run 1

claude-in-chrome

24-test data only, not re-benched

—

—

Servers with several recorded runs, so you can see the spread rather than only the row above: Playwright MCP ranges 29–30/31 across three runs (Runs 2–4), its best being 30/31 in 449s (Run 3); Chrome DevTools MCP ranges 27–29/31, its best 29/31 in 518s (Run 1). Run 2 is quoted for both because that is the run the tool-efficiency analysis below instruments end to end. On pass rate this field is effectively a tie — the durable difference is response size, and that holds across every Playwright run measured (avg 1,216–1,467 chars in Runs 2–4).

Tool-Efficiency (the fair metric)

We measure each tool call's response char length directly, group by tool name, estimate tokens via chars/4. Why this metric: in these April runs, session-level token deltas were dominated by LLM overhead (system prompt + CLAUDE.md + conversation history = ~80-90% of the budget) and only showed 5-15% differences between MCPs — untrustworthy for comparing browser servers. Tool-response size is the part the MCP server actually controls. (The September 2026 runs are different: they are blind sessions out of /tmp with no CLAUDE.md and 170–304 fresh input tokens per run, so their session totals are comparable and are quoted above.)

Public Browser Run 5 vs Playwright MCP Run 2 — the same two runs as the pass-rate table above.

Metric

Public Browser

Playwright MCP

Difference

Tool calls (MCP-only)

151

121

+25% (PB uses more, smaller calls)

Avg Response size

807 Chars

1,448 Chars

PB 1.8x smaller

Avg Response tokens est.

201

362

PB 1.8x smaller

P95 Response

2,328 Chars

8,068 Chars

PB 3.5x smaller

Total response content

128k Chars

175k Chars

PB 27% less

Per-Tool Breakdown (where the difference comes from)

Tool

Public Browser Avg

Playwright MCP Avg

Verdict

view_page¹ / browser_snapshot

1,124 Chars (21 calls)

6,084 Chars (8 calls)

PB 5.4x more compact per call

evaluate / browser_evaluate

510 Chars (33 calls)

2,155 Chars (47 calls)

PB 4.2x more compact per call

type / browser_type

88 Chars (13 calls)

147 Chars (13 calls)

PB 1.7x more compact

click / browser_click

1,278 Chars (63 calls)

463 Chars (44 calls)

Playwright 2.8x leaner — but see trade-off below

¹ recorded as read_page in the April 2026 runs; the tool was renamed to view_page afterwards.

The Ambient-Context trade-off

Ambient Context — Claude sees DOM changes for free, no extra view_page needed

Public Browser's click is 2.8x larger than Playwright's because every click response embeds the DOM diff (NEW/REMOVED/CHANGED lines). Playwright returns a bare confirmation, so the LLM typically follows up with a browser_snapshot or browser_evaluate to see what happened. Over a full benchmark run, Playwright MCP spends 47 browser_evaluate calls averaging 2,155 chars against Public Browser's 33 at 510 chars. Public Browser delivers the diff inline. Net result: PB's click+read_page+evaluate total is 120k chars vs Playwright MCP's 170k — 30% less response content overall.

April 2026, Opus 4.6: view_page was 5.4x more compact than Playwright MCP's browser_snapshot (superseded — against Playwright MCP 0.0.80 in September 2026 it is not)

Measured on the 35-test benchmark (2026-04-09): Public Browser's view_page averages 1,124 chars per call vs Playwright MCP's browser_snapshot at 6,084 chars. Same page, same test suite, same LLM driver. The a11y-tree compression + Ambient Context pipeline meant we only sent what the agent actually needed — smaller responses, less context pressure, cheaper runs. That was the April 2026 picture. Against Playwright MCP 0.0.80 it no longer holds — that release made the snapshot format much more compact, and in the September runs browser_snapshot averages 1,911 and 2,269 chars against view_page at 2,841 and 3,398; see September 2026 (current) above.

See test-hardest/README.md for the full protocol, per-test breakdown, and raw JSON runs with tool_efficiency blocks.

Cortex — Local Tool-Sequence Hints

Public Browser includes a small learning layer called Cortex. It writes down which tool sequences succeeded on which kind of page and, when the agent later lands on the same kind of page, adds one line to the navigate and view_page response with the most likely next tools once the top one reaches P ≥ 0.9, e.g. Cortex (login): next → fill_form (P=0.92), click (P=0.08). No ML model, no training step, no network access.

How it works

  1. Page Classification — Every page is classified by its accessibility tree into one of 16 functional types: login, signup, mfa, search_form, search_results, data_table, form_simple, form_wizard, article, navigation, dashboard, settings, media, checkout, profile, error (or unknown). The classifier is rule-based (ARIA roles, landmarks, keyword signals) — no domains or URLs are involved.

  2. Pattern Recording — A sequence that starts with navigate and continues with successful tool calls (2–20 calls within 60 seconds, e.g. navigate → view_page → fill_form → click on a login page) is stored in ~/.public-browser/cortex/patterns.jsonl, with a Merkle hash tree over the entries (tree-head.json) for integrity checks. Only the most recent sequence per page type is kept, so the file holds at most one line per page type. Only page type, tool names, a content hash, and a timestamp are stored — no URLs, no page content, no PII.

  3. Markov Predictions — The stored sequences are turned into a first-order Markov table that models P(next_tool | last_tool, page_type); its top predictions make up the hint line. Stale entries decay (0.95/week) and are removed after 30 days.

  4. Starter table — A hand-written transition table (community-markov.json) ships with the package so that a fresh install gets hints before anything has been recorded. Despite the file name it contains no collected usage data. The table is SHA-256 verified at load time and merged with local patterns (local data takes precedence).

Privacy by design

The Cortex stores only structural metadata, and only on your machine — page types (not domains), tool names (not arguments), and content hashes (not content). A login pattern reveals nothing about which login page was visited. Nothing is uploaded.

Local friction log (developer opt-in)

For development of Public Browser itself there is a second, fully local opt-in: SILBERCUE_CHROME_FRICTION_LOG=1 makes the server count tool calls, tool errors and detected fallback spirals per run in ~/.silbercue-chrome/friction-queue.json. It records counters, timestamps and the working directory — never page content, URLs or user input — and nothing ever leaves the machine. Without the variable the code path is not entered at all: no file, no counters, no hints.

Architecture

Public Browser (Node.js MCP server, public-browser)
+-- @modelcontextprotocol/sdk (stdio transport)
+-- CDP Client
|   +-- WebSocket transport (existing Chrome on :9222)
|   +-- Pipe transport (auto-launched Chrome with --remote-debugging-pipe)
+-- Auto-Launch: Chrome + optimal flags, visible by default
+-- A11y-tree cache + Selector cache
+-- Session Manager (OOPIF support for iframes and Shadow DOM)
+-- Tab State Cache (URL/title/ready across tabs)
+-- Cortex (local tool-sequence hints)
|   +-- Page Classifier (16 page types from a11y-tree)
|   +-- Pattern Recorder + Merkle Log (local persistence)
|   +-- Markov Table (transition predictions)
|   +-- Starter Table (hand-written, shipped, SHA-256 verified)
|   +-- Hint Matcher (delivers predictions to tool responses)
+-- Script API (Python, `pip install publicbrowser`)
|   +-- Shared Core via HTTP (:9223) — same tool handlers as MCP
|   +-- Escape Hatch via WebSocket (:9222) — direct CDP for power users
+-- 25 tools
    Reading - Interaction - Navigation - Scripting - Observation

Connection priority:

  1. Auto-Launch (default, zero-config) — starts Chrome as a child process via --remote-debugging-pipe, visible as a window, with all flags set for reliable screenshots and keyboard focus.

  2. WebSocket (optional) — if you already run Chrome with --remote-debugging-port=9222, Public Browser connects to that instead. Use this to drive a Chrome you started yourself with its own --user-data-dir (Chrome refuses remote debugging on its default profile directory).

Requirements

  • Node.js >= 20

  • Google Chrome, Chromium, or any Chromium-based browser (auto-detected on macOS/Linux/Windows; override with CHROME_PATH)

Environment Variables

Variable

Values

Default

Description

SILBERCUE_CHROME_AUTO_LAUNCH

true / false

true

Auto-launch Chrome if no running instance found

SILBERCUE_CHROME_HEADLESS

true / false

false

Opt-in headless mode for CI/server environments

SILBERCUE_CHROME_PORT

1–65535

9222

CDP debugging port. Non-default values spawn an isolated Chrome instance (separate --user-data-dir) that won't conflict with the user's browser. Alias: PUBLIC_BROWSER_CHROME_PORT

SILBERCUE_CHROME_HOST

host

127.0.0.1

CDP host. Alias: PUBLIC_BROWSER_CHROME_HOST

SILBERCUE_SCRIPT_PORT

1–65535

9223

Script API port (needs --script). Alias: PUBLIC_BROWSER_SCRIPT_PORT

PUBLIC_BROWSER_SCRIPT_TOKEN

string

— (random)

Script API key. Unset: a server started with --script generates one and writes it to ~/.public-browser/script-api-<port>.token (mode 0600)

SILBERCUE_STEALTH

0 / 1

1

0 disables the navigator.webdriver masking. Alias: PUBLIC_BROWSER_STEALTH

PUBLIC_BROWSER_DOWNLOAD_DIR

path

— (temp dir)

Directory downloads are written to. Created if missing, never deleted

PUBLIC_BROWSER_DOWNLOAD_HASH

1 / true

— (off)

Report a sha256 for every completed download

PUBLIC_BROWSER_DOWNLOAD_NAMING

guid / suggested

guid

suggested renames finished downloads to the server-supplied filename

PUBLIC_BROWSER_CORTEX_DIR

path

~/.public-browser/cortex

Per-instance cortex pattern store

SILBERCUE_CHROME_PROFILE

path

—

Chrome user profile directory (auto-launch only). Alias: PUBLIC_BROWSER_PROFILE (profile name)

CHROME_PATH

path

—

Path to Chrome binary (overrides auto-detection)

Invalid values fail loudly: an unparseable port or naming mode aborts startup with a named error instead of silently falling back to 9222. Sessions created through the Node library ignore every variable in this table except CHROME_PATH — see Node Library API.

License

MIT licensed — see LICENSE. Use it however you want, commercially or otherwise.

Contributing

Issues and pull requests welcome at github.com/Silbercue/public-browser.

Privacy

Public Browser runs entirely on your machine. All browser automation happens locally via CDP. The Cortex learning layer stores only structural metadata locally (page types, tool names, content hashes — no URLs, no domains, no page content, no PII). There is no telemetry upload; the Cortex data never leaves your machine.

When Chrome runs visibly, a small Public Browser bar sits in the page for the person watching (it is aria-hidden, so the agent never sees it). From the fifth tool call on, it shows a link for 20 seconds every 10 minutes — a GitHub star or a Jev hint. Click or close it once and it never comes back (remembered in ~/.public-browser/nudge.json); headless sessions never show it.

Building for iOS too? SilbercueSwift is the same idea for the iOS Simulator.

Available Tools

25 tools
batch_evaluateA

Visit several URLs in sequence and run the same JS expression on each. For controlled checks across known pages where view_page/run_plan would be too chatty; not for normal reading, clicking or form work.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlsYesURLs to visit in order
settle_msNoWait in ms after each page load before evaluating
continue_on_errorNoContinue with the remaining URLs if one fails
evaluate_per_pageYesJS expression evaluated on each page
timeout_per_page_msNoTimeout per page in ms (navigate+settle+evaluate)

TDQS

A4.2/5.0
Behavior3/5

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

With no annotations present, the description carries the full burden of behavioral disclosure. It does reveal the core behavioral pattern (sequential navigation, uniform JS expression per page), but it does not disclose that arbitrary JS execution can have side effects or mutate page state, nor what the tool returns for each URL. For a tool that runs JavaScript across multiple pages, that safety-relevant gap is notable.

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

Conciseness5/5

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

Two sentences with zero filler: the first fronts the action (what the tool does), the second delivers routing guidance (when and when-not). Every clause earns its place.

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

Completeness4/5

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

For a moderate-complexity tool with fully documented parameters, the description covers purpose and usage completely. The remaining gaps are the return format (no output schema exists to fill it) and unstated side-effect potential of JS evaluation (no annotations exist either). These are meaningful but do not prevent an agent from invoking the tool correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all five parameters. The description reinforces evaluate_per_page's semantics ('the same JS expression on each') but adds no new parameter-level detail beyond the schema. Baseline 3 is appropriate since the schema does the heavy lifting.

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

Purpose5/5

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

The description states a specific verb-resource-action combination: 'Visit several URLs in sequence and run the same JS expression on each.' It also explicitly distances itself from siblings by naming view_page/run_plan and stating what it is not for ('normal reading, clicking or form work'), making it impossible to confuse with the other browsing tools.

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

Usage Guidelines5/5

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

The description gives an explicit when-to-use condition ('controlled checks across known pages'), names the alternatives it replaces ('where view_page/run_plan would be too chatty'), and lists exclusions ('not for normal reading, clicking or form work'). An agent can make a correct routing decision with no additional inference.

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

capture_imageA

Pixel-level screenshot (WebP, max 800px, <100KB). Only for canvas or chart content without DOM text, pixel-level rendering or animation checks, or when the user explicitly asks for a screenshot. Returns no refs and reads no text — that is view_page.

ParametersJSON Schema
NameRequiredDescriptionDefault
somNoOverlay numbered labels matching view_page refs (Set-of-Mark)
full_pageNoCapture the full scrollable page, not just the viewport

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations, the description carries full behavioral disclosure. It states that the tool returns no refs and reads no text, and gives output constraints (WebP, 800px, <100KB). It does not explicitly say 'does not modify the page' but that is strongly implied by the screenshot nature; the disclosed constraints are sufficient for an agent to set expectations.

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

Conciseness5/5

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

The description is two sentences, front-loaded with the core definition and constraints, followed by usage guidance and sibling differentiation. Every word earns its place; no redundancy or fluff.

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

Completeness4/5

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

For a simple tool with two optional boolean parameters and no output schema, the description covers purpose, constraints, use cases, and key behavioral differences from view_page. A minor gap is that it doesn't explicitly describe the returned image data format, but the WebP and size details imply the output adequately.

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

Parameters3/5

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

Schema coverage is 100%: both 'som' and 'full_page' have clear descriptions in the schema. The tool description does not add extra parameter-level detail, but since the schema already documents both parameters adequately, the baseline score of 3 is appropriate.

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

Purpose5/5

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

The description opens with a precise definition: 'Pixel-level screenshot' with concrete format and size constraints (WebP, max 800px, <100KB). It further distinguishes itself from view_page by stating that it returns no refs and reads no text, making the tool's purpose unambiguous and clearly separated from its sibling.

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

Usage Guidelines5/5

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

Usage is explicitly scoped: 'Only for canvas or chart content without DOM text, pixel-level rendering or animation checks, or when the user explicitly asks for a screenshot.' It also names the alternative (view_page) for cases involving refs/text, giving the agent clear routing guidance.

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

clickA

Click an element by ref, CSS selector, visible text, or viewport x/y (canvas, pixel-precise targets). Dispatches real CDP mouse events. A click that opens a new tab is reported in the response. The DOM diff (NEW/REMOVED/CHANGED) arrives with the next page action, or here with wait_for_diff: true.

ParametersJSON Schema
NameRequiredDescriptionDefault
xNoViewport X in px; with y, not ref/selector
yNoViewport Y in px; use with x
refNoElement ref (preferred)
textNoVisible text (a11y name); no view_page needed, prefers interactive
selectorNoCSS selector (fallback)
wait_for_diffNoWait for the DOM diff before returning (default false, slower)

TDQS

A4.1/5.0
Behavior4/5

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

With no annotations, the description carries the full behavioral burden. It discloses multiple non-obvious traits: clicks dispatch 'real CDP mouse events' (not synthetic), new-tab openings are reported in the response, and the DOM diff arrives either with the next page action or immediately if wait_for_diff is true. This is substantial transparency, though it omits potential error conditions or permission requirements.

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

Conciseness4/5

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

The description is two sentences with no filler. The first sentence packs the core action and targeting modes, the second covers side effects and diff timing. It is appropriately dense and front-loaded with the main purpose, though the second sentence is long and could be split for even clearer scannability.

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

Completeness4/5

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

For a 6-parameter tool with no output schema and no annotations, the description covers the essential operational context: how to target, what events are dispatched, what happens on new-tab opens, and when DOM diffs appear. It does not describe the exact response payload for a normal click, but the mention of new-tab reporting partially addresses return behavior. Overall, an agent has enough to invoke it correctly in most scenarios.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3. The description adds value beyond the schema by explaining that viewport x/y are for 'canvas, pixel-precise targets' and that the 'text' targeting mode 'prefers interactive' elements and requires no prior view_page call. This enrichment helps an agent choose the correct targeting parameter without opening the schema.

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

Purpose5/5

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

The description states a specific verb ('Click') and resource ('an element'), and enumerates four distinct targeting modes (ref, CSS selector, visible text, viewport x/y). It differentiates itself from siblings like 'drag' and 'press_key' by mentioning 'real CDP mouse events' and 'canvas, pixel-precise targets', leaving no ambiguity about what the tool does.

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

Usage Guidelines3/5

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

The description clearly implies when to use the tool – whenever a click action is needed – and gives targeting options, but it never explicitly names alternatives or states conditions for choosing click over a sibling like 'press_key' or 'drag'. It provides no when-not-to-use guidance, so usage is implied rather than explicit.

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

configure_sessionA

View or set session defaults for recurring parameters (tab, timeout, …). Without params: show current defaults and auto-promote suggestions; autoPromote: true applies them. Set profile BEFORE any browser interaction to launch Chrome with a named profile.

ParametersJSON Schema
NameRequiredDescriptionDefault
profileNoChrome profile name (list them with: public-browser profiles); restart: true switches mid-session
restartNoRestart Chrome with the new profile even if running; closes all tabs
defaultsNoParam name → default value; null removes a default
autoPromoteNoApply all current auto-promote suggestions

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations, the description carries the full behavioral disclosure burden. It reveals the dual view/set behavior, what autoPromote actually does, and the important ordering constraint that profile changes should happen before browser interactions. It does not discuss persistence or side effects beyond what the schema already documents, but the description provides meaningful behavioral context rather than just restating the tool name.

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

Conciseness5/5

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

The description is only two sentences, yet it communicates the core operation, the no-parameter behavior, the autoPromote effect, and the profile timing constraint. Every clause earns its place with no filler or repetition.

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

Completeness4/5

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

Given the tool's moderate complexity, four optional parameters, and absence of an output schema, the description covers the essential cases: viewing defaults, applying suggestions, and setting a profile before browser interaction. It could be more explicit about what the returned defaults look like or how the defaults object relates to recurring parameters, but it is sufficient for an agent to invoke the tool correctly in the main scenarios.

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

Parameters4/5

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

The schema already documents all four parameters with 100% coverage, so the baseline is 3. The description adds extra meaning by explaining the zero-parameter call behavior and by specifying that profile must be set before browser interaction, which is not conveyed by the schema's parameter descriptions alone.

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

Purpose5/5

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

The description opens with a specific verb-resource pair, 'View or set session defaults,' and clarifies that it handles recurring parameters like tab and timeout. This distinguishes it clearly from the sibling browser-interaction tools, which act on pages or tabs rather than session-level configuration.

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

Usage Guidelines4/5

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

It gives concrete usage conditions: calling with no parameters shows current defaults and auto-promote suggestions, setting autoPromote to true applies them, and profile must be set before any browser interaction. It does not explicitly name alternatives or state when not to use the tool, so it doesn't earn a 5, but the guidance is clear and actionable.

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

console_logsA

Read collected browser console logs; filter by level (info/warning/error/debug) and/or regex; optionally clear the buffer after reading.

ParametersJSON Schema
NameRequiredDescriptionDefault
clearNoClear the buffer after reading
levelNoFilter by log level
patternNoRegex matched against log text

TDQS

A4.4/5.0
Behavior4/5

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

No annotations are present, so the description must carry the full disclosure burden. It does so by explicitly labeling the operation as 'Read' and prominently disclosing the optional destructive side effect of clearing the buffer. It could add more detail about buffer source or retention, but the key side effect is clearly exposed.

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

Conciseness5/5

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

A single well-structured sentence packs the main action, the filtering options, and the optional side effect without wordiness. It is front-loaded with the most important information and every phrase earns its place.

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

Completeness4/5

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

The tool has only three optional parameters, no output schema, and no annotations, so the description adequately covers purpose, filters, and the clear flag. Minor gaps around return format and where the logs come from remain, but they are not critical for a tool this simple.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3. The description goes beyond the schema by explaining that 'level' and 'pattern' can be used together as filters ('and/or regex') and that 'clear' is an optional post-read action, which clarifies parameter relationships and intent.

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

Purpose5/5

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

States a specific verb ('Read') and resource ('collected browser console logs'), then adds filter behavior and an optional clearing side effect. This clearly distinguishes it from the other browser automation siblings by emphasizing a read-only diagnostic purpose.

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

Usage Guidelines4/5

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

The purpose is obvious: read logs, apply filters, optionally clear. It doesn't explicitly name alternatives or exclusion conditions, but the context is sufficient because this tool serves a unique diagnostic function among the sibling browser tools.

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

dom_snapshotA

Structured layout data: bounding boxes, computed styles, paint order, colors; refs match view_page. Only for spatial questions view_page cannot answer (is A above B? what color?).

ParametersJSON Schema
NameRequiredDescriptionDefault
refNoElement ref for a subtree snapshot

TDQS

A4.6/5.0
Behavior4/5

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

No annotations are present, so the description carries full responsibility. It discloses the kind of data returned and the scope of use, and the data-only framing implies a read-only operation. It doesn't explicitly state that no page mutation occurs or what happens when ref is omitted, but it is reasonably transparent for a snapshot-introspection tool.

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

Conciseness5/5

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

Two tightly packed sentences: the first lists the data categories and the cross-tool ref relationship; the second states the exclusive usage condition. No filler, no repetition, and key information is front-loaded.

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

Completeness4/5

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

For a one-parameter tool with no output schema, the description covers the nature of return data, the ref linkage to view_page, and the intended use case. A small gap is the unspecified default scope when ref is omitted, but the schema's 'subtree snapshot' partially addresses that. Overall, sufficient for correct invocation.

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

Parameters4/5

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

The schema already documents the only parameter (ref) at 100% coverage, so baseline is 3. The description adds genuinely useful context by noting 'refs match view_page', telling the agent that refs are shared with the sibling tool and clarifying how to populate the parameter. This exceeds the schema alone.

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

Purpose5/5

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

The description clearly states the tool's resource and function: it provides structured layout data including bounding boxes, computed styles, paint order, and colors. It explicitly distinguishes itself from view_page by naming the exact spatial questions it answers, so an agent can differentiate it from siblings without inspecting schemas.

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

Usage Guidelines5/5

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

It explicitly says the tool is 'only for spatial questions view_page cannot answer' and provides concrete examples ('is A above B? what color?'). This is a clear when-to-use instruction and also implies when to prefer view_page, leaving nothing to inference.

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

downloadA

Wait for pending downloads or list this session's downloaded files. Downloads start by themselves when you click a download link or open a file URL (PDF, CSV, …); this tool never triggers them. download() waits for a pending download to finish; download({ action: 'list' }) returns at once.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionNostatus waits for pending downloads; list: history, never waits (use for polling)status
settleNoMs to wait for a download to START before reporting none — Chrome fires downloadWillBegin a few ms after the click; 0 = instant
timeoutNoMax wait in ms for pending downloads

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations, the description carries the full burden. It clearly discloses that the tool never triggers downloads and that the default call waits while list returns immediately. It could additionally describe what happens when no download is pending or what the return payload looks like, but the schema partially covers timeouts and settling.

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

Conciseness5/5

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

Three sentences with no filler: the first states the core purpose, the second explains the triggering mechanism, and the third shows the behavioral difference between modes. The most important information is front-loaded.

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

Completeness4/5

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

The tool has no output schema and no annotations, but the description plus the rich parameter schema gives the agent enough to call it correctly. It explains what the tool does, when to use it, and which mode to choose for waiting vs. listing, though an explicit note about the return format would make it fully complete.

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

Parameters3/5

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

Schema description coverage is 100%, with detailed descriptions for action, settle, and timeout, including defaults and enum meanings. The description adds a useful call-form example but not semantic information beyond what the schema already provides, so baseline 3 is appropriate.

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

Purpose5/5

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

The description states a specific verb and resource: 'Wait for pending downloads or list this session's downloaded files.' It also clearly distinguishes itself from triggering tools by saying 'this tool never triggers them,' which prevents confusion with click/navigate/file actions.

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

Usage Guidelines5/5

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

It explicitly tells the agent when to use the tool: after a download link is clicked or a file URL is opened, and explains that downloads are triggered by user/browser actions, not by this tool. It also distinguishes the two modes: status waits, list returns immediately for polling.

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

dragA

Drag a source onto a target: HTML5 drag-and-drop (draggable lists, SortableJS, React DnD) and mouse drags (sliders, canvas, resize handles). Scrolls the source into view; says when no page reaction was detected. Source: from_ref/from_selector or from_x+from_y; target: to_ref/to_selector or to_x+to_y.

ParametersJSON Schema
NameRequiredDescriptionDefault
to_xNoTarget viewport X in px (with to_y)
to_yNoTarget viewport Y in px
stepsNoNative mouseMoved events between press and release; minimum 5
from_xNoSource viewport X in px (with from_y)
from_yNoSource viewport Y in px
to_refNoTarget element ref
from_refNoSource element ref
to_selectorNoTarget CSS selector
from_selectorNoSource CSS selector

TDQS

A4.2/5.0
Behavior4/5

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

The description discloses important behavioral traits: it scrolls the source into view and reports when no page reaction was detected. It also explains the two drag mechanisms (HTML5 vs mouse drags), which is valuable context beyond the schema. With no annotations provided, the description carries the burden and does a good job, though it could mention whether the operation is reversible or what side effects occur.

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

Conciseness5/5

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

The description is compact and information-dense. It front-loads the core action, then covers the two drag types, the scroll behavior, the no-reaction feedback, and the parameter grouping in just two sentences. Every sentence earns its place.

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

Completeness4/5

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

For a tool with 9 optional parameters and no output schema, the description covers the key decision points: how to specify source/target, what drag types are supported, and what feedback to expect. It doesn't explain the return value or error cases, but the absence of an output schema and the clear parameter grouping make the description largely sufficient.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all 9 parameters. The description adds the grouping of source (from_ref/from_selector or from_x+from_y) and target (to_ref/to_selector or to_x+to_y), which helps an agent understand the mutually exclusive alternatives. However, it doesn't add detail beyond that grouping, so a baseline 3 is appropriate.

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

Purpose5/5

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

The description clearly states the tool performs drag operations, enumerates the two supported drag types (HTML5 drag-and-drop and mouse drags), and gives concrete examples of each. It also distinguishes the source and target specification methods, making the tool's purpose unambiguous.

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

Usage Guidelines4/5

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

The description provides clear context on when to use the tool (for draggable lists, sliders, canvas, resize handles) and explains the source/target specification options. It doesn't explicitly name alternative tools or state when not to use it, but the context is strong enough for an agent to select it appropriately among siblings.

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

evaluateA

Run JavaScript in the page. Good uses: computation, style mutations (.style.X = ..., classList), shadow-root traversal, in-page fetch(), app-specific side effects no dedicated tool covers. Scope is shared between calls; top-level const/let/class are wrapped in an IIFE. If/else blocks may return undefined — use a ternary or an explicit return.

ParametersJSON Schema
NameRequiredDescriptionDefault
expressionYesJavaScript code to execute in the page context
await_promiseNoWhether to await Promise results

TDQS

A4.2/5.0
Behavior4/5

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

With no annotations, the description carries the full behavioral burden, and it delivers: it warns that scope is shared between calls, top-level declarations are wrapped in an IIFE, and if/else blocks may return undefined, recommending ternaries or explicit returns. This is meaningful execution behavior an agent needs to predict results. It omits error handling and return-format details, but the provided traps are valuable.

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

Conciseness5/5

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

Three compact sentences carry high signal: the primary purpose, a curated list of good uses, and the critical behavioral caveats. Every sentence adds value, and the most important facts are front-loaded. It is appropriately concise for the complexity involved.

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

Completeness4/5

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

For a generic JS-evaluation tool with two simple parameters and no output schema, the description covers purpose, usage scope, and key execution quirks. Missing are default return behavior and error semantics, but the given detail is enough for most agent decisions. The tool is not so complex that every nuance needs explanation.

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

Parameters3/5

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

Schema description coverage is 100%: both 'expression' and 'await_promise' have descriptive schema text. The description adds no parameter-level information, so it earns the baseline score of 3. No gaps in parameter understanding remain because the schema already explains them.

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

Purpose5/5

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

The description begins with a clear verb and resource: 'Run JavaScript in the page.' It then enumerates specific good-use categories (computation, style mutations, shadow-root traversal, in-page fetch, app-specific side effects) that distinguish it from sibling tools like click, type, and dom_snapshot. It also signals it is the fallback when 'no dedicated tool covers' a case, which helps an agent choose it correctly.

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

Usage Guidelines4/5

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

The description gives explicit use cases and implies dedicated tools take precedence ('no dedicated tool covers'). It does not explicitly list which sibling to use instead for common actions, but the provided good uses create a clear boundary. The guidance is usable and context-rich, though it could be stronger by naming alternatives.

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

file_uploadB

Upload file(s) to an identified by ref or CSS selector; file paths must be absolute.

ParametersJSON Schema
NameRequiredDescriptionDefault
refNoElement ref of the file input
pathYesAbsolute path(s) of the file(s) to upload
selectorNoCSS selector of the file input; needed for hidden inputs (display:none, off-screen), which have no ref

TDQS

B3.3/5.0
Behavior2/5

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

There are no annotations, so the description carries the full burden of behavioral disclosure. It only restates the upload action and the absolute-path requirement, which is already in the schema. It does not disclose side effects, event behavior, handling of hidden inputs, or what happens on failure.

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

Conciseness5/5

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

A single well-structured sentence that front-loads the action and target, then adds the critical path constraint. No wasted words.

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

Completeness3/5

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

The schema fully documents all three parameters, so a valid call can be constructed. However, with no annotations and no output schema, the description does not clarify behavioral expectations, error conditions, or when selector is needed versus ref, leaving some contextual gaps for an agent.

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

Parameters3/5

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

Schema description coverage is 100%, so the baseline is 3. The description adds little beyond the schema: it mentions ref/CSS selector and absolute paths, both of which are already documented in the parameter descriptions.

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

Purpose4/5

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

The description states a specific verb ('Upload') and a clear resource ('<input type="file">'), and identifies targeting via ref or CSS selector. It is clear and distinct from siblings like click/type, but it does not explicitly name an alternative or contrast itself with fill_form, so it stops short of a top-tier differentiation.

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

Usage Guidelines3/5

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

The usage is implied: use this tool when uploading files to a file input. The absolute-path requirement is a useful prerequisite, but there is no explicit guidance about when to prefer this over fill_form/type or any exclusion criteria.

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

fill_formA

Fill a whole form in one call: text inputs, (by value or visible label), checkboxes (boolean) and radio buttons. One round-trip; partial errors do not abort and each field reports its own status. On per-field errors call view_page and retry the failing fields.

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldsYesEach needs ref or selector plus value

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations, the description carries the full burden, and it delivers meaningful behavioral details: one round-trip, partial errors do not abort, each field reports its own status, and select values can be supplied by value or visible label. These traits go beyond a generic 'fill form' description, though side effects or clearing behavior are not addressed.

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

Conciseness5/5

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

The description is three tight sentences with no filler. The scope and supported input types are front-loaded, followed by behavioral guarantees and the error-retry instruction. Every clause earns its place.

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

Completeness4/5

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

For a single-parameter tool with no annotations and no output schema, the description gives enough to invoke the tool correctly and recover from failures. It covers supported element types, value semantics, non-aborting partial failures, and a retry path. The only notable gap is the exact shape of per-field status reporting.

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

Parameters4/5

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

The schema already covers parameter structure with 100% description coverage, so the baseline is 3. The description adds value by explaining type-to-control mapping: booleans for checkboxes/radio buttons, string for text/select, and select matching by value or visible label. This clarifies semantics beyond the raw schema.

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

Purpose5/5

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

The description clearly states a specific action ('Fill a whole form in one call') and enumerates the supported input types: text inputs, select, checkboxes, and radio buttons. It distinguishes fill_form from siblings like click and type by emphasizing the batch, one-round-trip behavior.

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

Usage Guidelines4/5

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

The description conveys when to use the tool: for filling an entire form in one call rather than field-by-field typing/clicking. It also gives an explicit recovery path for per-field errors: call view_page and retry failing fields. It stops short of listing scenarios where fill_form should not be used, but the guidance is still actionable.

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

handle_dialogA

Configure handling of alerts, confirms and prompts. Set it BEFORE triggering the action that opens the dialog. Uses CDP Page.javascriptDialogOpening, so it works even while the dialog blocks all JS.

ParametersJSON Schema
NameRequiredDescriptionDefault
textNoText entered into prompt dialogs (with action accept)
actionYesaccept or dismiss the next dialog; get_status: pending ones

TDQS

A4.2/5.0
Behavior4/5

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

With no annotations, the description carries the behavioral burden. It discloses an important implementation trait: it uses CDP Page.javascriptDialogOpening, so it functions even while the dialog blocks all JavaScript. It also warns that configuration must happen before the triggering action. Minor gaps remain, such as whether the handler persists across dialogs, but the provided behavioral detail is meaningful.

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

Conciseness5/5

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

Two compact sentences with no filler. The purpose is front-loaded, and the critical timing caveat and CDP mechanism each earn their place.

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

Completeness4/5

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

For a simple two-parameter tool with full schema coverage, the description is largely complete: it explains purpose, timing, and mechanism. The only missing piece is the exact shape of get_status output, but the action is simple enough that the schema enum plus 'pending ones' provides adequate guidance.

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

Parameters3/5

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

Schema description coverage is 100%, so the input schema already fully documents both parameters and the enum values. The description adds no additional parameter meaning, which is acceptable given the baseline for fully covered schemas.

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

Purpose5/5

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

The description states a specific verb ('Configure') and a precise resource ('handling of alerts, confirms and prompts'), clearly distinguishing this from the browser-interaction siblings like click, type, and navigate. No other sibling targets JavaScript dialogs.

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

Usage Guidelines4/5

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

The description gives clear usage context: set this before triggering the action that opens the dialog, and it works even when the dialog blocks JS. It does not explicitly enumerate alternatives or exclusions, but no sibling tool handles dialogs, so the routing is still clear.

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

network_monitorA

Capture network requests via CDP, including those the page issues itself. Workflow: start → trigger the action → get(pattern: 'api').

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYesstart recording | get results | stop: return and clear
filterNo'failed': only HTTP >= 400 or network errors
patternNoRegex for request URLs

TDQS

A4.3/5.0
Behavior4/5

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

With no annotations, the description carries the behavioral burden. It discloses a non-obvious behavior: the tool captures requests the page itself issues, not just externally visible traffic. The workflow also communicates the stateful lifecycle of start/get/stop. It does not detail return payload shape or repeated-start reset behavior, but schema covers the stop semantics. Overall, it adds meaningful behavioral context beyond the structured fields.

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

Conciseness5/5

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

Two sentences with no filler. The first sentence states the core purpose and scope; the second gives the essential workflow. Every part contributes meaning, and the most important information is front-loaded.

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

Completeness3/5

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

For a stateful tool with three parameters and no output schema, the description provides the essential lifecycle and an example call. However, it does not explain what the get action returns (e.g., status codes, headers, timing data), and there is no guidance on repeated start calls. Since no output schema exists, this missing return-format information leaves the agent somewhat uncertain after invocation. Adequate but not fully complete.

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

Parameters4/5

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

Schema description coverage is 100%, so the baseline is 3. The description adds value by showing how the pattern parameter is intended to be used in the workflow ('get(pattern: "api")'), which illustrates parameter application rather than just listing definitions. This minimal but useful addition justifies slightly above baseline.

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

Purpose5/5

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

The description states a specific verb and resource: 'Capture network requests via CDP'. It also refines the scope with 'including those the page issues itself', which clearly distinguishes what is captured. No sibling tool overlaps with this function, so the purpose is unambiguous.

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

Usage Guidelines4/5

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

The description provides a clear workflow: 'start → trigger the action → get(pattern: "api")', which tells an agent the order of operations and how to use the pattern parameter. It does not explicitly mention alternatives or exclusions, but no sibling tool is a natural alternative for network monitoring, so this is acceptable.

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

observeA

Watch an element for changes instead of polling with MutationObserver/setInterval code in evaluate. collect: record text/attribute changes for 'duration' ms. until: wait for a condition, then optionally click at once (then_click). click_first fires the triggering action after the observer is armed, so nothing is missed.

ParametersJSON Schema
NameRequiredDescriptionDefault
untilNoJS expression checked on each change, 'el' is the element, e.g. el.textContent === '8'
collectNotext (textContent) | attributes | alltext
timeoutNoMax observation time in ms (max 25000)
durationNoCollect window in ms (default 5000); exclusive with until
intervalNoPolling fallback interval in ms
selectorYesCSS selector or ref of the element to observe
then_clickNoSelector or ref to click immediately when until holds
click_firstNoSelector or ref to click once the observer is armed

TDQS

A4.3/5.0
Behavior4/5

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

With no annotations provided, the description carries the full disclosure burden, and it delivers meaningfully: it reveals two distinct operational modes (collect and until), the click-ordering guarantee ('click_first fires the triggering action after the observer is armed, so nothing is missed'), and the optional then_click behavior. It stops short of noting the internal polling fallback behind the interval parameter, though the schema documents that parameter.

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

Conciseness5/5

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

Three sentences with the main purpose front-loaded in sentence one, followed by compact mode explanations. Every sentence earns its place; the 'so nothing is missed' clause captures an important ordering guarantee in four words without padding.

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

Completeness3/5

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

The description covers the two execution modes and the click behaviors well, but the tool has no output schema and the description never states what the tool returns after a collect window or when an until condition fires. Constraint details like duration/until exclusivity and the polling-fallback behavior are left entirely to the schema. For an 8-parametter tool with zero annotations, this is adequate but not complete.

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

Parameters4/5

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

Schema description coverage is 100%, giving a baseline of 3, and the description adds genuine value on top of the schema: it connects collect to duration, until to then_click, and clarifies the intent of click_first ('fires the triggering action after the observer is armed'). This conceptual grouping makes the parameter model understandable in a way the flat per-parameter schema descriptions alone do not.

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

Purpose5/5

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

States a specific action (watch an element for changes) on a specific resource (a DOM element via selector), and immediately distinguishes itself from the sibling evaluate tool ('instead of polling with MutationObserver/setInterval code in evaluate'). A new agent can tell what this tool does and how it differs from evaluate without opening the schema.

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

Usage Guidelines4/5

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

The opening line gives a clear when-to-use signal: prefer this tool over hand-written MutationObserver/setInterval polling inside evaluate, which is a concrete alternative. However, the 'until' mode functionally overlaps with the sibling wait_for tool, and the description never mentions that alternative or states when not to use observe, so the routing guidance is incomplete.

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

press_keyA

Press a key or shortcut (Enter, Escape, Tab, arrows, Ctrl+K). Optionally focus an element first via ref or selector.

ParametersJSON Schema
NameRequiredDescriptionDefault
keyYesKey to press, e.g. 'Enter'; printable chars as-is
refNoElement ref to focus first
selectorNoCSS selector to focus first
modifiersNoModifier keys held during the press

TDQS

A3.8/5.0
Behavior3/5

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

There are no annotations, so the description carries the burden of behavioral disclosure. It states the core action (pressing a key or shortcut) and the optional focus-first behavior, which is useful. It does not disclose details like whether the key event is dispatched natively, whether modifiers are held during the press, or whether any wait/retry behavior occurs, but the description is not misleading.

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

Conciseness5/5

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

The description is a single, tightly worded sentence that front-loads the core action and then adds the optional focusing detail. No filler or redundant information is present.

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

Completeness4/5

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

For a relatively simple keyboard-action tool with a fully described schema, the description covers the essential usage and behavior. It lacks an explicit note about return values or post-press page effects, but the tool's low complexity and complete parameter documentation make the definition largely sufficient.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents key, ref, selector, and modifiers. The description adds value with concrete key examples and clarifies the optional focus-first usage of ref/selector, but it does not meaningfully enrich parameter semantics beyond what the schema provides.

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

Purpose5/5

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

The description uses a specific verb ('Press') and a clear resource ('a key or shortcut'), with concrete examples like Enter, Escape, Tab, arrows, and Ctrl+K. This makes the tool's purpose unambiguous and distinguishes it from sibling tools like click (mouse action) or type (text input) without needing to open the schema.

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

Usage Guidelines3/5

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

The description implies when to use the tool through examples of keyboard shortcuts and key presses, and mentions the optional focus-first behavior via ref or selector. However, it does not explicitly state when to prefer press_key over siblings like type or click, nor does it provide exclusions or alternative routing.

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

run_planA

Batch the next 2+ known actions (click, type, scroll, view_page chains) here instead of N separate calls: a sequence of tool steps, executed server-side in one call. Variables via vars and saveAs ($name), conditions (if), suspend/resume to ask the agent mid-plan, and errorStrategy abort | continue | capture_image.

ParametersJSON Schema
NameRequiredDescriptionDefault
varsNoInitial variables, available as $name
stepsNoTool steps to run in order
resumeNoResume a suspended plan
errorStrategyNoabort stops at the first error; continue runs all steps; capture_image screenshots, then abortsabort

TDQS

A4.3/5.0
Behavior4/5

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 transparently describes server-side execution, variable handling via vars and saveAs, conditions, suspend/resume for mid-plan interaction, and errorStrategy options. It does not detail side effects (which depend on the underlying tools) but provides a clear picture of the tool's control-flow behavior.

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

Conciseness4/5

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

The description is a single, dense paragraph that front-loads the core purpose ('Batch the next 2+ known actions...') and then enumerates the key features efficiently. Every clause adds information, and there is no filler or repetition.

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

Completeness4/5

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

The tool is complex with nested objects and multiple control-flow options. The description covers the major features (variables, conditions, suspend/resume, error strategy) and the schema fills in structural details. It could be more explicit about when to use suspend or error strategies, but the combination of description and schema is sufficient for an agent to understand the tool's role and capabilities.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3. The description adds conceptual meaning beyond the schema by explaining how the parameters work together: variables available as $name, saveAs for later steps, if conditions, suspend/resume, and errorStrategy choices. This synthesis helps an agent understand the batching workflow, elevating it above a bare parameter list.

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

Purpose5/5

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

The description clearly states the tool's function: batching 2+ known actions into a single server-side call. It explicitly contrasts with N separate calls, which differentiates it from individual action tools. The resource is well-defined as a sequence of tool steps, and the purpose is unmistakable.

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

Usage Guidelines4/5

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

The description implies when to use it: when you have 2+ known actions to execute. It mentions 'instead of N separate calls', which is a clear usage directive. However, it does not explicitly name sibling tools like batch_evaluate or exclude scenarios where individual calls are preferable, leaving some room for inference.

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

scrollA

Scroll the page, a container, or an element into view. Reports the position and content growth (scrollHeight delta — detects lazy-loaded content) and handles settle timing. Use container_ref/container_selector plus direction to scroll inside a container.

ParametersJSON Schema
NameRequiredDescriptionDefault
refNoElement ref to scroll into view
amountNoPixels per scroll (default 500); only with direction
selectorNoCSS selector to scroll into view
directionNoup or down (default down); used when no ref/selector
container_refNoScrollable container ref (scrolls it, not the page)
container_selectorNoScrollable container CSS selector

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It covers important runtime behavior: it reports position, detects content growth via scrollHeight delta (useful for lazy-loaded content), and handles settle timing. It does not mention potential side effects or failure modes, but for a scrolling action this is reasonably transparent.

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

Conciseness5/5

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

The description is two sentences, front-loaded with the core purpose, and every clause adds information: scope, behavioral reports, lazy-load detection, settle handling, and container-specific usage. There is no filler or redundant restating of the tool name.

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

Completeness4/5

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

The tool has six parameters, no required fields, no annotations, and no output schema. The description covers target selection, container behavior, and what is reported, which is enough to invoke it correctly. It could add explicit return structure or failure behavior, but those would be improvements rather than critical gaps.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3. The description adds value beyond the schema by explaining the relationship between container_ref/container_selector and direction for scrolling inside a container, which clarifies parameter combinations not fully spelled out in the schema.

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

Purpose5/5

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

The description uses a specific verb ('Scroll') and explicitly names the resources it operates on: page, container, and element. It further distinguishes itself by mentioning position reporting, content-growth detection, and settle timing, which separates it from navigation or page-view siblings.

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

Usage Guidelines4/5

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

The description gives clear operational context: use ref/selector to scroll into view, direction for page scrolling, and container_ref/container_selector plus direction for container scrolling. It does not explicitly name sibling alternatives or exclusion cases, but the usage context is clearly implied.

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

set_page_dataA

Write a payload larger than 1 MB into window.__pb_data[key], chunked past the CDP 1 MB message limit. Source: inline (data) or file (absolute path, read as binary). The page reads window.__pb_data[key] (string or ArrayBuffer per encoding) and window.__pb_data[key + '__complete'] === true. Never write one key from parallel calls — they race. Under ~200 KB use evaluate; for a real use file_upload.

ParametersJSON Schema
NameRequiredDescriptionDefault
keyYesProperty name under window.__pb_data (JS identifier, see pattern)
sourceYesinline: pass data. file: pass an absolute path, read as binary
encodingNoutf8 (inline default) keeps a string; binary (file default) gives an ArrayBuffer for FileReader/Blob/fetch; base64 keeps the base64 string
chunkSizeNoRaw bytes per chunk before base64 (default 500000; max 700000)

TDQS

A4.6/5.0
Behavior5/5

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

With no annotations, the description carries the full burden, and it delivers: chunking behavior, the page-side contract (`__complete` flag), encoding-dependent consumer types (string vs ArrayBuffer), and a concurrency race warning. This is a high level of disclosure for a tool with no structured hints.

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

Conciseness4/5

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

Every sentence carries useful information and the core purpose is front-loaded. It is slightly denser than necessary and partially repeats source options already in the schema, but there is no filler or padding.

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

Completeness3/5

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

The description thoroughly covers input modes, page behavior, safety, and alternatives. However, it never states what the tool call returns or whether it resolves after chunks are sent versus after the page sets `__complete`. With no output schema, this is a meaningful gap for an agent deciding how to observe completion.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3. The description adds meaningful context on top: inline vs file source semantics, binary reading for files, and how encoding affects whether the page receives a string or ArrayBuffer. This goes beyond the schema's raw type descriptions.

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

Purpose5/5

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

States a specific verb ('Write'), a specific target (`window.__pb_data[key]`), and a distinguishing mechanism (chunking past the CDP 1 MB limit). It is clearly differentiated from siblings like `evaluate` and `file_upload`, which are explicitly named as alternatives.

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

Usage Guidelines5/5

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

Provides explicit when-to-use guidance: payloads larger than 1 MB. It also names excluded alternatives: use `evaluate` under ~200 KB and `file_upload` for real `<input type=file>` cases. The warning about parallel writes to the same key adds important usage constraint.

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

switch_tabA

Open a new tab, switch to a tab by ID (from virtual_desk), or close one. Prefer 'open' over navigate when the user's active tab must stay untouched. Refs from the previous tab are invalid afterwards — call view_page before acting.

ParametersJSON Schema
NameRequiredDescriptionDefault
tabNoTab ID or 1-based number (switch/close; close: active tab)
urlNoURL for a new tab (open); default about:blank
actionNoopen | switch | closeswitch

TDQS

A4.4/5.0
Behavior4/5

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 clearly warns that refs from the previous tab are invalid afterward and tells the agent to call view_page before acting, which is a key side-effect beyond what the schema reveals.

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

Conciseness5/5

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

Three short sentences deliver all essential guidance with no filler. The main action set is front-loaded, followed by the usage caveat and the critical invalidation warning, each earning its place.

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

Completeness4/5

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

The description covers the three actions, the source of tab IDs, the open-vs-navigate decision, and the invalid-ref consequence, which is strong for a tool without annotations or an output schema. It could be slightly more explicit about post-open/switch state, but the provided guidance is sufficient for correct invocation in most cases.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3. The description adds meaningful value by specifying that tab IDs come from virtual_desk and by clarifying the open behavior relative to the active tab, going slightly beyond the schema's parameter descriptions.

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

Purpose5/5

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

The description clearly enumerates three specific operations—open, switch, and close—on tabs, and ties tab IDs to virtual_desk. It is differentiated from navigate by explicitly preferring open when the active tab must stay untouched.

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

Usage Guidelines4/5

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

It gives explicit guidance to prefer open over navigate when the active tab must remain unchanged, which is a concrete decision rule. It also instructs that view_page should be called after switching because old refs are invalid, though it does not fully cover when to use alternatives like tab_status.

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

tab_statusA

Active tab's cached URL, title, ready state and errors — a cheap mid-workflow sanity check ('did my click navigate?'). Not for tab discovery or page content.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

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 honestly states the data is cached, implying it may not be perfectly live, and characterizes the call as cheap, signaling low cost. It also mentions errors are included, giving useful behavioral context beyond a simple 'get status' phrasing.

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

Conciseness5/5

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

Two sentences deliver a dense but readable description: what the tool returns, the typical use case, and a clear boundary. Every phrase earns its place, and the most important identifying information is front-loaded.

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

Completeness5/5

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

For a zero-parameter tool with no output schema, the description is complete: it lists the returned fields (URL, title, ready state, errors), the scope (active tab), the performance profile (cheap), and the appropriate use case. Nothing essential is missing for an agent to decide whether and when to invoke it.

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

Parameters4/5

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

The tool has zero parameters, so there is no parameter semantics burden on the description. The baseline of 4 applies because no parameter documentation is needed and the description appropriately focuses on what the tool returns rather than input details.

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

Purpose5/5

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

The description clearly identifies the tool as returning the active tab's cached URL, title, ready state, and errors, which is a specific, concrete resource. It also explicitly distinguishes itself from tools for tab discovery or page content, preventing confusion with siblings like switch_tab, view_page, or dom_snapshot.

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

Usage Guidelines4/5

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

The description gives a clear when-to-use scenario: a cheap mid-workflow sanity check, exemplified by 'did my click navigate?'. It also provides an explicit when-not-to-use boundary (not for tab discovery or page content), though it does not name specific alternative tools.

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

typeA

Type text into an input identified by ref or CSS selector. For 2+ fields use fill_form; for special keys (Enter, Escape, Tab, arrows) or shortcuts (Ctrl+K) use press_key.

ParametersJSON Schema
NameRequiredDescriptionDefault
refNoElement ref (preferred)
textYesText to type
clearNoClear the field first
selectorNoCSS selector (fallback)

TDQS

A3.8/5.0
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure, but it only states the action and target mechanism. It does not disclose whether the field is focused or clicked first, whether text overwrites existing content or appends to the cursor position, what happens when neither ref nor selector is provided, or how multiple matching selectors are handled.

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

Conciseness5/5

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

The description is two sentences with no wasted words. The core action is front-loaded, and the sibling-routing guidance is compact and immediately actionable.

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

Completeness2/5

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

Despite a complete schema, this is a mutation-oriented tool with no annotations and no output schema. The description leaves important operational gaps undefined: target resolution when ref/selector is absent, clearing semantics, return/failure behavior, and handling of ambiguous element matches. The sibling routing helps but does not make the description complete.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents ref, text, clear, and selector meaningfully. The description largely restates 'ref or CSS selector' without adding substantive value beyond what the parameter descriptions already give, landing at the baseline of 3.

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

Purpose5/5

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

The description opens with a specific verb and resource: 'Type text into an input identified by ref or CSS selector.' It clearly differentiates itself from sibling tools by explicitly naming fill_form and press_key as the alternatives for multi-field and special-key scenarios.

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

Usage Guidelines5/5

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

The description explicitly states when to use the tool ('Type text into an input') and when not to: 'For 2+ fields use fill_form; for special keys (Enter, Escape, Tab, arrows) or shortcuts (Ctrl+K) use press_key.' It also communicates the ref-preferred, selector-fallback targeting strategy.

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

view_pageA

See what is on the page: text plus stable element refs for click/type/fill_form. Default filter 'interactive' lists actionable elements; for paragraphs or table cells call view_page(ref, filter: 'all'). Collapsed containers show as [eXX role, N items] — expand with view_page(ref: 'eXX', filter: 'all').

ParametersJSON Schema
NameRequiredDescriptionDefault
refNoElement ref for a subtree
depthNoTree levels shown; indentation only, hidden sections need a click
filterNointeractive | all | landmark | visual (bounds, click point, visibility)interactive
max_tokensNoToken budget; content downsampled. Omit for full output

TDQS

A4.4/5.0
Behavior4/5

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

No annotations are provided, so the description carries the burden of behavioral disclosure. It discloses that the output includes stable refs, that collapsed containers show summaries like '[eXX role, N items]', and that follow-up expansion calls are possible. This is meaningful behavior beyond the schema and helps the agent anticipate output shape.

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

Conciseness5/5

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

Three sentences, each earning its place: the core purpose comes first, then filter usage, then expansion behavior. There is no filler or redundant restatement of the schema.

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

Completeness4/5

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

For an inspection tool with four optional parameters and no output schema, the description is largely sufficient: it specifies the return content, default behavior, and how to get broader or expanded views. The main gap is the absence of explicit differentiation from sibling inspection tools, but the core calling pattern is fully covered.

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

Parameters4/5

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

Schema description coverage is 100%, so a baseline of 3 applies. The description adds practical meaning by mapping the 'filter' parameter to specific scenarios and explaining how 'ref' is used to expand collapsed subtrees, going beyond the schema's enum descriptions.

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

Purpose5/5

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

The description states a specific action and resource: 'See what is on the page: text plus stable element refs for click/type/fill_form.' It clearly communicates what the tool returns and why it is useful, distinguishing its role from generic page-capture tools even without naming a sibling.

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

Usage Guidelines4/5

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

The description gives explicit guidance for variant usage: default filter 'interactive' lists actionable elements, filter 'all' is for paragraphs/table cells, and collapsed containers are expanded via view_page(ref, filter: 'all'). It does not compare against sibling inspection tools like dom_snapshot or observe, but it clearly tells the agent when to adjust the call.

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

virtual_deskA

List all open tabs with IDs, URLs and state. Call it first in every session, after a reconnect, or when unsure; reuse a listed tab ID instead of opening duplicates. Cheap, call liberally.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

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

With no annotations, the description carries the burden of behavioral disclosure. It clearly indicates a read-only listing operation and adds useful context about cost and call frequency. It does not explain what 'state' includes or what happens if no tabs are open, but for a simple listing tool this is a minor gap.

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

Conciseness5/5

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

Three sentences with zero fluff: the core purpose is front-loaded, followed by direct usage instructions and a cost hint. Every sentence adds actionable information.

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

Completeness5/5

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

For a zero-parameter listing tool with no output schema, the description is complete: it states what is listed, when to call it, how to use the results, and how often it may be invoked. No missing information is needed to call or interpret the tool correctly.

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

Parameters4/5

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

The tool has zero parameters, so the baseline is 4; the description correctly implies no input is needed. It adds no parameter-specific detail because none exists, which is appropriate.

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

Purpose5/5

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

The description begins with a specific verb and resource: 'List all open tabs with IDs, URLs and state.' This clearly distinguishes it from sibling tools like switch_tab or tab_status, which operate on specific tabs or current tab state rather than enumerating everything.

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

Usage Guidelines5/5

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

The description provides explicit when-to-use guidance: 'Call it first in every session, after a reconnect, or when unsure.' It also tells the agent to reuse listed tab IDs instead of opening duplicates, and explicitly states the cost profile: 'Cheap, call liberally.'

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

wait_forA

Wait until a condition holds: element visible, page text present, URL match, network idle, or a JS expression true. Prefer condition 'text' over a JS expression for 'has the page said X yet'. assert: true checks once and fails instead of waiting.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoSubstring of the page URL; required for condition 'url'
textNoSubstring of document.body.innerText, case-sensitive; required for condition 'text'
assertNoCheck once, fail if it does not hold; timeout 0, _meta.code 'assertion_failed'
timeoutNoMax wait in ms (default 10000; 0 when assert is true)
selectorNoCSS selector or ref; required for condition 'element'
conditionYeselement | text | url | network_idle | js
expressionNoExpression that should become true; required for condition 'js'

TDQS

A3.9/5.0
Behavior4/5

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 explains the core polling-style behavior, the five condition types, and the critical assert mode that checks once and fails instead of waiting. This is meaningful behavioral context beyond the bare schema.

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

Conciseness5/5

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

Three sentences with no filler. The main purpose is front-loaded, condition selection guidance is next, and assert behavior is saved for last. Every sentence earns its place.

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

Completeness4/5

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

The tool is moderately complex with seven parameters and no annotations, but the schema covers every parameter and the description covers the core behaviors and condition choices. It could go further by mentioning timeout failure behavior or the relationship to observe, but as paired with the schema it is largely complete for correct invocation.

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

Parameters4/5

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

Schema description coverage is 100%, so the baseline is 3. The description adds value by advising when to use 'text' instead of a JS expression, which helps an agent choose the correct condition parameter. It also clarifies the semantic effect of assert, adding meaning beyond the parameter descriptions.

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

Purpose4/5

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

Theescription clearly states what the tool does: wait until a condition holds, and enumerates the supported condition types (element, text, URL, network_idle, JS). It is specific about the resource and actions, but does not explicitly differentiate itself from sibling tools such as observe, so it loses the full marks for sibling diistinction.

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

Usage Guidelines3/5

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

The description provides useful internal selection guidance, particularly preferring condition 'text' over JS expressions for checking page text, and explains the assert: true behavior. However, it does not address when to use wait_for versus a sibling tool such as observe or evaluate, so the usage guidance is partial.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev3.0.0
    • Changedrun_plan2 fields changed
      • removedInput schema / properties / parallel
        Removed value: -{
        -  "description": "Tab groups to run in parallel",
        -  "items": {
        -    "properties": {
        -      "steps": {
        -        "description": "Steps for this tab",
        -        "items": {
        -          "$ref": "#/properties/steps/items"
        -        },
        -        "type": "array"
        -      },
        -      "tab": {
        -        "description": "Tab ID (targetId)",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "tab",
        -      "steps"
        -    ],
        -    "type": "object"
        -  },
        -  "type": "array"
        -}
      • removedInput schema / properties / use_operator
        Removed value: -{
        -  "default": false,
        -  "description": "Operator mode (rule engine + micro-LLM); needs the executeOperator hook",
        -  "type": "boolean"
        -}
  2. 25 tool updatesv2.10.5
    • Changedbatch_evaluate7 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / continue_on_error / description
        Previous value: -"Continue processing remaining URLs if one fails (default: true)"New value: +"Continue with the remaining URLs if one fails"
      • changedInput schema / properties / evaluate_per_page / description
        Previous value: -"JavaScript expression to evaluate on each page after it loads"New value: +"JS expression evaluated on each page"
      • changedInput schema / properties / settle_ms / description
        Previous value: -"Wait time in ms after each page load before evaluating (default: 2000)"New value: +"Wait in ms after each page load before evaluating"
      • changedInput schema / properties / timeout_per_page_ms / description
        Previous value: -"Timeout per page in ms for the full navigate+settle+evaluate cycle (default: 30000)"New value: +"Timeout per page in ms (navigate+settle+evaluate)"
      • changedInput schema / properties / urls / description
        Previous value: -"Array of URLs to visit and evaluate sequentially"New value: +"URLs to visit in order"
    • Changedcapture_image4 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / full_page / description
        Previous value: -"Capture full scrollable page instead of just viewport"New value: +"Capture the full scrollable page, not just the viewport"
      • changedInput schema / properties / som / description
        Previous value: -"Overlay numbered labels on interactive elements matching view_page ref IDs (Set-of-Mark)"New value: +"Overlay numbered labels matching view_page refs (Set-of-Mark)"
    • Changedclick8 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / ref / description
        Previous value: -"A11y-Tree element ref (e.g. 'e5') — preferred over selector"New value: +"Element ref (preferred)"
      • changedInput schema / properties / selector / description
        Previous value: -"CSS selector (e.g. '#submit-btn') — fallback when ref is not available"New value: +"CSS selector (fallback)"
      • changedInput schema / properties / text / description
        Previous value: -"Visible text to match (e.g. 'Submit'). Finds element by name in the A11y tree — no prior view_page needed. Prefers interactive elements (buttons, links)."New value: +"Visible text (a11y name); no view_page needed, prefers interactive"
      • changedInput schema / properties / wait_for_diff / description
        Previous value: -"When true, wait for the DOM diff synchronously before returning (slower but diff is in this response). Default: false — diff piggybacks on the next tool response."New value: +"Wait for the DOM diff before returning (default false, slower)"
      • changedInput schema / properties / x / description
        Previous value: -"X coordinate (viewport pixels) — for canvas or pixel-precise clicks. Use with y instead of ref/selector."New value: +"Viewport X in px; with y, not ref/selector"
      • changedInput schema / properties / y / description
        Previous value: -"Y coordinate (viewport pixels) — for canvas or pixel-precise clicks. Use with x instead of ref/selector."New value: +"Viewport Y in px; use with x"
    • Changedconfigure_session6 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / autoPromote / description
        Previous value: -"If true, apply all current auto-promote suggestions as defaults"New value: +"Apply all current auto-promote suggestions"
      • changedInput schema / properties / defaults / description
        Previous value: -"Set session defaults. Keys: param names (tab, timeout, etc.). Values: default values. null removes a default."New value: +"Param name → default value; null removes a default"
      • changedInput schema / properties / profile / description
        Previous value: -"Chrome profile name (e.g. \"Julian\", \"Business\"). Use `public-browser profiles` to list available profiles. With restart: true, can switch profiles mid-session."New value: +"Chrome profile name (list them with: public-browser profiles); restart: true switches mid-session"
      • changedInput schema / properties / restart / description
        Previous value: -"If true, restart Chrome with the new profile even if the browser is already running. Closes all current tabs."New value: +"Restart Chrome with the new profile even if running; closes all tabs"
    • Changedconsole_logs4 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / clear / description
        Previous value: -"Clear the log buffer after returning results"New value: +"Clear the buffer after reading"
      • changedInput schema / properties / pattern / description
        Previous value: -"Regex pattern to match against log text"New value: +"Regex matched against log text"
    • Changeddom_snapshot3 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / ref / description
        Previous value: -"Element ref (e.g. 'e42') to get subtree snapshot for"New value: +"Element ref for a subtree snapshot"
    • Changeddownload5 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / action / description
        Previous value: -"status: check/wait for pending downloads (waits briefly for one to start, then until it finishes). list: full session history, returns immediately and never waits — use it for polling loops."New value: +"status waits for pending downloads; list: history, never waits (use for polling)"
      • changedInput schema / properties / settle / description
        Previous value: -"Grace window in ms to wait for a download to START before reporting 'no downloads' (default: 250). Chrome fires downloadWillBegin a few ms after the click that triggers it. Set 0 for an instant check, or use action: 'list' which never waits."New value: +"Ms to wait for a download to START before reporting none — Chrome fires downloadWillBegin a few ms after the click; 0 = instant"
      • changedInput schema / properties / timeout / description
        Previous value: -"Max wait time in ms for pending downloads (default: 30000)"New value: +"Max wait in ms for pending downloads"
    • Changeddrag11 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / from_ref / description
        Previous value: -"A11y-Tree source ref (e.g. 'e5')"New value: +"Source element ref"
      • changedInput schema / properties / from_selector / description
        Previous value: -"CSS selector for source element"New value: +"Source CSS selector"
      • changedInput schema / properties / from_x / description
        Previous value: -"Source X coord (viewport px) — alternative zu Ref"New value: +"Source viewport X in px (with from_y)"
      • changedInput schema / properties / from_y / description
        Previous value: -"Source Y coord (viewport px) — alternative zu Ref"New value: +"Source viewport Y in px"
      • changedInput schema / properties / steps / description
        Previous value: -"Anzahl mouseMoved-Events zwischen press und release (min 5 fuer HTML5-dragover)"New value: +"Native mouseMoved events between press and release; minimum 5"
      • changedInput schema / properties / to_ref / description
        Previous value: -"A11y-Tree target ref (e.g. 'e7')"New value: +"Target element ref"
      • changedInput schema / properties / to_selector / description
        Previous value: -"CSS selector for target element"New value: +"Target CSS selector"
      • changedInput schema / properties / to_x / description
        Previous value: -"Target X coord (viewport px)"New value: +"Target viewport X in px (with to_y)"
      • changedInput schema / properties / to_y / description
        Previous value: -"Target Y coord (viewport px)"New value: +"Target viewport Y in px"
    • Changedevaluate2 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
    • Changedfile_upload5 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / path / description
        Previous value: -"Absolute file path(s) to upload. String for single file, array for multiple files."New value: +"Absolute path(s) of the file(s) to upload"
      • changedInput schema / properties / ref / description
        Previous value: -"A11y-Tree element ref (e.g. 'e8') — preferred when input is visible in view_page"New value: +"Element ref of the file input"
      • changedInput schema / properties / selector / description
        Previous value: -"CSS selector (e.g. 'input[type=file]') — use this for hidden file inputs (display:none, off-screen). Many React/Vue apps render a visible custom button that triggers a hidden <input type=file>; the hidden input is NOT in the a11y-tree, so ref won't find it. Pass the selector instead."New value: +"CSS selector of the file input; needed for hidden inputs (display:none, off-screen), which have no ref"
    • Changedfill_form7 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / fields / description
        Previous value: -"Array of fields to fill. Each field needs ref or selector plus value."New value: +"Each needs ref or selector plus value"
      • removedInput schema / properties / fields / items / additionalProperties
        Removed value: -false
      • changedInput schema / properties / fields / items / properties / ref / description
        Previous value: -"A11y-Tree element ref (e.g. 'e5') — preferred over selector"New value: +"Element ref (preferred)"
      • changedInput schema / properties / fields / items / properties / selector / description
        Previous value: -"CSS selector (e.g. '#email') — fallback when ref is not available"New value: +"CSS selector (fallback)"
      • changedInput schema / properties / fields / items / properties / value / description
        Previous value: -"Value to set: string for text/select, boolean for checkbox/radio, number coerced to string"New value: +"string for text/select, boolean for checkbox/radio; number → string"
    • Changedhandle_dialog4 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / action / description
        Previous value: -"accept: accept the next dialog, dismiss: dismiss/cancel it, get_status: check pending dialogs"New value: +"accept or dismiss the next dialog; get_status: pending ones"
      • changedInput schema / properties / text / description
        Previous value: -"Text to enter in prompt dialogs (only used with action: accept)"New value: +"Text entered into prompt dialogs (with action accept)"
    • Changednavigate5 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / action / description
        Previous value: -"Navigation action: goto (default), back, or reload"New value: +"goto (default) | back | reload"
      • changedInput schema / properties / settle_ms / description
        Previous value: -"Extra wait time in ms after page load (default: 500)"New value: +"Extra wait in ms after load (default 500)"
      • changedInput schema / properties / url / description
        Previous value: -"URL to navigate to (required for goto action)"New value: +"URL to open (required for goto)"
    • Changednetwork_monitor5 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / action / description
        Previous value: -"start: begin recording, get: retrieve recorded requests, stop: return and clear"New value: +"start recording | get results | stop: return and clear"
      • changedInput schema / properties / filter / description
        Previous value: -"Filter results — 'failed': only requests with HTTP >= 400 or network errors"New value: +"'failed': only HTTP >= 400 or network errors"
      • changedInput schema / properties / pattern / description
        Previous value: -"Regex pattern to match against request URLs"New value: +"Regex for request URLs"
    • Changedobserve10 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / click_first / description
        Previous value: -"CSS selector or element ref (e.g. 'e5') to click AFTER the observer is set up but BEFORE collection starts. Use to trigger the changes you want to observe (e.g. 'Start Mutations' button)."New value: +"Selector or ref to click once the observer is armed"
      • changedInput schema / properties / collect / description
        Previous value: -"What to collect: 'text' for textContent changes, 'attributes' for attribute changes, 'all' for both (default: 'text')"New value: +"text (textContent) | attributes | all"
      • changedInput schema / properties / duration / description
        Previous value: -"Collect all changes for this many ms, then return them. Mutually exclusive with 'until'. Default: 5000"New value: +"Collect window in ms (default 5000); exclusive with until"
      • changedInput schema / properties / interval / description
        Previous value: -"Polling interval in ms for change detection fallback (default: 100)"New value: +"Polling fallback interval in ms"
      • changedInput schema / properties / selector / description
        Previous value: -"CSS selector or element ref (e.g. 'e5') of the element to observe"New value: +"CSS selector or ref of the element to observe"
      • changedInput schema / properties / then_click / description
        Previous value: -"CSS selector or element ref (e.g. 'e5') to click immediately when 'until' condition is met (for timing-critical actions). Only used with 'until'."New value: +"Selector or ref to click immediately when until holds"
      • changedInput schema / properties / timeout / description
        Previous value: -"Maximum observation time in ms (default: 10000, max: 25000)"New value: +"Max observation time in ms (max 25000)"
      • changedInput schema / properties / until / description
        Previous value: -"JS expression evaluated on each change — stops when it returns true. Variable 'el' is the observed element. Example: el.textContent === '8'"New value: +"JS expression checked on each change, 'el' is the element, e.g. el.textContent === '8'"
    • Changedpress_key6 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / key / description
        Previous value: -"Key to press — e.g. 'Enter', 'Escape', 'Tab', 'a', 'ArrowDown', 'F1'. For printable characters use the character itself."New value: +"Key to press, e.g. 'Enter'; printable chars as-is"
      • changedInput schema / properties / modifiers / description
        Previous value: -"Modifier keys to hold during key press (e.g. ['ctrl', 'shift'] for Ctrl+Shift+key)"New value: +"Modifier keys held during the press"
      • changedInput schema / properties / ref / description
        Previous value: -"Element ref to focus before pressing key (e.g. 'e5')"New value: +"Element ref to focus first"
      • changedInput schema / properties / selector / description
        Previous value: -"CSS selector to focus before pressing key (e.g. '#search-input')"New value: +"CSS selector to focus first"
    • Changedrun_plan24 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • addedInput schema / properties / errorStrategy
        Added value: +{
        +  "default": "abort",
        +  "description": "abort stops at the first error; continue runs all steps; capture_image screenshots, then aborts",
        +  "enum": [
        +    "abort",
        +    "continue",
        +    "capture_image"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / parallel / description
        Previous value: -"Array of tab groups to execute in parallel across tabs."New value: +"Tab groups to run in parallel"
      • removedInput schema / properties / parallel / items / additionalProperties
        Removed value: -false
      • changedInput schema / properties / parallel / items / properties / steps / description
        Previous value: -"Steps to execute on this tab"New value: +"Steps for this tab"
      • changedInput schema / properties / parallel / items / properties / tab / description
        Previous value: -"Tab ID (targetId) to execute steps on"New value: +"Tab ID (targetId)"
      • removedInput schema / properties / resume / additionalProperties
        Removed value: -false
      • changedInput schema / properties / resume / description
        Previous value: -"Resume a previously suspended plan."New value: +"Resume a suspended plan"
      • changedInput schema / properties / resume / properties / answer / description
        Previous value: -"Agent's answer to the suspend question"New value: +"Answer to the suspend question"
      • changedInput schema / properties / resume / properties / planId / description
        Previous value: -"ID of the suspended plan to resume"New value: +"ID of the suspended plan"
      • changedInput schema / properties / steps / description
        Previous value: -"Array of tool steps to execute sequentially."New value: +"Tool steps to run in order"
      • removedInput schema / properties / steps / items / additionalProperties
        Removed value: -false
      • changedInput schema / properties / steps / items / properties / if / description
        Previous value: -"Condition expression — step runs only if true. Use $varName for variables. Example: \"$pageTitle === 'Login'\""New value: +"Run the step only if this expression is true, e.g. \"$pageTitle === 'Login'\""
      • changedInput schema / properties / steps / items / properties / params / description
        Previous value: -"Parameters for the tool. Use $varName for variable substitution."New value: +"Tool parameters; $name substitutes a variable"
      • changedInput schema / properties / steps / items / properties / saveAs / description
        Previous value: -"Save step result as variable (accessible via $name in later steps)"New value: +"Save the result as $name for later steps"
      • removedInput schema / properties / steps / items / properties / suspend / additionalProperties
        Removed value: -false
      • changedInput schema / properties / steps / items / properties / suspend / description
        Previous value: -"Suspend plan at this step to ask the agent a question"New value: +"Pause here to ask the agent a question"
      • changedInput schema / properties / steps / items / properties / suspend / properties / condition / description
        Previous value: -"Condition expression — suspend AFTER step if true. Uses $varName syntax."New value: +"Suspend after the step if this $-expression is true"
      • changedInput schema / properties / steps / items / properties / suspend / properties / context / description
        Previous value: -"Context to include: 'capture_image' captures the page"New value: +"capture_image: attach a screenshot"
      • changedInput schema / properties / steps / items / properties / suspend / properties / question / description
        Previous value: -"Question to ask the agent when suspending"New value: +"Question for the agent"
      • changedInput schema / properties / steps / items / properties / tool / description
        Previous value: -"Tool name to execute (e.g. 'click', 'type', 'press_key', 'navigate', 'scroll')"New value: +"Tool name"
      • changedInput schema / properties / use_operator / description
        Previous value: -"Operator mode (rule engine + Micro-LLM). Requires the executeOperator hook to be registered."New value: +"Operator mode (rule engine + micro-LLM); needs the executeOperator hook"
      • addedInput schema / properties / vars
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Initial variables, available as $name",
        +  "type": "object"
        +}
    • Changedscroll8 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / amount / description
        Previous value: -"Pixels to scroll (default: 500). Only used with direction."New value: +"Pixels per scroll (default 500); only with direction"
      • changedInput schema / properties / container_ref / description
        Previous value: -"Scrollable container ref — scroll this container instead of the page (e.g. 'e10')"New value: +"Scrollable container ref (scrolls it, not the page)"
      • changedInput schema / properties / container_selector / description
        Previous value: -"Scrollable container CSS selector (e.g. '.sidebar-list')"New value: +"Scrollable container CSS selector"
      • changedInput schema / properties / direction / description
        Previous value: -"Scroll direction (when no ref/selector given). Default: down"New value: +"up or down (default down); used when no ref/selector"
      • changedInput schema / properties / ref / description
        Previous value: -"Element ref to scroll into view (e.g. 'e42')"New value: +"Element ref to scroll into view"
      • changedInput schema / properties / selector / description
        Previous value: -"CSS selector to scroll into view (e.g. '#item-30')"New value: +"CSS selector to scroll into view"
    • Changedset_page_data7 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / chunkSize / description
        Previous value: -"Raw bytes per chunk before base64 encoding. Default 500_000 (~670 KB base64, safe under CDP's 1 MB-per-message limit). Capped at 700_000 (~933 KB base64) to leave safety margin."New value: +"Raw bytes per chunk before base64 (default 500000; max 700000)"
      • changedInput schema / properties / encoding / description
        Previous value: -"Encoding interpretation. 'utf8' (default for inline) keeps the data as a string. 'binary' (default for file) decodes to ArrayBuffer in the page so apps can pass it to FileReader / Blob / fetch body. 'base64' keeps the base64 string as-is (the page can decode it itself)."New value: +"utf8 (inline default) keeps a string; binary (file default) gives an ArrayBuffer for FileReader/Blob/fetch; base64 keeps the base64 string"
      • changedInput schema / properties / key / description
        Previous value: -"Property name under window.__pb_data (JS identifier — letters, digits, underscore; cannot start with digit)"New value: +"Property name under window.__pb_data (JS identifier, see pattern)"
      • changedInput schema / properties / source / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "data": {
        -        "description": "The payload as a string (base64 or utf-8)",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "inline",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "data"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "path": {
        -        "description": "Absolute file path — the server reads the file as binary",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "file",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "path"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "properties": {
        +      "data": {
        +        "description": "Payload string (base64 or utf-8)",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "inline",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "data"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "properties": {
        +      "path": {
        +        "description": "Absolute file path",
        +        "type": "string"
        +      },
        +      "type": {
        +        "const": "file",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "path"
        +    ],
        +    "type": "object"
        +  }
        +]
      • changedInput schema / properties / source / description
        Previous value: -"Where to read the payload from. type 'inline' → pass `data` as a string. type 'file' → pass absolute `path`; the server reads the file as binary."New value: +"inline: pass data. file: pass an absolute path, read as binary"
    • Changedswitch_tab5 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / action / description
        Previous value: -"Action: open (new tab), switch (to existing tab, default), close (close tab)"New value: +"open | switch | close"
      • changedInput schema / properties / tab / description
        Previous value: -"Tab ID or tab number (1-based index, e.g. '2') to switch to or close (defaults to active tab for close)"New value: +"Tab ID or 1-based number (switch/close; close: active tab)"
      • changedInput schema / properties / url / description
        Previous value: -"URL to navigate to (for open action, defaults to about:blank)"New value: +"URL for a new tab (open); default about:blank"
    • Changedtab_status1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedtype6 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / clear / description
        Previous value: -"Clear existing field content before typing (default: false)"New value: +"Clear the field first"
      • changedInput schema / properties / ref / description
        Previous value: -"Element reference from view_page (e.g. 'e12') — preferred over selector"New value: +"Element ref (preferred)"
      • changedInput schema / properties / selector / description
        Previous value: -"CSS selector as fallback (e.g. 'input[name=email]')"New value: +"CSS selector (fallback)"
      • changedInput schema / properties / text / description
        Previous value: -"Text to type into the element"New value: +"Text to type"
    • Changedview_page6 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / depth / description
        Previous value: -"Nesting depth — how many tree levels to display (default: 3). Controls indentation, not visibility. Hidden sections (display: none) require clicking tabs/buttons to reveal."New value: +"Tree levels shown; indentation only, hidden sections need a click"
      • changedInput schema / properties / filter / description
        Previous value: -"Filter mode: interactive (default), all, landmark, or visual (adds bounds/click/visibility)"New value: +"interactive | all | landmark | visual (bounds, click point, visibility)"
      • changedInput schema / properties / max_tokens / description
        Previous value: -"Token budget — page content is automatically downsampled to fit. Omit for full output."New value: +"Token budget; content downsampled. Omit for full output"
      • changedInput schema / properties / ref / description
        Previous value: -"Element ref (e.g. 'e5') to get subtree for"New value: +"Element ref for a subtree"
    • Changedvirtual_desk1 field changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
    • Changedwait_for9 fields changed
      • removedInput schema / $schema
        Removed value: -"http://json-schema.org/draft-07/schema#"
      • removedInput schema / additionalProperties
        Removed value: -false
      • changedInput schema / properties / assert / description
        Previous value: -"Assert instead of wait: check the condition once and fail if it does not hold (default timeout becomes 0). Failures carry _meta.code = 'assertion_failed'."New value: +"Check once, fail if it does not hold; timeout 0, _meta.code 'assertion_failed'"
      • changedInput schema / properties / condition / description
        Previous value: -"What to wait for: element visibility, visible page text, the URL, network idle, or a JS expression returning true"New value: +"element | text | url | network_idle | js"
      • changedInput schema / properties / expression / description
        Previous value: -"JavaScript expression that should evaluate to true — required when condition is 'js'"New value: +"Expression that should become true; required for condition 'js'"
      • changedInput schema / properties / selector / description
        Previous value: -"CSS selector or element ref (e.g. 'e5') — required when condition is 'element'"New value: +"CSS selector or ref; required for condition 'element'"
      • changedInput schema / properties / text / description
        Previous value: -"Substring of the page's visible text — required when condition is 'text'. Case-sensitive; matches document.body.innerText, i.e. what a reader sees."New value: +"Substring of document.body.innerText, case-sensitive; required for condition 'text'"
      • changedInput schema / properties / timeout / description
        Previous value: -"Maximum wait time in milliseconds (default: 10000, or 0 when assert is true)"New value: +"Max wait in ms (default 10000; 0 when assert is true)"
      • changedInput schema / properties / url / description
        Previous value: -"Substring of the page URL — required when condition is 'url'"New value: +"Substring of the page URL; required for condition 'url'"
  3. 1 tool updatev2.10.1
    • Changedconfigure_session1 field changed
      • addedInput schema / properties / restart
        Added value: +{
        +  "description": "If true, restart Chrome with the new profile even if the browser is already running. Closes all current tabs.",
        +  "type": "boolean"
        +}
  4. 2 tool updatesv2.10.0
    • Changeddownload2 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"status: check/wait for pending downloads, list: show all session downloads"New value: +"status: check/wait for pending downloads (waits briefly for one to start, then until it finishes). list: full session history, returns immediately and never waits — use it for polling loops."
      • addedInput schema / properties / settle
        Added value: +{
        +  "default": 250,
        +  "description": "Grace window in ms to wait for a download to START before reporting 'no downloads' (default: 250). Chrome fires downloadWillBegin a few ms after the click that triggers it. Set 0 for an instant check, or use action: 'list' which never waits.",
        +  "type": "number"
        +}
    • Changedwait_for7 fields changed
      • addedInput schema / properties / assert
        Added value: +{
        +  "default": false,
        +  "description": "Assert instead of wait: check the condition once and fail if it does not hold (default timeout becomes 0). Failures carry _meta.code = 'assertion_failed'.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / condition / description
        Previous value: -"What to wait for: element visibility, network idle, or JS expression returning true"New value: +"What to wait for: element visibility, visible page text, the URL, network idle, or a JS expression returning true"
      • changedInput schema / properties / condition / enum
        Previous value: -[
        -  "element",
        -  "network_idle",
        -  "js"
        -]New value: +[
        +  "element",
        +  "text",
        +  "url",
        +  "network_idle",
        +  "js"
        +]
      • addedInput schema / properties / text
        Added value: +{
        +  "description": "Substring of the page's visible text — required when condition is 'text'. Case-sensitive; matches document.body.innerText, i.e. what a reader sees.",
        +  "type": "string"
        +}
      • removedInput schema / properties / timeout / default
        Removed value: -10000
      • changedInput schema / properties / timeout / description
        Previous value: -"Maximum wait time in milliseconds (default: 10000)"New value: +"Maximum wait time in milliseconds (default: 10000, or 0 when assert is true)"
      • addedInput schema / properties / url
        Added value: +{
        +  "description": "Substring of the page URL — required when condition is 'url'",
        +  "type": "string"
        +}
  5. 25 tool updatesv2.7.0
    • First observedbatch_evaluate
    • First observedcapture_image
    • First observedclick
    • First observedconfigure_session
    • First observedconsole_logs
    • First observeddom_snapshot
    • First observeddownload
    • First observeddrag
    • First observedevaluate
    • First observedfile_upload
    • First observedfill_form
    • First observedhandle_dialog
    • First observednavigate
    • First observednetwork_monitor
    • First observedobserve
    • First observedpress_key
    • First observedrun_plan
    • First observedscroll
    • First observedset_page_data
    • First observedswitch_tab
    • First observedtab_status
    • First observedtype
    • First observedview_page
    • First observedvirtual_desk
    • First observedwait_for

TDQS

A4.1/5.0

Scored across 25 tools

Disambiguation5/5

Each tool has a clear, singular purpose with minimal overlap. view_page and dom_snapshot are distinct (text/refs vs layout/geometry), and capture_image is explicitly limited to pixel-level content. The descriptions reinforce boundaries, so an agent should rarely misselect.

Naming Consistency4/5

The majority follow a verb_noun pattern (view_page, file_upload, fill_form, switch_tab), but there are single verbs (type, click, scroll, evaluate) and noun-only names (virtual_desk, console_logs). The consistent snake_case and predictable verb-first style keep it readable, though not perfectly uniform.

Tool Count4/5

At 25 tools, this is on the heavy end of the appropriate range. However, each tool addresses a distinct browser automation capability (navigation, interaction, waiting, dialogs, downloads, JS execution, network, etc.), so the count is justified by the breadth of the domain.

Completeness5/5

The surface is remarkably complete for browser automation: navigation, tab management, clicking, typing, forms, drag-and-drop, scrolling, waiting, dialogs, downloads, screenshots, DOM inspection, JS execution, network monitoring, console logs, file upload, session config, and batch evaluation. No critical gaps are apparent.

Maintenance

ActivityMaintained
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A token-optimized MCP server that groups Chrome Devtools tools into 8 semantic operations, reducing context window tokens by 69.5% while preserving full functionality.
    11 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    A zero-dependency MCP server that drives a real Chrome browser through a companion extension, enabling AI agents to automate real user sessions with trusted input events, compact accessibility-tree snapshots, and 14 tools for navigation, interaction, scripting, and inspection.
    418 npm
    MIT