browser-use-mcp
Drives Google Chrome through the Chrome DevTools Protocol, either by attaching to the user's existing Chrome instance or launching a dedicated Chrome/Chromium browser with a managed profile. Exposes the browser to agents as an indexed action space, enabling page reading and guarded operations such as clicking, typing, selecting, scrolling, and waiting.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@browser-use-mcpopen news.ycombinator.com and click the top story"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@ricardochen1996/browser-use-mcp
English · 中文
An MCP server that gives any agent a browser it can see and click: a page is read as an indexed action-space table, and every step is one guarded operation on a target that came from that table.
browser-use-mcp is a Model Context Protocol server. Claude
Code, Claude Desktop, Cursor, Codex, a DeepSeek Harness conversation, or any other MCP client
connects to it, and its agent gets a browser: open a page, read the page as a table of numbered
targets, then name one operation on one target. The model never writes a selector, a coordinate, or
a script, and the server never retries a change by itself.
It is built on dsh-browser-use: the same
Python engine, the same sidecar protocol, the same page-as-indexed-action-space idea — with the
DeepSeek Harness plugin host replaced by an MCP host. So there is no browser logic of its own: this
package is the adapter that lets any MCP client drive the Jev Ultrafast
engine. The engine and browser-harness ship as a
pinned wheel plus hash-pinned requirements, and are installed into a managed Python environment on
first use. No engine clone, no manual Python setup.
What it is
The browser tools expose one loop:
Read.
browser_opendownloads the page and returns its action space as text: one line per reachable element, the operations that element supports, and the exact target indices those operations accept. The result carries an observation number.Choose. The agent picks one operation and one target from that observation:
browser_act({ observation, operation, target }).Guard. The server sends the operation together with the fingerprint of the observation it came from. The engine re-reads the page first. If the page changed, nothing is executed and the result starts with
Nothing was executed:followed by the fresh table. That is a refusal, not an error: the agent chooses again from something that is true.Verify. If the page matched, the one operation runs and the server returns the new observation. The next step refers to the new number. A tool result is never proof of an outcome; the next observation is.
Here is a real table, produced by the bundled test fixture (the visible-text line is clipped):
Page file:///…/test/fixtures/fixture.html — 🐴 Forma · Find a place to slow down
Observation 1. Targets below are valid only for this observation.
Visible text: forma. Find a stay Reading room PLACES WITH CHARACTER THE SLOWER WAY TO STAY …
[1] link forma. — CLICK
[2] link Find a stay — CLICK
[3] link Reading room — CLICK
[4] searchbox Destination — TYPE_TEXT, CLICK
[5] button Find stays — CLICK
[6] combobox Stay category · "All stays" — SELECT
6:1 → Stay category → Design
6:2 → Stay category → Nature
6:3 → Stay category → Coastal
[7] checkbox Free cancellation · "on" (not checked) — CLICK
[8] button View Casa Flora — CLICK
[9] button View The Glasshouse — CLICK
[10] button View Serra Lodge — CLICK
Without a target: SCROLL_DOWN, WAITA step against it is one object, not code:
{ "observation": 1, "operation": "CLICK", "target": "5" } // button Find stays
{ "observation": 1, "operation": "TYPE_TEXT", "target": "4", "text": "Lisbon" }
{ "observation": 1, "operation": "SELECT", "target": "6:1" } // Stay category → DesignIf the page moved between the read and the click, the answer is a refusal that hands back the current observation:
Nothing was executed: The page changed since that observation. Choose from the new one.
Page … — …
Observation 3. Targets below are valid only for this observation.The engine reads a page over a single CDP connection that this server's session owns. Every tool —
browser_eval included — uses that one connection. Starting a second client yourself (a raw
websocket, a puppeteer/playwright script, another browser-harness daemon) is how Chrome 144+'s
per-connection "Allow remote debugging?" prompt turns into a stack of prompts, and how two loops end
up driving one page.
Related MCP server: jev-ultrafast-mcp
How it differs from selector-based browser MCPs
Selector-based browser MCP |
| |
What the model sends | A selector, a coordinate, or code | One operation on an index from the table it just read |
What happens when the page moved | The selector may miss, or hit the wrong element | The action is refused, and the fresh table is returned |
Retries | Up to the client or the model | Never — a refusal is the retry point, not an automatic replay |
What the model knows is possible | It has to guess | Each line lists that element's targets and operations, dropdown options included |
CDP connections | Depends on the implementation | Exactly one per browser session, shared by every tool |
Token cost per step | Often a screenshot or the whole DOM | Structured text: visible text plus the element table |
browser_eval is the one deliberate exception: it evaluates a single JavaScript expression over
the same connection, returns a value as data, and produces no observation — so the next guarded
action still refers to the last table. It exists so a model that needs a programmatic read does not
start a second CDP client. --no-eval withdraws it.
Quick start
Requires Node.js ^22.19.0 || >=24.0.0, Chrome or Chromium, and a way to get Python 3.12+:
either python3.12–python3.14, python3 (3.12 or newer) on PATH, or uv,
which can fetch the Python itself.
Which browser it drives. The default mode is attach: the server drives the Chrome you already
use, so its logins and tabs are the ones the agent sees. Enable remote debugging once in that Chrome
(chrome://inspect/#remote-debugging → allow); on macOS the application that runs the server also
needs Full Disk Access (System Settings → Privacy & Security → Full Disk Access). Prefer an
isolated browser? Pass --mode launch and the server starts its own Chrome with a profile under the
state root instead.
The package is not published to npm yet — run it from a checkout:
cd /Users/ricardo.chen/PersonalGit/browser-use-mcp
npm installnpm link in the checkout additionally puts a browser-use-mcp command on your PATH for terminal
use; GUI clients are more reliable with the absolute path shown below.
Then check the machine (on a fresh machine the first browser call builds the environment;
install does it eagerly). Its output looks like this, with machine-specific paths shortened:
The example below asks for a dedicated browser (--mode launch); with the default attach mode the
Connection: line names the Chrome you browse with instead, and Status: ready requires that
browser to answer its debugging endpoint.
node /Users/ricardo.chen/PersonalGit/browser-use-mcp/bin/browser-use-mcp.mjs doctor --mode launchbrowser-use-mcp v0.1.0 — self-check (Node v22.23.2, darwin-arm64)
Transport: stdio (MCP over stdin/stdout)
Connection: launch Chrome/Chromium, profile "default" at /Users/you/.browser-use-mcp/profiles/default
State : /Users/you/.browser-use-mcp (managed Python environment)
Timeouts: 180s per browser request
Logging : info (stderr)
Jev : on, typesafe decider: default endpoint (model default), key unset; …
Tools : browser_open, browser_page, browser_act, browser_eval, browser_screenshot, browser_console, browser_goal, browser_close, browser_doctor
Session : idle (this transport has no browser session yet)
Engine : /Users/you/.browser-use-mcp/env/bin/python (Python 3.12.14, chosen by managed environment)
jev_ultrafast 0.1.1 at …/env/lib/python3.12/site-packages/jev_ultrafast
browser-harness 0.1.13
Install : none in this process; the engine is already importable
Sidecar : /…/browser-use-mcp/sidecar/bridge.py
Browser : /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
Goal mode: enabled: browser_goal and browser_act intents spend model quota; …
Status : readyStatus: ready means the browser tools work. Jev … NOT READY is expected until a policy key is
configured: only browser_goal and browser_act's intent need one; everything else works.
To build the environment ahead of time instead of during the first browser call:
node /Users/ricardo.chen/PersonalGit/browser-use-mcp/bin/browser-use-mcp.mjs installThat creates <state root>/env (default ~/.browser-use-mcp/env) with the bundled engine wheel and
browser-harness, and records what it built in <state root>/install.json. It is safe to re-run.
A browser tool that runs before the install finished waits for it instead of failing.
Put it in front of your agent
Every absolute path below is this checkout's — substitute where you cloned it.
Claude Code:
# Use the Chrome you already browse with (the default attach mode):
claude mcp add --scope user browser-use \
-- node /Users/ricardo.chen/PersonalGit/browser-use-mcp/bin/browser-use-mcp.mjs
# Or give the agent a dedicated, unseen browser instead:
claude mcp add --scope user browser-use \
--env BROWSER_USE_MCP_MODE=launch --env BROWSER_USE_MCP_HEADLESS=1 \
-- node /Users/ricardo.chen/PersonalGit/browser-use-mcp/bin/browser-use-mcp.mjsGeneric mcpServers JSON (Claude Desktop, Cursor, and most hosts accept this shape):
{
"mcpServers": {
"browser-use": {
"command": "node",
"args": ["/Users/ricardo.chen/PersonalGit/browser-use-mcp/bin/browser-use-mcp.mjs"],
"env": { "BROWSER_USE_MCP_HEADLESS": "1" }
}
}
}Copy-pasteable snippets for Claude Code, Claude Desktop, Cursor, Codex, DSH and Streamable HTTP — including the absolute-path substitution rule — are in docs/clients.md.
Then ask the agent to browser_open a URL. A first call on a fresh machine may take a minute or two
while the environment is installed; later calls start at once.
The nine tools
Tool | Parameters | What it does |
|
| Start (or reuse) the session's browser, open the page, return the first action-space table and observation number. |
| — | Re-read the current page under a new observation number. Use it when the page changed on its own, or after a refusal. |
|
| Run one operation on one target from that observation: |
|
| Evaluate one JavaScript expression over the session's CDP connection; returns a value as data and produces no observation. Hidden by |
| — | The visible viewport as an MCP image (JPEG). Target boxes are not drawn into it. Hidden by |
| — | Console messages, uncaught exceptions, failed requests and |
|
| Hand one goal to the policy in its own tab; it chooses every operation and target itself. Spends model quota. Hidden by |
| — | Close the session's tab. In launch mode the browser stays for the next |
|
| Self-check: interpreter, engine, browser, attach target, config, live session, and a fix per problem. |
A missing policy key, an unsupported target, missing TYPE_TEXT text and a stale page are
refusals in normal tool results. A broken browser or environment is a failed call (isError).
An agent should report a failure, not retry it blindly.
Transports and concurrency
Transport | What one agent gets | What two agents get |
stdio (default) | One server process, one browser session | Two processes; in attach mode both drive the same browser in their own tabs, in launch mode two browsers |
Streamable HTTP | One MCP session, one browser session | One long-running server; attach mode shares the running browser, launch mode gives each session its own tab of a dedicated browser |
Start an HTTP endpoint when several agents should share one installed server:
node /Users/ricardo.chen/PersonalGit/browser-use-mcp/bin/browser-use-mcp.mjs \
--transport http --host 127.0.0.1 --port 7331 --path /mcpThe endpoint is http://127.0.0.1:7331/mcp by default. Each MCP initialize session gets its own
browser session; in the default attach mode all of them drive the Chrome you already use (see
--attach-sharing above), while --mode launch gives every session its own tab of a dedicated
browser. A session that goes quiet for 15 minutes has its browser closed and its anonymous profile
removed (BROWSER_USE_MCP_HTTP_IDLE_MS overrides the timeout). There is no authentication: the
default bind is loopback, and a non-loopback bind logs a warning at startup. Put it behind a tunnel
or proxy you control if it must leave the machine.
Named profiles (launch mode). In launch mode an anonymous HTTP session gets a throwaway profile that is deleted when the session ends. A client that wants its logins to survive reconnects names its profile on the initialize request:
http://127.0.0.1:7331/mcp?profile=claude (or header X-Browser-Use-Profile: claude)The named profile lives at <state root>/profiles/<server profile>-<name> and is kept when the
session ends; the next session with the same name reuses it. Different names run at the same time on
different profiles, and two live sessions with the same name are refused by the profile lock rather
than silently sharing one browser. Names are 1–32 characters from [A-Za-z0-9_-].
Two servers configured for the same browser profile are refused rather than silently sharing a
browser: the first holds a lock in the profile directory and the second gets a tool error naming
--profile and the fix. A lock left behind by a crashed process is reclaimed.
Which browser: attach (default) or launch
attach (default) drives the Chrome you already use:
With no endpoint, the server finds that browser through the
DevToolsActivePortChrome publishes in its profile. Turn remote debugging on in that Chrome first (chrome://inspect/#remote-debugging). On macOS that file is privacy-protected, so the application running this server needs Full Disk Access (System Settings → Privacy & Security → Full Disk Access), then must be restarted.--cdp-endpoint http://127.0.0.1:9333(or aws://…endpoint) drives a specific browser you started with remote debugging on.--attach-sharing shared(default) lets several MCP sessions — and several server processes — drive that browser at once, each in its own tab over its own CDP connection. Chrome 144+ asks "Allow remote debugging?" once per connection, so two agents mean at least two prompts; approve each once and never retry an unanswered one.--attach-sharing exclusiverefuses the second live holder instead, with a fix, which is the safer choice when two agents must never touch the same browser.browser_closestops the session's tab and daemon and never closes the attached browser.
--mode launch starts a dedicated Chrome with its own profile under the state root, so your
everyday browser and its logins stay out of automation. browser_close keeps that browser for the
next browser_open; the server closes it when the connection ends. It is the right mode for a
headless CI machine, or for an agent that should not see your real sessions.
Configuration highlights
Every server setting is a flag with an environment-variable twin (the policy uses
TYPESAFE_*/TEXT_MODEL_*; a couple of extras are environment-only), and the flag wins when both
are present. The exhaustive tables, parsing rules, defaults and exit codes are in
docs/config.md.
Setting | Flag(s) | Env | Default |
Transport |
|
|
|
Own or attach |
|
|
|
Attach sharing |
|
|
|
Browser window |
|
| visible window |
Profile |
|
|
|
State root |
|
|
|
Interpreter |
|
| managed environment |
Tool surface |
|
| all tools offered |
Policy |
|
|
|
Request budget |
|
|
|
Logging |
|
|
|
State root layout:
~/.browser-use-mcp/
├── env/ managed Python virtualenv (engine + browser-harness)
├── install.json what was installed, with which strategy and Python
├── profiles/<name>/ launched Chrome profiles (logins, tabs); named HTTP profiles too
└── attachments/<hash>.lock exclusive attach-mode claims, one per DevTools endpointThe Jev policy
browser_goal runs a whole decision loop inside the engine; browser_act with an intent (and no
operation) delegates exactly one decision to the same policy. Both spend model quota. Who answers
is configuration:
--jev-decider typesafe(default): Jev at the TypeSafe endpoint. NeedsTYPESAFE_API_KEY; optionalTYPESAFE_BASE_URL,TYPESAFE_MODEL,TYPESAFE_FALLBACK_URL.--jev-decider chat: the engine serves its System One protocol on loopback and a chat model answers it. NeedsTEXT_MODEL_BASE_URLandTEXT_MODEL(plusTEXT_MODEL_API_KEYif the endpoint needs one).
TEXT_MODEL_* is also what writes a TYPE_TEXT value when the agent gives an intent but no text.
Without either setup the rest of the tool surface is unchanged; the policy tools refuse with one
sentence naming the variable to set. --no-jev withdraws browser_goal and the intent parameter
entirely.
Put credentials in the client's env block (or the flags), never in args:
"env": {
"TYPESAFE_API_KEY": "…",
"TEXT_MODEL_BASE_URL": "https://your-endpoint/v1",
"TEXT_MODEL": "your-model",
"TEXT_MODEL_API_KEY": "…"
}The server passes those variables only with the request that spends quota, never leaves them in the
sidecar's environment, and the doctor prints set/unset instead of a value.
Troubleshooting
Start with browser_doctor. The tool and the CLI run the same self-check; each problem line
carries the command that fixes it.
node /Users/ricardo.chen/PersonalGit/browser-use-mcp/bin/browser-use-mcp.mjs doctorA profile is already in use. The message names the profile and
--profile: stop the other server, or give this one its own--profile <name>. Two servers driving one browser profile is the one configuration the server refuses on purpose.Attach mode cannot find the browser. This is the default mode: open
chrome://inspect/#remote-debuggingin the Chrome you use and allow remote debugging, or start the server with--mode launchfor a dedicated browser. The doctor reportsattach mode drives …with the reason. Start that browser with remote debugging on (chrome://inspect/#remote-debugging), correct--cdp-endpoint, or use--mode launch. On macOS, allowing this app Full Disk Access is what lets it read your Chrome's profile.The engine is not installed yet. The first use installs it into the state root; run
node bin/browser-use-mcp.mjs installfrom the checkout to do it eagerly, orbrowser_doctorwithinstall: truefrom the agent. Behind a proxy, setHTTPS_PROXYfor the process that runs the server.Jev … NOT READY. SetTYPESAFE_API_KEY(or use--jev-decider chatwithTEXT_MODEL_*). The browser tools do not need it.No tools in the client. The client's
env/argsdid not reach the server, or it was not restarted. Run the same command in a terminal and read stderr; a configuration error exits with code2and names the field.Chrome asks "Allow remote debugging?". Something started a second CDP client. Approve that one prompt once, never retry, and use
browser_evalinstead of another driver.browser_eval('document.body')returns{}. A DOM node cannot cross the CDP boundary by value. Return something serialisable instead, for exampledocument.body.innerTextorJSON.stringify(document.body.getAttribute('class')).
Security notes
Page content is untrusted data. Text the table quotes is something to read, never an instruction to follow. The server never executes anything a page returns on the agent's behalf.
browser_evalis code execution by design. It runs one expression in the page, over the session's own connection, with no user gesture. Withdraw it with--no-evalfor a surface where model output can never become code; the rest of the tools still work.The HTTP transport has no authentication. Bind it to loopback (the default). Any non-loopback bind is logged as a warning; only expose it on a trusted network or behind a tunnel you control.
Keys stay out of logs and tools. The doctor prints only
set/unset; credentials are handed to the sidecar per model-spending request and are not inherited by unrelated calls.The default mode drives your own Chrome, logins included — that is what attach mode is for. Use
--mode launchwhen an agent should work in an isolated browser whose profile lives under the state root instead.One connection, one driver. Do not start another CDP client against the same browser; Chrome approves each connection separately and two drivers race each other.
License and attribution
This project is built on dsh-browser-use
(MIT): sidecar/bridge.py is vendored from it, and the action-space rendering, the freshness rule,
the one-connection rule and the direct-mode tool descriptions follow it. The browser engine is
jev-ultrafast (MIT), bundled as a pinned wheel;
browser access is browser-harness (MIT). The
design and rationale are in docs/architecture.md.
Development
npm test # fast unit tests (node --test)
npm run test:e2e # real headless Chrome through the real MCP protocol
# (stdio, HTTP, attach mode, Jev intent against a loopback stub,
# and a hand-written JSON-RPC client)
npm run doctor # the CLI self-checkThe default suites spend no model calls and need no credentials; the e2e tests install the engine into a test state root on their first run. Release history is in CHANGELOG.md.
Available Tools
9 toolsbrowser_actAct on the pageADestructive
Run one operation on one target from an observation: CLICK, TYPE_TEXT, SELECT, SCROLL_UP, SCROLL_DOWN, WAIT. CLICK, TYPE_TEXT, and SELECT take a target index from that observation; TYPE_TEXT also takes the text. SCROLL_UP, SCROLL_DOWN, and WAIT take no target. You may instead omit operation and target and give an intent: the policy chooses the operation and target on that observation and runs it. A TYPE_TEXT with an intent and no text gets its value from the text model. Both spend model quota; --jev-decider says whether Jev or the text model answers.
| Name | Required | Description | Default |
|---|---|---|---|
| text | No | The value to enter when the operation is TYPE_TEXT. | |
| intent | No | What this one step should accomplish, in plain language. The policy chooses the step without an operation, and writes the TYPE_TEXT value without text. | |
| target | No | Target index from that observation, for example "4" or "6:2". Omit for SCROLL_*, WAIT. | |
| operation | No | One of CLICK, TYPE_TEXT, SELECT, SCROLL_UP, SCROLL_DOWN, WAIT. Omit it, with an intent, to let the policy choose. | |
| observation | Yes | The observation number whose table the target came from. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and openWorldHint=true, so the safety profile is covered. The description adds meaningful behavior beyond annotations by disclosing that intent mode spends model quota and that --jev-decider controls whether Jev or the text model answers. It still does not describe side effects or what the operation returns, but it adds useful operational context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loads the operation list before explaining target and intent modes. Every sentence contributes, though the final sentence is somewhat cryptic with 'Both' and '--jev-decider' requiring outside context.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a five-parameter action tool with no output schema, the definition explains operation modes and target handling adequately. It does not explain how an observation is obtained, what the tool returns after acting, or how errors are surfaced, which leaves meaningful gaps for an agent planning a browser interaction sequence.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already documents text, intent, target, operation, and observation individually. The description mostly restates those semantics, reinforcing that CLICK/TYPE_TEXT/SELECT take a target and SCROLL/WAIT do not, but it adds little beyond the schema itself. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: run one operation on one target from an observation, with an explicit list of supported operations. It is clear what the tool does, but it does not differentiate itself from siblings such as browser_eval or browser_goal, leaving the agent to infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly explains the two modes: explicit operation plus target, or omit both and provide an intent for the policy to choose. It also specifies which operations need a target and that TYPE_TEXT may take text. However, it gives no guidance on when to use this tool instead of sibling tools like browser_eval or browser_page.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browser_closeClose the tabADestructive
Close the tab this session owns and stop its daemon. The browser this session attached to is never closed: other sessions may still be driving it.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The destructiveHint annotation only flags that something is destroyed; the description specifies exactly what (the owned tab and its daemon) and, crucially, what is spared (the shared browser other sessions may still be driving). That is context annotations cannot convey and it prevents a damaging misread of blast radius.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no filler. The action comes first and the safety boundary second, which is the right order for a destructive operation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, but for a zero-parameter teardown call there is nothing to return and nothing to configure. The description covers the action, the scope, and the side effect on the shared browser, which is everything an agent needs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate. Baseline 4 applies; no parameter claims are made or needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (close the tab, stop its daemon) plus the ownership scope ('this session owns'), which cleanly separates it from sibling session-management tools like browser_open. An agent can pick this without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The sentence 'The browser this session attached to is never closed' implicitly tells the agent when this is appropriate: per-session tab teardown, not full browser shutdown. It doesn't explicitly name an alternative for closing the whole browser, so it stops short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browser_consoleRead page diagnosticsARead-only
Read what the page reported since the last read: console messages, uncaught exceptions, and failed or 4xx/5xx requests. Each read drains the buffer, so read it after reproducing a problem.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses the critical, non-obvious trait that each read *drains* the buffer, which shapes call ordering in a way annotations (readOnlyHint, openWorldHint) cannot express. That single sentence prevents a real failure mode (losing diagnostics by reading too early).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly written sentences with no filler; the scope of returned data is front-loaded ahead of the drain warning and usage hint.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-param, no-output-schema tool, the description covers what comes back and how to time the call. It does not describe the shape/volume of the returned payload (e.g., truncation or ordering), a minor remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters and the schema is trivially complete, so the baseline is high. The description adds no parameter detail because there is none to add.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb (read) plus a precise enumeration of the resource: console messages, uncaught exceptions, and failed/4xx-5xx requests. An agent can immediately distinguish it from browser_page (DOM content) or browser_eval (executes code).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Read it after reproducing a problem" gives a clear timing condition for invocation. It stops short of naming an alternative or exclusion (e.g., when to prefer browser_doctor for environment diagnostics), so it isn't fully explicit routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browser_doctorSelf-checkA
Report what the browser tools need and whether it is there: the Python interpreter, the engine and browser-harness versions, the managed environment, the browser that would be launched, the attach target, the configuration in use, and the fix for anything missing. Call it first when browser work fails. With install: true it builds a missing engine environment first and waits for that install.
| Name | Required | Description | Default |
|---|---|---|---|
| install | No | Build the browser engine environment now if it is missing, or retry a failed install; the call returns when the install is done. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false, and the description justifies that by explaining that install: true builds a missing engine environment and waits for completion. It also discloses that the tool reports fixes for missing items. It does not cover permissions or possible side effects beyond the install, but the key mutation behavior is clear.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core reporting purpose, then gives the failure scenario and the install exception. The long enumeration is useful but makes the first sentence dense; overall it is efficient with no irrelevant content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by listing the diagnostic categories the tool reports, including versions, environment, browser, attach target, configuration, and fixes. Combined with the install behavior and failure-triage guidance, an agent has enough context to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already explains that install builds a missing environment or retries a failed install and returns when done. The description reinforces this but adds no meaningful syntax or semantic detail beyond the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific diagnostic verb and resource: report what the browser tools need and whether it is present, enumerating interpreter, engine, harness, environment, browser, attach target, config, and fixes. This clearly distinguishes browser_doctor from action siblings like browser_act or browser_open, which perform browser work rather than diagnose the setup.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit usage rule: 'Call it first when browser work fails.' That is strong guidance for the primary scenario. It does not state when not to use it or name alternatives, but no sibling appears to serve the same diagnostic role.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browser_evalEvaluate JavaScriptADestructive
Evaluate one JavaScript expression in the page this session owns and return what it produced: a string as itself, any other value as JSON, capped so one dump cannot crowd out the conversation. Use it for a programmatic read the indexed action space cannot express, or a one-off DOM operation. It runs on this session's one CDP connection: never start a second client — a raw websocket, a puppeteer/playwright script, another browser-harness daemon — because Chrome 144+ asks "Allow remote debugging?" once per connection and two drivers race each other on the same page. The expression gets no user gesture, a thrown error comes back as text, and it produces no observation: call browser_page before the next browser_act.
| Name | Required | Description | Default |
|---|---|---|---|
| expression | Yes | The JavaScript expression to evaluate in the page. | |
| timeout_ms | No | How long an awaited promise may take, in milliseconds. Default 30000, at most 120000. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond the annotations: output is capped, a thrown error returns as text, there is no user gesture, the call produces no observation, and browser_page must be called before the next browser_act. The single-CDP-connection constraint and the Chrome 144+ permission race are exactly the kind of operational hazard annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and return shape are front-loaded in the first sentence, and the operational warnings follow. Sentences are dense and long, but each carries distinct information; a slightly tighter phrasing of the second-driver warning would help.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return contract itself (string as-is, other values as JSON, capped) and explains state sequencing after the call. For a tool annotated destructive and open-world, the side-effect and session constraints are fully covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both expression and timeout_ms are already documented, and the description does not restate or extend timeout semantics. It adds related context (no user gesture, error-as-text) but nothing about parameter formats beyond the schema, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (evaluate) plus resource (one JavaScript expression in the page this session owns) and defines the return contract. It is clearly distinguishable from browser_act ('the indexed action space') and browser_page, so an agent can pick it 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete selection criteria: use for a programmatic read the indexed action space cannot express, or a one-off DOM operation. It also warns against starting a second CDP client. It stops short of explicit 'do not use this for X, use browser_act instead' routing, but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browser_goalRun a goalADestructive
Hand one goal to the browser policy in its own tab and return the trace it produced. The policy chooses operations and targets by itself and spends model quota — --jev-decider says whether Jev or the text model answers; the page this session already has open is left untouched.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Page the run starts from. | |
| goal | Yes | What must be true when the run finishes. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial behavior beyond the annotations: the policy selects operations and targets autonomously, it consumes model quota, it runs isolated in a new tab, and the call returns a trace. Annotations already flag destructiveHint/openWorldHint, so the description does not need to repeat them, though it never warns that the autonomous policy may take irreversible web actions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with what the tool does and what it returns, with no padding. The trailing clause about --jev-decider is poorly anchored and slightly muddies an otherwise tight description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description correctly says a trace is returned, and it covers autonomy, quota cost, and isolation — the key facts for a non-idempotent, destructive open-world tool. It could still say more about failure/timeout behavior or how long a run may take.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters (url, goal) are already documented in the schema, so the description adds no new parameter meaning. The mention of '--jev-decider' is not a schema parameter, which risks confusing an agent into trying to pass it as an argument.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resource: it runs one goal through the browser policy in its own tab and returns the resulting trace. The phrase 'in its own tab' and 'the page this session already has open is left untouched' distinguishes it implicitly from browser_act, though it never names a sibling outright.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The isolation detail ('left untouched') and quota cost imply this is for autonomous, self-contained multi-step goals rather than direct page manipulation. However, there is no explicit 'use this instead of browser_act when…' statement or exclusion, leaving the agent to infer the boundary with browser_act and browser_page.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browser_openOpen a pageA
Open a page in the browser this MCP session owns and return its indexed action space. The session reuses that browser and its logins across calls; the previous tab is closed first.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Absolute page address (http(s); file:// works for local pages). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare openWorldHint, idempotentHint=false and destructiveHint=false, but the description adds details absent from them: the browser and its logins persist across calls, and the prior tab is closed on each open. That tab-closing side effect is exactly the kind of state change an agent needs to know. It stops short of 5 by not covering navigation failures, popups, or auth prompts.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with zero filler. The key constraints (session ownership and tab replacement) are front-loaded, and the return value is stated in the same breath.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully explains that the call returns an indexed action space, and it covers the session-persistence model. Remaining gaps (error behavior, how the indexed action space is consumed) are minor for a one-parameter tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is a single parameter with 100% schema coverage, and the schema already documents that it must be an absolute http(s) or file:// address. The description adds no syntax or format guidance beyond that, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (open a page) plus scope: the browser owned by this MCP session. It clarifies what is returned (an indexed action space) so the agent knows this is not a pure navigation call. It does not explicitly differentiate itself from siblings like browser_page or browser_act, keeping it just short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by 'open a page' and 'the previous tab is closed first', which hints at a one-page-at-a-time workflow. However, it never says when to choose this over browser_page (inspect) or browser_act (interact), and gives no prerequisites or exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browser_pageRead the pageARead-only
Read the current page again: the same indexed action space under a new observation number. Use it after the page changes on its own, or after a refused action.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only and open-world, so the bar is lower, and the description adds real value: the call bumps an observation number while keeping the same indexed action space, meaning prior references stay valid. It doesn't explain what happens if nothing changed or whether any latency/limit applies.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with what it does, then when to use it. Every clause carries information; nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description supplies the essential return semantics (new observation number, same action space), which is what an agent most needs. Missing only edge-case behavior such as whether a re-read triggers navigation or waiting.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the baseline is 4. The description correctly frames this as a parameterless re-observation, and the empty schema is consistent with that.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete action and resource ('Read the current page again') and clarifies the result is the same indexed action space under a new observation number, which separates it from browser_act or browser_screenshot. It doesn't name those siblings explicitly, so the differentiation is implied rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two concrete triggers: the page changed on its own, or an action was refused. That is actionable guidance, though it doesn't state exclusions (e.g. use browser_screenshot when you need pixels, or browser_act when you want to act rather than observe).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
browser_screenshotScreenshot the viewportARead-only
Capture the visible viewport as an image. Target boxes and page structure are not drawn into it.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, so safety is covered. The description adds a genuinely useful behavioral detail (target boxes and page structure are not rendered), but says nothing about return format, timing, or whether it waits for the page to settle.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero waste, with the core action front-loaded and the caveat immediately after. Nothing redundant with the title or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless tool with a self-evident action and read-only annotations, the description covers what is captured. The only remaining gap is the return shape (image payload vs. reference), and with no output schema that would have been a useful addition.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the baseline is 4. The description still earns it by making clear the capture is viewport-only with no full-page or element-selector options, telling the agent not to look for such parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb and resource ('Capture the visible viewport as an image'), and the second sentence clarifies scope by excluding drawn overlays. It implicitly contrasts with browser_page (which surfaces page structure) but never names a sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: the note that page structure isn't drawn in hints that browser_page should be used for structural/textual inspection, but there is no explicit when-to-use or when-not-to-use guidance among the eight siblings.
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.
9 tool updates
v0.1.0- First observed
browser_act - First observed
browser_close - First observed
browser_console - First observed
browser_doctor - First observed
browser_eval - First observed
browser_goal - First observed
browser_open - First observed
browser_page - First observed
browser_screenshot
TDQS
Scored across 9 tools
Tools mostly target distinct browser operations: open, re-observe, act, evaluate, screenshot, console, goal, close, and doctor are separable. The main overlap is browser_act's intent mode versus browser_goal, both policy-driven model actions, though goal is scoped to its own tab and an overall objective.
All tools use a consistent browser_ prefix, making them easy to recognize as one family. Suffixes mix nouns (browser_page, browser_screenshot, browser_console) and verbs (browser_open, browser_close, browser_act), which is a minor deviation but still readable and predictable.
Nine tools is well-scoped for a browser automation server. Each tool covers a meaningful part of the browser-control lifecycle without obvious redundancy or filler.
The surface covers opening, observing, acting, evaluating JavaScript, screenshotting, console inspection, autonomous goals, closing, and diagnostics. Minor gaps exist around explicit navigation/history controls such as back, forward, or direct URL navigation separate from browser_open, but core workflows are covered.
Maintenance
Related MCP Connectors
Real Chrome for agents: start a browser, read pages as numbered markdown, click, type, hand off.
Control a real browser from AI agents: navigate, click, type, screenshot, read the page.
Stealth web browser for agents: search, fetch, click, download and type in persistent MCP sessions.
Turn any webpage into a structured action manifest — clickable, fillable, submittable elements.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server for vision-first browser automation that annotates interactive elements with numbered markers, allowing AI agents to control any webpage by simply referencing element numbers.12MIT
- AlicenseAqualityAmaintenanceEnables AI agents to drive a Chromium browser over MCP, reading pages as a numbered list of controls and performing actions like clicking, filling forms, and asserting page state without screenshots or API keys.1076 PyPI20MIT
- AlicenseAqualityBmaintenanceEnables AI agents to drive a Chromium browser through MCP, with persistent sessions, compact page snapshots, guarded actions, batched operations, and explicit result verification.16MIT
- AlicenseAqualityBmaintenanceEnables coding agents to drive a real, indexed Chrome browser over MCP stdio, navigating pages, observing numbered interactive elements, and performing click, type, and select actions without the server itself calling any model.5MIT