A real browser for your AI agent. One command to add.
A local, open-source browser MCP server for Codex CLI, Claude Code and other MCP clients. Use it for interactive browser automation and front-end QA: inspect console logs and network requests, check responsive layouts, and collect screenshots alongside what happened on the page. Your agent opens pages, reads them, and acts on them through **verified actions**: it fills a whole form in one call, picks "Mrs." from a custom React dropdown by name, and is told what the page did in response, instead of clicking coordinates and hoping. Ascended is my own private agent workspace; this package extracts its browser so it can run on its own: [Camoufox](https://camoufox.com) (a hardened Firefox that looks like a person's browser to the sites it visits) behind the same tool dispatcher, page reading and result formatting Ascended's own agent uses. ## Start with a QA check [**Read the documentation site**](https://aceatdev.github.io/ascended-browser/): [QA setup](https://aceatdev.github.io/ascended-browser/qa-quickstart/) and [interrupted-action reproduction](https://aceatdev.github.io/ascended-browser/interrupted-actions/). Running Codex CLI over SSH and missing a browser? [Connect a browser MCP on your Linux dev box](https://aceatdev.github.io/ascended-browser/codex-cli-browser-ssh/) with setup commands, localhost guidance and alternatives. Practical recipes with real local-fixture screenshots: - [Find console errors and failed requests](https://aceatdev.github.io/ascended-browser/debug-console-network/). - [Check responsive layouts](https://aceatdev.github.io/ascended-browser/responsive-layout-qa/). - [Test login-protected pages and session reuse](https://aceatdev.github.io/ascended-browser/authenticated-browser-testing/). [**Set up a browser MCP for Codex CLI or Claude Code and check your app**](docs/qa-quickstart.md). The guide covers installation, a copy-paste QA prompt, console and network inspection, bug-report structure, and what to do after an interrupted action. - **Check a local app:** walk through a flow and report errors before changing code. - **Inspect the evidence:** read failed requests and console errors, then capture the broken state. - **Use a Linux dev box:** run the client, MCP server and app on the same machine; the guide explains how localhost behaves when you connect over SSH. This is agent-assisted exploratory QA, not a replacement for deterministic regression tests or a validated CI integration. Browser MCP tools are also available from projects such as Playwright MCP and Chrome DevTools MCP; console and network inspection are not exclusive to this server. ## See it work Three real runs of Claude Code with only this server attached, on live sites. The panel on the side is Claude Code's own transcript: every tool call it made and what came back, errors included, with the real elapsed time. Waiting is cut and tool time plays faster; the pointer, device frames, devtools panels and outlines are drawn afterwards from the server's event log (`demo/record_demo.py`), so they sit on what the agent really touched and read. The full unedited screen recordings are in [`videos/unedited/`](videos/unedited/) (the browser is driven through Playwright, so no OS pointer appears in them).
**Front-end QA.** react.dev on a phone and a tablet, dark/light, before/after screenshots, an audit outlining the real offending elements, console and network.
**Browsing.** Types into Wikipedia's search, finds a fact in the article, then searches YouTube and plays the first video.
**Real forms.** booking.com: popup, destination autocomplete, date picker, search, sort by price.
**Saved logins.** You save a login once; the agent signs in with it and never sees it. Built from a real run (`demo/login_demo.py`, then `demo/login_explainer.py`): the CLI's output, the screen, and the exact text and pictures the agent got back.
Click any clip for the full-quality MP4.
## Install
From Python (3.11 or newer) or from npm; both run the same server.
```bash
uvx ascended-browser doctor # checks the machine; downloads nothing
uvx ascended-browser fetch # downloads the browser now (~700 MB; otherwise on first use)
npx -y ascended-browser doctor # the same, from npm
```
The browser is Camoufox 135.0.1-beta.24, the build every test here ran on; it is
pinned, so a newer Camoufox release never changes what your agent drives, and
any other Camoufox you have installed is left as it is. Run `fetch` once before
adding the server to an agent, so its first tool call does not wait for the
download.
The npm package is a small launcher: it runs the Python package with `uvx`
when [uv](https://docs.astral.sh/uv/) is installed (uv brings its own Python),
else `pipx`, else a private venv made with your Python 3.11+. Use whichever
command you prefer in the configs below (`npx -y ascended-browser` in place of
`uvx ascended-browser`).
Linux: install `xvfb` to keep the browser on a virtual display (closest to a
real screen); without it the browser runs headless. On a display,
`browser_viewport` resizes the real window, so phone and tablet checks reflow
the page at true breakpoints.
## Add it to your agent
**Claude Code**
```bash
claude mcp add ascended-browser -- uvx ascended-browser
# or: claude mcp add ascended-browser -- npx -y ascended-browser
```
**Codex**
```bash
codex mcp add ascended-browser -- uvx ascended-browser
```
Whether Codex asks before each browser action follows the permission mode you
pick in Codex: under **Full access** it just runs them; under **Ask for approval**
it asks first. In a sandboxed mode, `codex exec` cannot ask and fails every
call: use Full access, or pre-approve this server's tools by adding
`default_tools_approval_mode = "approve"` under `[mcp_servers.ascended-browser]`
in `~/.codex/config.toml`.
**opencode** (`opencode.json`)
```json
{
"mcp": {
"ascended-browser": { "type": "local", "command": ["uvx", "ascended-browser"], "enabled": true }
}
}
```
**Cursor, Windsurf, Claude Desktop and other clients:** a stdio server with
command `uvx` and args `["ascended-browser"]`, or command `npx` and args
`["-y", "ascended-browser"]`.
## Tools
| Tool | What it does |
|---|---|
| `browser_open` | Open a URL (or several at once) and return what is on the page, each control with a ref |
| `browser_observe` | Look at the page again, or narrow to a region, a query or a filter |
| `browser_act` | `navigate`, `click`, `fill`, `fill_form`, `select`, `check`, `date`, `press`, `upload`, `scroll`, `wait`, `sequence`: each verified, each answered with what changed |
| `browser_extract` | Read the page's text and field state, `find` a phrase, list every match of a CSS selector with its text and attributes (every product link, every price), or `read=console`, `read=network`, `read=inspect`, `read=audit`, `read=design` to debug a page |
| `browser_screenshot` | A picture, when an observation cannot describe it: canvas, charts, visual layout; `compare_with` diffs against an earlier picture or another tab |
| `browser_viewport` | Resize to phone/tablet/desktop (Linux), emulate dark mode, reduced motion, forced colors or offline |
| `browser_evaluate` | Read-only JavaScript, policy-checked |
| `browser_tabs` | List, close or sleep tabs |
| `browser_flow` | Record a task once, replay it on the next page with new values |
| `browser_login` | Sign in with a login you saved (see [Saved logins](#saved-logins)); the agent never sees its values |
| `wait_for_bot_wall` | Wait out a "checking your browser" page; press a Turnstile/reCAPTCHA checkbox if one blocks a form |
Long results come back clipped, with an `evidence_ref` that
`browser_extract` pages through, so a huge page cannot flood the agent's
context.
Every action waits for the page to settle, under a hard timeout, and says
when its effect could not be confirmed. If a turn ends while one is still
running, the next turn is told instead of finding a page it cannot explain:
an interrupted click (you stopped the turn) is named in the next result for
that tab, and an action whose server died (`claude -p` ending, an SSH session
dropping) is listed with the first result of the next session, so the agent
checks before it repeats an order or a form.
## Saved logins
Save a login once and the agent can sign in with it, without ever seeing it.
```bash
uvx ascended-browser login add github.com --username you@example.com # prompts for the password
uvx ascended-browser login add accounts.example.com --name Work --totp # also a TOTP secret (or otpauth:// URI)
uvx ascended-browser login list # names, usernames, sites; never passwords
uvx ascended-browser login edit Work --password # change only what you pass
uvx ascended-browser login remove Work
```
The site is the host of the **sign-in page** (`accounts.example.com`, not
`example.com`, when they differ); pass several for one account on several
hosts. Passwords and TOTP secrets come from a hidden prompt, or from stdin
with `--password-stdin`, never from the command line, where they would end up
in shell history and the process list.
Then ask your agent to sign in. `browser_open` tells it a saved login exists
for the page, and `browser_login` finds the username, password and
one-time-code fields and types the values in (`submit: true` also presses
the button). What the agent gets:
- **Every tool result is scrubbed** of every saved username, password and TOTP
secret, also URL-encoded, JSON- or HTML-escaped: `browser_observe`,
`browser_evaluate` (reading `input.value` returns `[redacted]`),
`browser_extract` (page text, field values, network bodies) and action
results. A page that prints "Signed in as you@example.com" reads as
"Signed in as [redacted]".
- **Every screenshot is masked** before it reaches the agent: password, card
and one-time-code fields, username and email fields, and any saved value
shown as page text.
For sites a saved login cannot fill (single sign-on, passkeys, a CAPTCHA, a
code sent by email), sign in by hand once:
```bash
uvx ascended-browser signin https://example.com/login # opens the browser; sign in, then press Enter
```
The cookies stay in the browser profile that every later agent session
starts from. Close running agent sessions first, so the sign-in lands in the
saved profile rather than a session's copy.
The vault is `logins.db` in the data directory, readable only by your user,
and not encrypted (like `gh` or `aws` credentials files). The scrubbing and
masking cover what the **browser tools** return. An agent that also has a
shell or file tools (Claude Code, Codex) runs as you and can read any file you
can, this one included. Deny its file tools the path (in Claude Code,
`"deny": ["Read(~/.local/share/ascended/**)"]` under `permissions` in
`~/.claude/settings.json`) and keep shell commands on approval; an agent free
to run any command can still reach the file. Values shorter than 4 characters
are not scrubbed, and a value the page changes (the last four digits of a
card, say) is not matched.
## Settings
| Variable | Default | |
|---|---|---|
| `ASCENDED_BROWSER_WINDOW` | hidden | `show` opens a visible window |
| `ASCENDED_DATA_DIR` | `~/.local/share/ascended/browser` | Browser profile (sign-ins persist), saved logins (`logins.db`), session files |
| `ASCENDED_RESULT_MAX_CHARS` | `24000` | Longer results are clipped with an `evidence_ref` |
| `ASCENDED_SETTING_