# The stealth browser MCP server: install, configure, and its 94 tools

`stealth-chrome-devtools-mcp` is a Model Context Protocol server that gives an AI agent a real, undetectable Chrome. It is a Python package, AGPL-3.0, currently at 2.1.14 on PyPI. This page is the install: the command, the config block, the requirements, the tool surface, and the environment variables — with the reasoning for the parts that are easy to get subtly wrong.

## What it plugs into

Anything that speaks MCP. Claude Code, Claude Desktop, Codex and other MCP clients all launch a server process over stdio and read its tool list, so the server is configured the same way in each: one entry naming a command to run. Nothing here is client-specific except the path to the config file.

The server itself is not an agent. It exposes browser tools; the model in your client decides which to call. If you want the conceptual version first, [stealth browser automation](/stealth-browser-automation) is the page for that.

## The right way: a pinned executable

<!-- terminal -->
```
uv tool install stealth-chrome-devtools-mcp==2.1.14
```

That installs a version-pinned executable at `~/.local/bin/stealth-chrome-devtools-mcp` (Windows: `%USERPROFILE%\.local\bin\stealth-chrome-devtools-mcp.exe`). Point your MCP config at it:

<!-- claude_desktop_config.json -->
```
{
  "mcpServers": {
    "stealth-chrome-devtools-mcp": {
      "command": "C:\\Users\\<you>\\.local\\bin\\stealth-chrome-devtools-mcp.exe",
      "args": []
    }
  }
}
```

Claude Code has a one-liner that writes the same entry for you — use the `.exe` path above on Windows:

<!-- terminal -->
```
claude mcp add --scope user stealth-chrome-devtools-mcp -- ~/.local/bin/stealth-chrome-devtools-mcp
```

To upgrade later, install the new pin (`uv tool install stealth-chrome-devtools-mcp==<new-version>`) or track the latest release with `uv tool upgrade stealth-chrome-devtools-mcp`.

## Why not `uvx` in the MCP config

Putting `uvx` in a client config works, and for a single session it is the fastest way to try the server. It is the wrong shape at scale, for a reason that is easy to miss until the moment it bites.

`uvx` re-resolves the package on **every client session start**. Each agent session launches its own stdio proxy, so a fleet of sessions starting together — the shared backend is scale-tested at 50 — turns startup into a package-resolution storm: fifty resolutions of the same package, against the same index, at the same moment. A `uv tool install` gives every proxy an instant pinned executable instead. The shared backend, the profile handling and the per-session browser isolation behave identically either way; the only thing that changes is what happens in the first second of fifty sessions.

## uvx, pip, and local development

A zero-install trial, fine for a first look and not for fleets:

<!-- claude_desktop_config.json -->
```
{
  "mcpServers": {
    "stealth-chrome-devtools-mcp": {
      "command": "uvx",
      "args": ["stealth-chrome-devtools-mcp==2.1.14"]
    }
  }
}
```

Or via pip — `pip install stealth-chrome-devtools-mcp==2.1.14` — then use the `stealth-chrome-devtools-mcp` console script from that environment as the `command`. For local development against a checkout:

<!-- claude_desktop_config.json -->
```
{
  "mcpServers": {
    "stealth-chrome-devtools-mcp": {
      "command": "uv",
      "args": [
        "--directory", "/path/to/stealth-chrome-devtools-mcp",
        "run", "stealth-chrome-devtools-mcp"
      ]
    }
  }
}
```

## What the machine needs

- Python 3.11 or newer (3.11, 3.12 and 3.13 are the classifiers on PyPI).
- Chrome, Chromium or Microsoft Edge — a real installed browser, not a bundled one.
- `uv` (recommended) or pip.
- A desktop session for **headed** browsing. Headless works from SSH, CI and services.

## Install to first logged-in session, in six steps

1. **Install the pinned executable.** Run uv tool install stealth-chrome-devtools-mcp==2.1.14. This puts a version-pinned executable at ~/.local/bin/stealth-chrome-devtools-mcp, or %USERPROFILE%\.local\bin\stealth-chrome-devtools-mcp.exe on Windows.
2. **Point your MCP config at it.** Add an mcpServers entry whose command is the absolute path to that executable, in claude_desktop_config.json, ~/.claude.json or whichever config your MCP client reads. Claude Code users can instead run: claude mcp add --scope user stealth-chrome-devtools-mcp -- ~/.local/bin/stealth-chrome-devtools-mcp
3. **Restart the MCP client.** Clients read their server list at startup, so the new server appears after a restart.
4. **Call spawn_browser().** Ask the agent to spawn a browser with no user_data_dir. The server opens Chrome on the master profile, stripping any detectable launch flags first.
5. **Sign in to your accounts.** Use the window that opens like an ordinary browser and log in to whatever the automation will need.
6. **Close the browser.** Closing it commits the profile. Future sessions reuse it, or clone from its snapshot when it is busy, so the logins survive.

After that, a session that finds the master profile busy clones it from the snapshot instead of waiting, and the clone carries the same cookies and logins. Named profiles work the same way: `spawn_browser(user_data_dir="github-session")` persists, and auto-suffixes to `github-session-2` while the first is open.

## 94 tools across 11 sections

The count is derived from the live tool registry and asserted by a test, so it is the number the server actually serves rather than a number in a document. The 11 section modules are `browser_management`, `cdp_functions`, `cookies_storage`, `debugging`, `dynamic_hooks`, `element_extraction`, `element_interaction`, `file_extraction`, `network_debugging`, `progressive_cloning` and `tabs`.

