wraith-mcp
# wraith-mcp
[](https://pypi.org/project/wraith-mcp/)
[](LICENSE)
AI-native stealth browser MCP server. Tell it what to do — it figures out how.
[Browser Use](https://github.com/browser-use/browser-use) (AI vision navigation) + [Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) (bot detection bypass).
## Why This One?
| | wraith-mcp | playwright-mcp | stealth-browser-mcp | browser-use-mcp-server |
|---|---|---|---|---|
| Navigation | **AI vision** (self-healing) | CSS selectors | CSS selectors | AI vision |
| Bot detection bypass | **Patchright** (binary-level) | None | nodriver | None |
| Tools | **17** — keyless `navigate/snapshot/click/type_text/…` + AI `browse`/`extract` | 20+ | 90+ | via Agent |
| Keyless (no API key) | **Yes** — client AI drives low-level tools directly | Yes | Yes | No |
| Site layout changes | **Adapts automatically** | Breaks | Breaks | Adapts |
| LLM providers | **6** (Anthropic, OpenAI, OpenRouter, Google, Ollama, +compatible) | N/A | 1 | 1 |
**One command does it all** — no selectors, no step-by-step scripting:
```
"Log into my dashboard and download the monthly report"
```
## Quick Start
> Add wraith-mcp as MCP server
## Install
```bash
pip install wraith-mcp
```
## Setup
Add to your MCP config (`.mcp.json`, `.cursor/mcp.json`, `.windsurf/mcp.json`, etc.):
```json
{
"mcpServers": {
"wraith": {
"command": "wraith-mcp"
}
}
}
```
Works with any MCP client: Cursor, Windsurf, VS Code, Cline, Roo Code, OpenCode, Codex, and more.
The browser window is **visible by default**. Set `"HEADLESS": "true"` in `env`
to run it headless — recommended for servers, CI, and Docker (the Docker image
already sets it).
These clients call Wraith over MCP. The low-level tools work with no API key —
your client's AI drives them directly. The autonomous `browse`/`extract` tools
use MCP sampling when supported, otherwise a fallback key (see below).
## Do I Need an API Key?
**No — for the low-level tools.** `navigate`, `snapshot`, `click`, `type_text`,
`scroll`, `press_key`, `get_content`, `screenshot`, `pdf`, and `tabs` need **no
API key**: your MCP client's model (Claude Code, Codex, OpenCode, …) calls them
directly and is the brain, exactly like playwright-mcp.
Only the **autonomous Agent tools** — `browse` and `extract`, which plan the
steps themselves — need a model. They use MCP sampling when the client supports
it, otherwise a fallback provider key. Note that many clients, **including Claude
Code today, do not implement MCP sampling**, so `browse`/`extract` need a
fallback key there (`ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY`,
`GOOGLE_API_KEY`, or Ollama via `OLLAMA_MODEL`). The low-level tools work either way.
## Tools
**Keyless** — your client's AI drives the browser directly, no API key:
| Tool | Description |
|------|-------------|
| `navigate` | Open a URL; returns the page's interactive elements |
| `snapshot` | List interactive elements as `[index] <tag> text` |
| `click` | Click an element by index (from `snapshot`) |
| `type_text` | Type text into an element by index |
| `scroll` | Scroll the page |
| `press_key` | Press a key or chord (e.g. `Enter`, `Control+a`) |
| `get_content` | Current page as clean markdown |
| `screenshot` | Capture a page as base64 PNG |
| `pdf` | Render a page to base64 PDF (headless only) |
| `tabs` | List, open, or close tabs |
| `list_downloads` | List files downloaded this session (saved to `./downloads` by default) |
| `save_storage_state` | Save cookies + localStorage to a file (restore via `BROWSER_STORAGE_STATE`) |
| `list_sessions` / `close_session` / `close_all_sessions` | Manage persistent sessions |
**Autonomous AI Agent** — needs MCP sampling or a fallback key:
| Tool | Description |
|------|-------------|
| `browse` | Execute any browser task in natural language |
| `extract` | Pull structured data from a page |
The low-level tools share a persistent browser via `session_id` (default
`"default"`): call `navigate` once, then `snapshot`/`click`/`type_text`/… reuse
the same page. `browse`/`extract` also accept `session_id`.
## Fallback LLM Providers
Use these only when your MCP client does not support sampling, or when you want
to force Wraith to use an explicit provider.
| Provider | Key |
|----------|-----|
| Anthropic (default) | `ANTHROPIC_API_KEY` |
| OpenRouter | `OPENROUTER_API_KEY` |
| OpenAI | `OPENAI_API_KEY` |
| DeepSeek / Groq / Together | `OPENAI_API_KEY` + `OPENAI_BASE_URL` |
| Google Gemini | `GOOGLE_API_KEY` |
| Ollama (local) | `OLLAMA_MODEL` |
Set `BROWSER_USE_MODEL` to override the default model or provide a sampling model
hint to the MCP client.
## Browser Options
Wraith keeps its default browser profile unless optional env vars are set. It can
forward Browser Use profile knobs for domain policy (`BROWSER_ALLOWED_DOMAINS`,
`BROWSER_PROHIBITED_DOMAINS`, `BROWSER_BLOCK_IP_ADDRESSES=true` for Browser
Use's direct-IP navigation block), session/artifacts (`BROWSER_STORAGE_STATE`,
`BROWSER_USER_DATA_DIR`, `BROWSER_DOWNLOADS_PATH`, `BROWSER_RECORD_HAR_PATH`,
`BROWSER_RECORD_VIDEO_DIR`, `BROWSER_TRACES_DIR`), and permissions, viewport, or
wait timing (`BROWSER_PERMISSIONS`, `BROWSER_VIEWPORT`,
`BROWSER_MINIMUM_WAIT_PAGE_LOAD_TIME`,
`BROWSER_WAIT_FOR_NETWORK_IDLE_PAGE_LOAD_TIME`, `BROWSER_WAIT_BETWEEN_ACTIONS`)
when the installed Browser Use `BrowserProfile` supports those fields. Domain/IP
policy env vars fail closed on unsupported Browser Use versions.
**Locale & timezone (stealth):** by default Wraith pins neither — it inherits the
host locale and timezone so they stay consistent with your exit IP. Forcing
`en-US` on a non-US IP is itself a bot signal, so set these only when routing
through a proxy: point `BROWSER_LOCALE` and `BROWSER_TIMEZONE` at the proxy's
region (e.g. `en-US` + `America/New_York`) so locale, timezone, and IP agree.
`BROWSER_TIMEZONE` is applied via CDP (`Emulation.setTimezoneOverride`) because
Browser Use's `BrowserProfile` has no timezone field.
**Downloads:** files save to `./downloads` (relative to the server's working
directory) by default so they persist — Browser Use otherwise uses a temp dir
that gets cleaned up on session stop, losing the file. Override with
`BROWSER_DOWNLOADS_PATH`, and use the `list_downloads` tool to retrieve the saved
paths after a `click`/`navigate` triggers a download.
## Docker
```bash
docker build -t wraith-mcp .
docker run -i --rm wraith-mcp
```
SSE mode for local-only testing:
```bash
docker run -p 127.0.0.1:8808:8808 wraith-mcp --transport sse --host 0.0.0.0 --port 8808
```
Do not expose the SSE port directly to an untrusted network. If you need remote
access, put it behind an authenticated proxy or SSH tunnel and restrict browsing
with `BROWSER_ALLOWED_DOMAINS` plus `BROWSER_BLOCK_IP_ADDRESSES=true`.
## SSE Transport
```bash
wraith-mcp --transport sse --host 127.0.0.1 --port 8808
```
The SSE host defaults to `127.0.0.1`. Binding to `0.0.0.0` is only appropriate
behind an authenticated proxy or another trusted network boundary.
## How It Works
**Keyless (low-level tools)** — your MCP client's AI is the brain:
```
Client AI -> MCP Server -> Patchright Chromium
```
1. `navigate` opens a page and returns its interactive elements
2. The client AI reads the `[index] <tag> text` snapshot and decides what to do
3. It calls `click`/`type_text`/`scroll`/… by index — no model runs in Wraith
4. Patchright executes without triggering bot detection
**Autonomous (`browse`/`extract`)** — Wraith drives a Browser Use Agent:
```
AI Agent -> MCP Server -> Browser Use Agent -> Patchright Chromium
```
1. Describe a task in natural language
2. Browser Use asks the MCP client model through sampling, or a fallback provider
3. Browser Use sees the page (screenshot + DOM) and decides actions
4. Patchright executes without triggering bot detection
## Security
- URL scheme validation (http/https only)
- `max_steps` capped at 50 server-side
- Input length capped at 4000 chars
- Task timeout (default 120s, configurable via `BROWSER_TASK_TIMEOUT`)
- Proxy support via `PROXY_SERVER`
- SSE transport binds to `127.0.0.1` by default; do not expose it directly
without authentication
- Browser Use page context is sent to the MCP client model via sampling, or to
the configured fallback provider
## Limitations
- Binary-level stealth only (no `Runtime.enable` CDP fix)
- Enterprise WAFs may still block without residential proxies
- Fresh browser per call (~3s startup)
- The low-level tools (`navigate`/`snapshot`/`click`/…) are keyless — no model needed
- `browse`/`extract` need MCP sampling or a fallback key; many clients (incl. Claude Code) don't implement sampling, so a fallback key is required there
## License
MIT
TDQS
Scored across 17 tools
Most tools have clearly distinct purposes: navigation, interaction, content reading, screenshots, PDFs, downloads, and session management are all separate. The only potential confusion is between browse, extract, and navigate, but their descriptions make the distinction clear.
Naming is mixed: some tools use bare verbs (browse, scroll, navigate, click, extract), others use verb_noun (type_text, get_content, close_session), and a couple are nouns used as commands (tabs, screenshot). The style is readable but not uniform.
With 17 tools, this is slightly above the ideal 3-15 range, but each tool covers a distinct browser or session operation, making the count justified. It feels a bit heavy but not excessive.
The server covers the core browser lifecycle well: navigation, interaction, content extraction, screenshots, PDFs, downloads, persistent sessions, and tabs. Minor gaps exist (e.g., no explicit history or wait commands), but workflows are largely complete.