Auto-Browser
# Auto Browser
[](https://mcptoplist.com/server/glama%2FLvcidPsyche%2Fauto-browser)
[](https://github.com/LvcidPsyche/auto-browser/actions/workflows/ci.yml)
[](https://pypi.org/project/auto-browser-client/)
[](./LICENSE)
[](./README.md)
[](./README.md)
[](https://codespaces.new/LvcidPsyche/auto-browser?quickstart=1)
<a href="https://glama.ai/mcp/servers/LvcidPsyche/auto-browser">
<img src="https://glama.ai/mcp/servers/LvcidPsyche/auto-browser/badge" alt="Auto-Browser on Glama: grade A for license, quality, and maintenance" width="420">
</a>

> **Give your AI agent a real browser, with a human in the loop.**
Auto Browser is an MCP-native browser control plane for authorized workflows. It gives MCP clients, LLM agents, and operators a shared Playwright browser with human takeover, reusable auth profiles, approvals, audit trails, and local-first deployment.
Works with:
- Claude Code, Codex (CLI, IDE extension, and app), Google Antigravity, Cursor, and VS Code,
connected directly over HTTP
- Claude Desktop, through the bundled stdio bridge
- any MCP client that can talk HTTP or stdio
- direct REST callers when you want curl-first control
## Why Auto Browser
- **MCP-native from day one.** The browser surface is already packaged as an MCP server instead of bolted on after the fact.
- **Human takeover when the web gets brittle.** noVNC keeps the same live session available when a person needs to step in.
- **Login once, reuse later.** Save named auth profiles and reopen fresh sessions that are already signed in.
- **Local-first by default.** Run the full stack on your own box with Docker Compose, or use Codespaces for a quick hosted demo.
- **Safety rails built in.** Approvals, operator identity, PII scrubbing, Witness receipts, and policy presets are all part of the product surface.
- **Evidence you can hand to someone else.** Witness receipt chains are Ed25519-signed, and an exported bundle verifies with [`scripts/verify_witness_bundle.py`](./scripts/verify_witness_bundle.py) — which imports nothing from this project, so a recipient need not run or trust this controller to check it. The signing key sits beside the receipts, so for evidence that survives a compromised box, pin a head hash you recorded earlier or ship the receipts off the box ([details](./docs/deployment.md#witness-receipts-what-a-signature-proves)).
- **We audit ourselves in public.** [`docs/audits/2026-08-execution-audit.md`](./docs/audits/2026-08-execution-audit.md) documents an adversarial audit of this repo that found safety controls which reported success while doing nothing, with reproductions, the fixes, and the gates that close the class.
- **Governed skill induction.** Verified browser traces can become staged skill candidates with provenance that is signed when a mesh identity is configured — and checked on read, not just produced — plus verifier adapters and review-only graduation — agents that prove they can repeat themselves correctly, not just act once.
## Good Fits
- internal dashboards and admin tools
- operator-assisted QA and browser debugging
- login-once, reuse-later account workflows
- brittle sites where a human may need to recover the flow
- MCP-powered agent workflows that need a real browser, not just HTML fetches
## Not the Goal
- CAPTCHA solving
- unauthorized scraping or account automation
- deceptive identity shaping or bypass tooling
## What You Get
| Browser Control | Operator Safety | Deployment and Integration |
| --- | --- | --- |
| Playwright-backed sessions with screenshots, DOM summaries, OCR excerpts, tab controls, downloads, and network inspection | approval gates, operator identity headers, audit events, PII scrubbing, Witness receipts, and protection profiles | MCP over HTTP, bundled stdio bridge, REST API, Docker Compose, Codespaces, auth profiles, and optional per-session isolation |
## Quickstart
```bash
git clone https://github.com/LvcidPsyche/auto-browser.git
cd auto-browser
docker compose up --build
```
That is enough for local development with the default settings. On first start the controller
generates an API token and saves it to `data/api-token` in this folder (git ignores it), and
every API and MCP request must send it as `Authorization: Bearer <token>`, apart from the health
checks, `/version`, the dashboard page, share links and `/mesh/receive`. The dashboard asks
for it once, the examples below read it with `$(cat data/api-token)`, and the MCP clients take
it as shown under [MCP Clients](#mcp-clients). `API_ALLOW_TOKENLESS_LOOPBACK=true` runs the
controller without a token, as it ran before, but then any container on the compose network
can drive the browser (a write a browser sends from another web origin is refused).
Optional:
```bash
cp .env.example .env
make doctor
```
Run `make doctor` from a normal terminal with local Docker access and permission to open localhost sockets.
Open:
- API docs: `http://127.0.0.1:8000/docs` (Authorize with the token to try requests). The docs need no token on a loopback deployment (an exposed one answers 401), and Swagger UI loads its assets from cdn.jsdelivr.net.
- Operator dashboard: `http://127.0.0.1:8000/dashboard`, which includes the queue of actions waiting on your approval;
paste the token when it asks, or open `http://127.0.0.1:8000/dashboard#token=<token>`
- Visual takeover: `http://127.0.0.1:6080/vnc.html?autoconnect=true&resize=scale`
All published ports bind to `127.0.0.1` by default.
### Prebuilt images
Every release is also published to GHCR, so you can skip the build:
```bash
docker compose -f docker-compose.yml -f docker-compose.images.yml up -d
```
`AUTO_BROWSER_IMAGE_TAG` picks the version: `latest` (the default), a release such as `1.10.1`, or
`edge` for the current `main`. The images are linux/amd64, and each carries an SBOM and a build
provenance attestation (`gh attestation verify oci://ghcr.io/lvcidpsyche/auto-browser-controller:latest -R LvcidPsyche/auto-browser`).
The controller image leaves out the agent CLIs; for `*_AUTH_MODE=cli`, build from source with
`INSTALL_AGENT_CLIS=true`.
With isolated sessions, name `docker-compose.images.yml` last, so they start from the published browser image too:
```bash
docker compose -f docker-compose.yml -f docker-compose.isolation.yml -f docker-compose.images.yml up -d
```
In the other order, the isolation overlay's default wins, and isolated sessions run a local
`auto-browser-browser-node:latest`: a stale build, or no image at all.
## Try It in Codespaces
[](https://codespaces.new/LvcidPsyche/auto-browser?quickstart=1)
Codespaces provisions the stack automatically. The dashboard and noVNC tabs are ready once both images have built and started; the first start builds them from source.
## First Useful Demo
The highest-signal flow in this repo is:
1. create a session
2. log in manually if the site needs a human
3. save the session as a named auth profile
4. open a new session from that auth profile
5. continue work without reauthing
Start here:
- [`examples/login-and-save-profile.md`](./examples/login-and-save-profile.md)
- [`examples/README.md`](./examples/README.md)
Minimal session creation, from the auto-browser folder (`AUTH` carries the token; if you
set `API_BEARER_TOKEN`, use `$API_BEARER_TOKEN` in place of `$(cat data/api-token)`):
```bash
AUTH=(-H "Authorization: Bearer $(cat data/api-token)")
curl -s "${AUTH[@]}" http://127.0.0.1:8000/sessions \
-X POST \
-H 'content-type: application/json' \
-d '{"name":"demo","start_url":"https://example.com"}' | jq
```
Minimal observation:
```bash
curl -s "${AUTH[@]}" http://127.0.0.1:8000/sessions/<session-id>/observe | jq
```
## Recent Changes
See [CHANGELOG.md](./CHANGELOG.md) for the full release history.
## MCP Clients
Auto Browser exposes:
- an HTTP MCP endpoint at `http://127.0.0.1:8000/mcp`
- convenience endpoints at `http://127.0.0.1:8000/mcp/tools` and `http://127.0.0.1:8000/mcp/tools/call`
- a stdio bridge: `uvx auto-browser-mcp` from PyPI, or [`scripts/mcp_stdio_bridge.py`](./scripts/mcp_stdio_bridge.py) in a repo checkout
Clients that speak MCP over HTTP connect directly, sending the controller's token. With Claude
Code or Codex, from the auto-browser folder (`--scope user` makes the server available in every
project, not only this folder):
```bash
claude mcp add --transport http --scope user auto-browser http://127.0.0.1:8000/mcp \
--header "Authorization: Bearer $(cat data/api-token)"
# Codex reads the token from the variable you name: export it wherever Codex runs.
export AUTO_BROWSER_TOKEN="$(cat data/api-token)"
codex mcp add auto-browser --url http://127.0.0.1:8000/mcp --bearer-token-env-var AUTO_BROWSER_TOKEN
```
Google Antigravity takes the server in its raw MCP config (MCP Servers, then Manage MCP
Servers, then View raw config), or in `~/.gemini/config/mcp_config.json`. It reads
`serverUrl`, not `url`, and the token goes in `headers`:
```json
{
"mcpServers": {
"auto-browser": {
"serverUrl": "http://127.0.0.1:8000/mcp",
"headers": { "Authorization": "Bearer <contents of data/api-token>" }
}
}
}
```
Cursor, VS Code, Claude Desktop through the stdio bridge (which reads the file named by
`AUTO_BROWSER_TOKEN_FILE` / `--token-file`; the repo wrapper, `make stdio-bridge` or
`scripts/mcp_stdio_bridge.py`, defaults that to its checkout's `data/api-token` for a loopback
URL), and pairing Auto Browser with a web-search MCP server are covered in
[`docs/mcp-clients.md`](./docs/mcp-clients.md).
The default MCP tool profile is `curated`: the 20 tools (21 with a vision model) a browsing agent needs (sessions, observe and screenshot, `execute_action`, page and download reading, tabs, auth profiles, `wait_for_selector`, `eval_js`, human takeover). Every listed tool costs the model context on every request, so the diagnostics, audit, harness, and admin tools are in the full profile. To expose everything, set:
```bash
MCP_TOOL_PROFILE=full
```
`MCP_TOOL_PROFILE=lean` serves just the 7-tool browsing loop (observe, screenshot, `execute_action`, `get_html`, `find_elements`, human takeover, close), and `MCP_TOOL_PACKS` adds or removes named packs on top of any profile, e.g. `MCP_TOOL_PACKS=tabs,downloads` or `-scripting`. The pack table is in [`docs/mcp-clients.md`](./docs/mcp-clients.md#mcp-tool-profiles-and-packs).
Raw tool-call example:
```bash
curl -s "${AUTH[@]}" http://127.0.0.1:8000/mcp/tools/call \
-X POST \
-H 'content-type: application/json' \
-d '{
"name":"browser.create_session",
"arguments":{
"name":"demo",
"start_url":"https://example.com"
}
}' | jq
```
Client setup guides:
- [`docs/mcp-clients.md`](./docs/mcp-clients.md)
- [`examples/claude-desktop-setup.md`](./examples/claude-desktop-setup.md)
- [`examples/cursor-mcp-setup.md`](./examples/cursor-mcp-setup.md)
- [`examples/claude_desktop_config.json`](./examples/claude_desktop_config.json)
For resource listing, resource reads, and subscription-style update examples,
see [`docs/mcp-clients.md#resources-and-subscriptions`](./docs/mcp-clients.md#resources-and-subscriptions).
## Convergence Harness
Auto Browser ships a Stage 0 convergence harness for Agent Skill Induction. It runs a structured task contract, records tamper-checked traces, verifies completion, and writes a staged skill candidate carrying provenance. With a mesh identity configured that provenance is signed, and the registry verifies the signature before serving a candidate — a candidate that fails the check, or that was dropped into the staging directory unsigned, is refused. Candidates induced from a mock run are marked `simulated` so they cannot pass as converged. Generated skills are staged only — promotion stays explicit and reviewed.
The harness tools — convergence runs, run status and traces, drift checks, candidate management, and graduation — are in the `full` MCP tool profile (`MCP_TOOL_PROFILE=full`); over REST, `POST /mcp/tools/call` calls them once the controller serves them.
Start with [`docs/convergence-harness.md`](./docs/convergence-harness.md). A deterministic local smoke is:
```bash
python -m controller.harness.run --contract evals/contracts/example_read.json --mock-final-url https://example.com --mock-final-text "Example Domain"
```
For MCP clients, set `MCP_TOOL_PROFILE=full` (or `MCP_TOOL_PACKS=harness` on top of curated) to expose the `harness.*` tools.
## Security and Compliance
For a real private deployment, set at least:
```bash
APP_ENV=production
API_BIND_SCOPE=exposed
API_BEARER_TOKEN=<strong-random-secret>
REQUIRE_OPERATOR_ID=true
AUTH_STATE_ENCRYPTION_KEY=<44-char-fernet-key>
REQUIRE_AUTH_STATE_ENCRYPTION=true
SHARE_TOKEN_SECRET=<strong-random-secret>
CONTROLLER_ALLOWED_HOSTS=<controller hostname>
ALLOWED_HOSTS=<sites the browser may visit>
REQUEST_RATE_LIMIT_ENABLED=true
METRICS_ENABLED=true
STEALTH_ENABLED=false
```
With `APP_ENV=production` the controller refuses to start while the token, `REQUIRE_OPERATOR_ID`, either encryption setting, `SHARE_TOKEN_SECRET` or `CONTROLLER_ALLOWED_HOSTS` is missing, rate limiting is off, or `ALLOWED_HOSTS` is empty or `*`, and the startup log names each one; `ALLOWED_HOSTS` left out or at its example value, `METRICS_ENABLED=false` and `STEALTH_ENABLED=true` only warn.
By default every session shares one Chromium process and one noVNC desktop (`SESSION_ISOLATION_MODE=shared_browser_node`). Cookies and storage stay separate per session, but a human taking over one session can see the others' windows. When sessions belong to different people, accounts or trust domains, start with `make up-isolation` to give each session its own browser container, takeover surface and Docker network (`docker_ephemeral`), so one session's browser cannot reach another's Playwright server. [`docs/session-isolation-audit.md`](./docs/session-isolation-audit.md) has the details.
`ALLOWED_HOSTS` is held before the request goes out (`PRE_REQUEST_BLOCKING=true`, the default):
- **Stopped before it is sent:** a top-level navigation's request to a host off `ALLOWED_HOSTS`, including
every redirect hop and a popup's first request.
- **Still caught after the fact by NAV-07** (the guard that sends a refused page back to `about:blank` once it
commits):
- speculation-rules prefetch in attach modes, and in a launched browser where it could not be turned off
(launched browsers turn it off when the controller acquires them; `/readiness` warns when that failed);
- a navigation a service worker serves (one a same-site refused iframe registered) for a popup's first
navigation, and for any the popup makes before its service-worker bypass is set (a background task once
the controller sees the popup), in every mode;
- `blob:` navigations a refused iframe makes;
- bfcache restores in attach modes;
- requests held when the gate detaches, and any navigation a page starts itself before the replacement gate
attaches. They go out. The controller attaches the replacement before it closes that browser's sessions,
so the window is one attach, and it refuses their actions meanwhile.
- **Any tab, rarely:** a navigation to a refused host fails closed in any tab of a gated browser, the
operator's own included in attach modes, when the gate cannot classify its frame (its lookup failed).
- **Not covered:** iframes and subresources of other hosts.
In an attach mode (`CDP_CONNECT_URL`, `browser.cdp_attach`) the gate stops navigations of the controller's
sessions only (apart from a frame it cannot classify, above), but every navigation in that Chrome, the
operator's own tabs included, waits on its reply: about 13 ms each, the median measured on Chromium 151. Start
that Chrome with `--disable-features=BlockOriginHeaderModificationOnRedirect,AvoidCorsURLLoaderRestartOnRedirect`
and set `CDP_BLOCK_ORIGIN_ON_REDIRECT_DISABLED=true`. Without it, cross-origin 307/308 POST redirects can fail, and
on Chrome 155 and later credentialed cross-origin requests that need a CORS preflight fail after a redirect, in
the operator's own tabs too, while the controller is attached. Details:
[`docs/deployment.md`](./docs/deployment.md#stopped-before-the-request-is-sent).
`COMPLIANCE_TEMPLATE` can apply a preconfigured posture at startup:
| Preset | Auth Encryption | Operator ID | PII Scrub | Isolation | Max Auth-State Age |
| --- | --- | --- | --- | --- | --- |
| `strict` | required | required | all layers | `docker_ephemeral` | 4h |
| `balanced` | - | required | network + text | shared | 24h |
Both presets require upload approvals and enable Witness receipts. Startup writes the applied policy to `/data/compliance-manifest.json`. The legacy names (`HIPAA`, `SOC2`, `GDPR`, `PCI-DSS`) still work as deprecated aliases and emit a warning at startup.
Example:
```bash
COMPLIANCE_TEMPLATE=strict docker compose up
```
For deployment details, hosted Witness notes, CLI auth modes, and reverse-SSH guidance, see:
- [`docs/deployment.md`](./docs/deployment.md)
- [`docs/production-hardening.md`](./docs/production-hardening.md)
- [`docs/session-isolation-audit.md`](./docs/session-isolation-audit.md): what each isolation mode does and does not separate
## Architecture at a Glance
```mermaid
flowchart LR
User[Human operator] -->|watch / takeover| noVNC[noVNC]
LLM[Any model: OpenAI / Claude / Gemini / OpenRouter / Grok / DeepSeek / MiniMax / local] -->|shared tools| Controller[Controller API]
Controller -->|Playwright protocol| Browser[Browser node]
noVNC --> Browser
Browser --> Artifacts[(screenshots / traces / auth state)]
Controller --> Artifacts
Controller --> Policy[Allowlist + approval gates]
```
### Model providers
First-class adapters for **OpenAI**, **Claude**, and **Gemini** (API or CLI). Beyond those,
a single generic **OpenAI-compatible** adapter drives any model reachable over an OpenAI
`/chat/completions` endpoint — set an API key to enable it:
| Provider | Reaches |
|----------|---------|
| `openrouter` | one key → ~every frontier model (Claude, GPT, Gemini, Grok, DeepSeek, Llama, Mistral, Qwen, …) |
| `xai` | Grok |
| `deepseek` | DeepSeek (text-only; driven from the DOM/accessibility outline) |
| `minimax` | MiniMax |
| `openai_compatible` | any custom base URL — self-hosted Ollama / vLLM / LM Studio, Azure OpenAI, Together, Groq, Fireworks, … |
Vision (screenshots) is used for every provider except text-only ones. See `.env.example` for
the `*_API_KEY` / `*_BASE_URL` / `*_MODEL` settings.
Core components:
- `browser-node/` runs Chromium, Xvfb, x11vnc, and noVNC
- `controller/` exposes the FastAPI controller, MCP transport, policy rails, and orchestration endpoints
- `data/` holds runtime artifacts, auth state, approvals, audit logs, and optional CLI caches
- `scripts/` contains local helpers for doctor, smoke tests, bridges, and release checks
## Repo Guide
| Path | What It Contains |
| --- | --- |
| [`controller/`](./controller/) | controller API, MCP transport, tests, and packaging |
| [`browser-node/`](./browser-node/) | browser runtime and Playwright connection layer |
| [`examples/`](./examples/) | copy-paste flows and MCP client setup |
| [`integrations/langchain/`](./integrations/langchain/README.md) | LangChain, LangGraph, and CrewAI adapters |
| [`docs/`](./docs/) | architecture, deployment, hardening, and launch docs |
| [`scripts/`](./scripts/) | doctor, smoke harnesses, stdio bridge, and auth helpers |
| [`ops/`](./ops/) | supporting service templates and operational assets |
## Common Commands
| Command | Purpose |
| --- | --- |
| `make help` | list available repo commands |
| `make lint` | run Ruff checks across the whole repo |
| `make test` | run controller tests in Docker |
| `make test-local` | run controller tests on host Python 3.11+ |
| `make eval` | run deterministic provider/profile eval scoring |
| `make doctor` | run the local readiness smoke |
| `make release-audit` | run the fuller release-validation pass |
| `make smoke-isolation` | verify per-session Docker isolation |
| `make smoke-reverse-ssh` | verify reverse-SSH remote access |
## Documentation Map
| If You Want To... | Start Here |
| --- | --- |
| understand the system shape | [`docs/architecture.md`](./docs/architecture.md) |
| connect Claude, Codex, Antigravity, Cursor, or VS Code | [`docs/mcp-clients.md`](./docs/mcp-clients.md) |
| run the curl-first examples | [`examples/README.md`](./examples/README.md) |
| deploy on a trusted host | [`docs/deployment.md`](./docs/deployment.md) |
| review production constraints | [`docs/production-hardening.md`](./docs/production-hardening.md) |
| see what session isolation does and does not separate | [`docs/session-isolation-audit.md`](./docs/session-isolation-audit.md) |
| run the convergence harness | [`docs/convergence-harness.md`](./docs/convergence-harness.md) |
| run the agent evals | [`docs/agent-evals.md`](./docs/agent-evals.md) |
| drive the built-in agent loop and its model providers | [`docs/llm-adapters.md`](./docs/llm-adapters.md) |
| let a peer node ask this controller to run a tool (mesh) | [`docs/mesh.md`](./docs/mesh.md) |
| inspect release history | [`CHANGELOG.md`](./CHANGELOG.md) |
| see where the project is headed | [`ROADMAP.md`](./ROADMAP.md) |
## Contributing
If you want to help, start with:
- [`CONTRIBUTING.md`](./CONTRIBUTING.md)
- [`docs/good-first-issues.md`](./docs/good-first-issues.md)
- [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md)
If Auto Browser is useful, a star helps other people find it. Sponsorship and tip options live in [`TIPS.md`](./TIPS.md).
TDQS
Scored across 20 tools
Most tools have distinct resource/action targets, and descriptions clarify when to use observe, get_html, find_elements, or screenshot. There is still some overlap around page observation and screenshots, since observe can return screenshot-only output while screenshot also captures the viewport.
The tool set is consistently snake_case under the browser namespace, mostly following verb_noun patterns like create_session, list_tabs, and save_auth_profile. A few names such as observe and screenshot are shorter verb/noun forms rather than strict verb_noun, but the convention remains readable and predictable.
Twenty tools is somewhat heavy but justified for a full browser automation server covering sessions, tabs, actions, observation, downloads, and auth profiles. Each tool has a plausible role, though the surface is near the upper end of what an agent can comfortably navigate.
The surface covers core browser automation lifecycle operations well: sessions, tabs, actions, observation, downloads, JS evaluation, and human takeover. There are minor gaps around auth-profile lifecycle, such as no obvious delete/update or explicit load operation, and tab creation is not directly exposed as its own tool.