| Tool | What it does |
| --- | --- |
| `spawn_browser` | Launch a new stealth browser instance |
| `navigate` | Navigate to a URL |
| `take_screenshot` | Capture a page screenshot |
| `execute_script` | Run JavaScript in page context |
| `query_elements` | Find DOM elements by CSS selector |
| `click_element` | Click an element |
| `type_text` | Type text into an input |
| `get_page_content` | Get the page HTML |
| `list_instances` | List active browser instances |
| `close_instance` | Close a specific browser |
| `list_network_requests` | View intercepted network traffic |
| `get_cookies` / `set_cookie` | Read and write cookies |

> The honest number next to the headline one: at the release SHA in the evidence ledger, **3 of the 94 are release-qualified** — asserted end to end over the real stdio transport a client speaks. The rest are driven against real Chrome by the E2E suite through an in-process seam, so at the wire they are `served-unqualified`: tested, not proved there. [`RELEASE_CONTRACT.md`](https://github.com/DevinoSolutions/stealth-chrome-devtools-mcp/blob/main/RELEASE_CONTRACT.md) records the state of every tool and is the only source for those numbers. The full navigation map of the source tree is in [`CLAUDE.md`](https://github.com/DevinoSolutions/stealth-chrome-devtools-mcp/blob/main/CLAUDE.md).

## Environment variables

All optional; the defaults work for normal use. Set them in your shell or in `~/.stealth-mcp/.env` — every key is documented in [`.env.example`](https://github.com/DevinoSolutions/stealth-chrome-devtools-mcp/blob/main/.env.example). A `.env` in your **project** directory is deliberately ignored, because the backend is a shared process launched with whatever folder the MCP client had open: reading it meant reading another application's config, which crashed the server outright on an ordinary `DATABASE_URL` and silently adopted that app's `PORT`, `DEBUG` and `SENTRY_DSN`.

| Variable | Default | Purpose |
| --- | --- | --- |
| `STEALTH_MCP_BROWSER_SESSION_ROOT` | `C:\stealth-mcp-browser-sessions` (Win) / `~/.stealth-mcp-browser-sessions` (Unix) | Base folder for profiles |
| `BROWSER_MASTER_USER_DATA_DIR` | `<root>/master` | Master Chrome profile path |
| `BROWSER_MASTER_SNAPSHOT_DIR` | `<root>/master-snapshot` | Snapshot clone source |
| `BROWSER_PROFILE_CLONE_ROOT` | `<root>/sessions` | Folder for profile copies |
| `BROWSER_PROFILE_REFRESH_DAYS` | `7` | Refresh copies after N days (`0` disables) |
| `STEALTH_MCP_CLONE_STORAGE_CAP_GB` | `10` | Cap on auto-clone storage; oldest **idle** clones reclaimed when exceeded |
| `STEALTH_MCP_BROWSER_SESSION_STORAGE_CAP_GB` | `20` | Cap on `sessions/`; largest idle named profiles trimmed of regenerable dirs, logins kept |
| `STEALTH_MCP_CLONE_TRASH_RETENTION_HOURS` | `24` | How long an evicted clone stays recoverable in `sessions/.trash/` |
| `BROWSER_IDLE_TIMEOUT` | `0` | Idle cleanup timeout; `0` means browsers live until closed |
| `STEALTH_BROWSER_DEBUG` | `false` | Enable debug logging |
| `STEALTH_MCP_NO_ERROR_REPORTING` | `false` | Set `true` to disable crash reporting |

On error reporting: crashes go to Sentry by default, with the machine name dropped entirely, the username scrubbed out of every path, and **local variables not captured at all** — a local in this tool can hold a proxy password, an `Authorization` or `Cookie` header, or a script you asked it to run. One variable turns the whole thing off.

## The ops CLI

The package also installs a `stealth-chrome-devtools` command for managing the backend and its disk usage. These four only read and preview — they change nothing, and the test suite runs them on every commit:

<!-- terminal -->
```
stealth-chrome-devtools status
stealth-chrome-devtools profiles
stealth-chrome-devtools cleanup
stealth-chrome-devtools cleanup --browser-session-cap-gb 12
```

`status` reports whether the backend is up plus the browser-session root and both caps. `profiles` lists profiles with size, role and in-use state. `cleanup` previews the reclaimable disk — it is a **dry run unless you pass `--apply`**, never touches in-use profiles, and uses the same selectors as the automatic sweep, so the preview matches what `--apply` does.

<!-- terminal -->
```
stealth-chrome-devtools cleanup --apply               # actually reclaim
stealth-chrome-devtools doctor                        # check Chrome / environment / display contexts
stealth-chrome-devtools serve --http --port 19222     # start the server
```

- [Stealth browser automation](/stealth-browser-automation) — what the server does and why it is not flagged.
- [Stealthly vs agent browsers](/compare/agent-browsers) — why the pinned-executable choice matters at fifty sessions.
- [How bot detection works](/bot-detection) — the probes this is all aimed at.
- [`RUNBOOK.md`](https://github.com/DevinoSolutions/stealth-chrome-devtools-mcp/blob/main/RUNBOOK.md) — operating the backend: verbs, logs, recovery, MCP smoke path.
- [The package on PyPI](https://pypi.org/project/stealth-chrome-devtools-mcp/) — release history and current version.

---

Canonical HTML version: https://stealthly.io/mcp
