toolBrowser Control6 min read
agent-browser
The browser tooling in most agent setups was built for test scripts and then handed to an agent, and the context bill is why teams quietly turn it off. This one was built for the agent. If you run the Browser Verification Loop and it feels too expensive to leave on, this is the fix.
Vercel Labs' Rust CLI that drives Chrome for coding agents. Accessibility snapshots with stable refs instead of DOM dumps, React fiber introspection, Core Web Vitals, an embedded axe-core audit, and isolated sessions so parallel agents each get their own browser. Works as a plain shell command, with an MCP server available when you want one.
Why It Exists
The Browser Verification Loop is the highest-leverage change a Frontend team can make to its agent setup, and it has one honest weakness: it is the most expensive thing you can put in a context window. A screenshot is expensive. A full accessibility dump is expensive. Do it three times in one task and the agent has spent more of its window looking at the page than reading your code, which is exactly the failure mode The Context Budget describes.
That cost is not inherent to browser automation. It is inherited. Playwright and Puppeteer were designed for test scripts, where a human wrote the selectors ahead of time and nothing had to be summarized for a reader with a token limit. Point an agent at that API and every observation arrives at the size a machine needed, not the size a model can afford.
agent-browser starts from the other end. The consumer is a language model with a budget, so the default output is a filtered accessibility tree where every element carries a short stable ref, and the agent acts on the ref rather than re-querying the page.
How the Refs Work
This is the whole design in four lines:
agent-browser open localhost:3000/pricingagent-browser snapshot -i # interactive elements only, each tagged @e1, @e2, ...agent-browser click @e2agent-browser fill @e3 "[email protected]"snapshot returns the page as structure, not pixels. -i limits it to interactive elements, -c compacts it, -d 3 caps the depth. The agent picks a ref and acts on it, so there is no second read of the DOM to resolve a selector and no vision model in the loop at all.
When it does need to find something by meaning rather than by ref, the semantic locators do it in one step:
agent-browser find role button click --name "Submit"agent-browser find label "Email" fill "[email protected]"The project does not publish a benchmark, and the comparison numbers circulating in blog posts are not ours to repeat. The mechanism is checkable on your own project in about a minute: run snapshot -i -c against the page you actually work on and compare it against what your current browser tooling hands back for the same page.
Setup
npm install -g agent-browseragent-browser install # pulls Chrome from Chrome for TestingThat is the entire setup for any agent that can run shell commands, which is all of them. Claude Code, Cursor, Copilot and Codex need no configuration, no MCP entry, and no restart. A Rust daemon holds the browser open between commands, so the agent is not paying browser startup on every step, and batch groups a sequence into one invocation:
agent-browser batch "open localhost:3000" "snapshot -i" "screenshot out.png"If you would rather expose it as tools than as a command, there is an MCP server with selectable profiles so you only load the surface you need:
{ "mcpServers": { "agent-browser": { "command": "agent-browser", "args": ["mcp", "--tools", "core,react,network"] } }}Profiles are core, network, state, debug, tabs, react, mobile and all. Default is core. Reach for all only when you know why, because every tool you load is schema you pay for on every turn.
The Part That Is Actually Frontend
Most browser automation stops at "click this, read that". These are the commands that make it worth a React team's attention:
- React fiber introspection.
react treereturns the component tree,react inspect <fiberId>returns props, hooks, state and source location, andreact renders start|stoprecords what re-rendered. The agent stops guessing which component owns a bug from the JSX and reads the running app instead. Launch with--enable react-devtoolsor thereactcommands error. Works on React, Next.js, Remix, Vite and TanStack Start. - Core Web Vitals as a command.
vitalsreports LCP, CLS, TTFB, FCP and INP plus a hydration summary. That turns "this feels slow" into a number the agent can move and then re-measure, which is the only form of performance work an agent can honestly close. - Accessibility with a real engine.
a11yruns an embedded axe-core audit, offline and under strict CSP, and takes--tags wcag2a,wcag2aaand--selector. Accessibility becomes something the agent checks on the change it just made rather than something a consultant finds in a quarterly audit. - Network control.
network route <url> --body <json>mocks a response,--abortblocks it, andnetwork har start|stoprecords the session. The agent can drive an error state or an empty state without you standing up a fixture. - Diffing built in.
diff snapshot --baselinecompares accessibility trees,diff screenshot --baseline b.png -t <threshold>does a pixel diff, anddiff url <a> <b>compares two deployments. This repo already runs pixelmatch scripts for exactly that; the difference is that the agent can now run the comparison itself, mid-task, instead of waiting for CI to tell it.
One Browser per Worktree
Sessions are fully isolated, each with its own browser instance, cookies, storage and auth state:
agent-browser --session agent1 open localhost:3000agent-browser --session agent2 open localhost:3001agent-browser session id --scope worktree --prefix myapp--scope worktree is the line that matters. If you run parallel agents out of git worktrees, each one derives its own session id from its own worktree, so two agents verifying two branches never share a cookie jar or fight over a tab. Parallel execution stops being a thing you do to the editor and becomes a thing the browser understands too.
Keeping It Safe
An agent with a browser is an agent that can read anything you are logged into, so the guardrails are worth setting on day one. They are all opt-in, which means they are all off until you turn them on:
--allowed-domains "localhost,*.staging.example.com"keeps the loop pointed at your own surfaces.--confirm-actions eval,downloadgates the two verbs you do not want happening unattended.--max-output 50000stops one enormous page from eating the window.--content-boundariesmarks page content as page content, which matters because everything the browser returns is untrusted text that an agent is about to read as instructions.- State files encrypt at rest with AES-256-GCM when you set
AGENT_BROWSER_ENCRYPTION_KEY.
Point it at localhost or an isolated staging profile. Never at production with your own session cookies.
Where It Fits in the Stack
- It is the fourth way to wire the Browser Verification Loop,alongside Claude in Chrome, Chrome DevTools MCP and Playwright MCP. Those three are still the right answer for interactive debugging, deep performance traces and CI-grade Playwright specs. This is the one you leave on during ordinary feature work, because it is the one whose output you can afford.
- It gives the Agent-Readable Design System its gate.Committed context, a registry and component metadata all tell the agent what to build.
diff screenshotanda11yare how it finds out whether it did. - It runs unattended in Continuous AI.A CLI with a domain allowlist and an output cap is a much easier thing to justify inside a sandboxed CI job than a browser MCP server with the full DevTools surface attached.
- It changes the shape of the context bill, not just the size.Structure survives compaction as text. Screenshots do not survive anything.
When Not to Use It
- Non-visual work.A type fix or a server-side refactor gets nothing from a browser and pays for it anyway.
- As your regression suite.An agent comparing a snapshot to a baseline mid-task is a fast, fuzzy check that gets the change close. Deterministic pixel diffing and real Playwright specs still belong in CI, where a failure blocks a merge.
- Against anything you would not hand an agent outright.Everything the page returns is untrusted input. Treat a logged-in production tab as out of scope, not as a convenience.
- With
--tools allby reflex.The default profile exists because loading every tool surface reintroduces the exact cost this thing was built to remove.
