aido-browser · the agent's web body

Give your agent a Chromium,
not your tabs.

aido-browser hands an LLM its own isolated Chromium over CDP, so it can see, click and type the frontend you're building · without ever touching your real browser, cookies, or tabs. 14 MCP tools, behind 7 security gates.

Anthropic computer-use drives your desktop. aido-vm drives a full VM. aido-browser sits in between: just a browser, just for frontend dev loops. CDP-fast, profile-clean, allowlist-gated.

14
MCP tools v1
7
security gates
5-20ms
per CDP call
~250MB
Chromium footprint
https://localhost:3000/login

Sign in

agent@x0ne.co
••••••••
Continue
profile · .aido/browser-profiles/<uuid> ● isolated
the missing primitive

Three ways to give an agent eyes.

Pick the smallest tool that does the job. For "test the localhost frontend I'm writing", that's aido-browser · not a full desktop, not a full VM.

Anthropic computer-use

drives your real desktop
  • sees everything
  • works with any app
  • can click your bank tab
  • no allowlist
  • no audit log

aido-browser

CDP · isolated profile
  • just a browser, just for dev
  • isolated profile per session
  • allowlist (localhost by default)
  • audit JSONL with redaction
  • rate limit + abort switch
  • 5-20 ms per call (CDP)

aido-vm

full QEMU/KVM body
  • KVM kernel isolation
  • snapshot in ~2 s
  • untrusted code OK
  • 256 MB+ per body
  • 30 s boot
security · 7 gates

Built so a runaway agent can't escape.

Every browser tool call passes through seven independent gates. Bypassing one is a footgun; bypassing all seven would require shipping code we explicitly refuse to write in v1.

01

Allowlist

Navigation refused outside the allowlist. Default: localhost, 127.0.0.1, *.x0ne.training, data:, about:. Returns the list so the agent can suggest an edit.

02

Isolated profile

Each session lives in ~/.aido/browser-profiles/<uuid>/, wiped at close. Your real Chrome cookies · invisible.

03

Audit log JSONL

One line per call in ~/.aido/logs/browser/<date>.jsonl. Args go through a redactor masking any key matching password|token|secret|api_key|auth|bearer.

04

Rate limit

Token bucket, default 60 calls/min. Past the limit → rate_limited with retry_after_ms.

05

Abort switch

touch ~/.aido/browser-tools.kill → every call returns killed_by_operator. Your emergency stop.

06

Size caps

Screenshot ≤ 5 MB, text payload ≤ 256 KB, DOM depth ≤ 8. Hard caps. Prevents 200 MB page dumps blowing your context.

07

v1 refusals

No eval_js (arbitrary scripts). No file upload. No multi-tab. Each is a categorical refusal · features that need explicit design before they ship.

·

Stable error codes

Every failure returns a typed code the agent branches on, not the message. 11 codes total; new ones get added, never renamed.

architecture

One MCP server, one Chromium, one job.

aido-browser is a separate workspace crate. Its only entry point is aido-browser-mcp, an MCP stdio server. Chromium is spawned lazily · the first call that needs it pays the ~250 ms startup; the rest are 5-20 ms.

┌── LLM (Claude · Codex · Cursor · aido-orchestrator) │ │ MCP / stdio (json-rpc) ▼ ┌──────────────────────────────────────────────────────────────┐ │ aido-browser-mcp │ │ ──────────────── │ │ ┌──────────────┐ ┌─────────────┐ ┌──────────────────┐ │ │ │ allowlist │ │ audit log │ │ rate limit │ │ │ │ origin check │ │ jsonl + r. │ │ token bucket │ │ │ └──────┬───────┘ └──────┬──────┘ └─────────┬────────┘ │ │ └──────────┬──────┘ │ │ │ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ ChromiumDriver (chromiumoxide · CDP) │ │ │ │ navigate · click · type · key · screenshot · read │ │ │ └────────────────────────────┬─────────────────────────┘ │ └────────────────────────────────┼─────────────────────────────┘ ▼ ┌────────────────────────┐ │ Chromium subprocess │ │ --user-data-dir= │ │ ~/.aido/browser- │ │ profiles/<uuid>/ │ └────────────────────────┘
# .mcp.json · minimum wiring { "mcpServers": { "aido-browser": { "command": "aido-browser-mcp" } } } # Stack all three bodies · agent picks the right one per task { "mcpServers": { "aido": { "command": "aido-runtime", "args": ["--mode","mcp"] }, "aido-vm": { "command": "aido-vm-mcp" }, "aido-browser": { "command": "aido-browser-mcp" } } }
tool catalog · 14 in v1

Just enough to see, drive and read a page.

No eval_js. No multi-tab. No file upload in v1. The toolset is deliberately small · fewer footguns, simpler reasoning. v2 features (hover, scroll, console logs, network log, tabs) ship when designed safely.

navigation
browser_navigate
Open a URL. Refused if outside allowlist (returns it for context).
navigation
browser_get_url
Current page URL · did the redirect land where expected?
navigation
browser_back / forward / reload
History ops. Same as browser buttons.
visual
browser_screenshot
PNG (default) or JPEG of viewport or full page. Base64.
input
browser_click
Click a CSS selector. Auto-waits up to default timeout.
input
browser_type
Type into an input. clear=true erases first.
input
browser_press_key
One keystroke: Enter, Tab, Escape, ArrowDown …
reading
browser_read_text
innerText, truncated at 256 KB.
reading
browser_read_dom
outerHTML, same cap. Structural reasoning.
waiting
browser_wait_for
Block until selector appears or timeout.
lifecycle
browser_close
Kill Chromium + wipe session profile.
diagnostics
browser_status
Rate-limit tokens, allowlist, kill-switch path. Always available.
error contract

Errors with codes, not just text.

Stable error codes are part of the API contract. The agent branches on code, not on the message · so wording improves without breaking flows. Codes are added in new versions, never renamed.

domain_not_allowedURL didn't match the allowlist.
selector_not_foundCSS / XPath / text matched nothing.
timeoutwait_for / navigation exceeded budget.
navigation_failedDNS / TLS / 5xx.
killed_by_operatorAbort switch file exists.
rate_limitedPer-minute budget consumed.
needs_confirmationCross-domain action · retry with force flag.
not_connectedChromium subprocess not running.
invalid_selectorMalformed selector syntax.
not_implementedv2 feature requested.
internalOther failure with detailed message.
quickstart

Three minutes to a safer browser agent.

Linux with Chromium installed · that's it. The crate spawns the system Chromium with an isolated --user-data-dir. Default config works; TOML only needed if you want to tighten or relax anything.

# 1. Build + install cargo build -p aido-browser --release install -m 755 target/release/aido-browser-mcp ~/.cargo/bin/ # 2. Wire into Claude Code (.mcp.json) { "mcpServers": { "aido-browser": { "command": "aido-browser-mcp" } } } # 3. (optional) tune via ~/.aido/browser-tools.toml # 4. Agent's typical flow: browser_navigate("http://localhost:3000") browser_screenshot({ full_page: true }) browser_click(".login-button") browser_type(".email", "agent@x.co") browser_press_key("Enter") browser_wait_for(".welcome", { timeout_ms: 5000 }) browser_read_text(".welcome h1") # 5. Emergency stop · pause without killing AIDO: touch ~/.aido/browser-tools.kill # 6. Tail what the agent actually did: tail -f ~/.aido/logs/browser/$(date +%F).jsonl | jq