Cat Burglar
# Cat Burglar
**A stealth browser MCP for agents. It gets in, reads the room, and leaves the DOM tidy.**
[](LICENSE)
[](package.json)
[](https://modelcontextprotocol.io)
[](bench/results/latest.md)
Cat Burglar is a local Model Context Protocol server for browser work. One agent session gets three browsers: patched stealth Chromium, stock Chromium for devtools, and Obscura's 43 MB Rust engine. Pages come back as compact snapshots and diffs. Sites are remembered between runs. High-impact clicks pass through a policy gate.

## Quick start
```bash
git clone https://github.com/DrMkdaddy/cat-burglar.git
cd cat-burglar
npm install
npm run build
npm run install:obscura # optional, 43 MB fallback engine
npm run smoke
```
Add the server to your MCP client. OpenCode V2 uses `mcp.servers`:
```jsonc
{
"mcp": {
"servers": {
"catburglar": {
"type": "local",
"command": ["node", "/absolute/path/to/cat-burglar/dist/index.js"],
"environment": {
"BROWSER_MCP_ALLOW_PRIVATE_NETWORK": "1",
"BROWSER_MCP_OBSCURA_PATH": "/absolute/path/to/obscura"
},
"timeout": { "startup": 45000, "execution": 600000 }
}
}
}
}
```
The server name is `catburglar` on purpose. OpenCode already has a built-in `browser` toolset, and the two names collide in Code Mode. Rename the key if you prefer, but keep it distinct from `browser`.
Then ask your agent:
> Open the pricing page, take a snapshot, and tell me what changed after I switch to annual billing.
## The 30 second tour
| Step | What happens |
| --- | --- |
| `navigate` | Opens a URL and waits for the page to settle |
| `snapshot` | Returns URL, title, controls with stable refs, and visible text |
| `click` / `fill` / `select` | Acts on a ref, selector, text, or ARIA role |
| change observation | The action returns only the added, changed, removed, or text delta |
| `memory learn` | Saves the recent tool sequence as a workflow for that origin |
Refs stay stable while the element remains in the DOM. A new snapshot does not renumber them, so an agent can act on a ref it learned earlier. If the element is gone, the action fails with a stale-ref error instead of clicking a clone.
## Three browsers, one session
| Engine | Use it for | Cost |
| --- | --- | --- |
| `patchright` (default) | Protected sites, logins, anti-bot work | Full Chromium memory |
| `chromium` | Devtools, console, traces, PDF, local app testing | Full Chromium memory |
| `obscura` | Low-memory scraping, high-volume reads, tracker blocking | About 43 MB |
Switch per session:
```json
{ "engine": "chromium", "restart": true }
```
A running browser cannot change engines in place. Stop it and open again.
## Observation that sends only what changed
Full DOM dumps are expensive and noisy. Cat Burglar returns a compact control list, then a diff after each action. Navigation collapses to one URL line. Filling a field is silent unless you ask for an observation. Clicking, selecting, uploading, and pressing keys return a bounded change summary.
Model profiles set the budgets:
| Profile | Snapshot | Controls | Markdown | Change summary | Diff by default |
| --- | --- | --- | --- | --- | --- |
| `local` | 1,800 chars | 50 | 4,000 | 300 chars | yes |
| `flash` | 2,800 | 70 | 6,000 | 400 | yes |
| `frontier` | 8,000 | 160 | 14,000 | 900 | no |
| `auto` | Flash-safe until per-model probing lands | | | | yes |
Use `open({ profile })` or set `BROWSER_MCP_PROFILE`.
## The numbers

| Mode | Success | Tool calls | Observation chars | Wall time |
| --- | --- | --- | --- | --- |
| Full snapshots | 12/12 | 80 | 30,029 | 15.37 s |
| Diff + change observation | 12/12 | 68 | 21,057 | 13.86 s |
That is 29.9% less text and 15% fewer calls with the same success rate. Content-heavy tasks dropped 40% to 46%. The flaky-UI task dropped 66%. One run, profile `flash`, engine `chromium`, Node 26. Raw JSON: `bench/results/2026-11-09-flash.json`. Reproduce with `npm run bench`.
The first version of this feature made output larger. It returned a rich observation after every action, including fills. Measurement caught it. Silent fills, URL-only navigation lines, and bounded diffs fixed it.
## Compared with the field
Measured on the same machine and the same page. Tool count and schema bytes matter when a client does not use Code Mode. RSS is the full process tree after the first snapshot. First navigation includes browser launch.
| Server | Tools | Schema KB | Startup | Snapshot chars | RSS | First nav |
| --- | --- | --- | --- | --- | --- | --- |
| **Cat Burglar** | 30 | 24.3 | 169 ms | 553 | 999 MB | 953 ms |
| Playwright MCP | 25 | 21.1 | 789 ms | 841 | 1174 MB | 438 ms |
| Chrome DevTools MCP | 32 | 28.9 | 880 ms | 859 | 1028 MB | 490 ms |
| Obscura MCP | 38 | 13.3 | 3 ms | 572 | 43 MB | 16 ms |
Snapshot size is not a quality score. The accessibility trees differ. All Chromium engines pay full-browser memory.
| Capability | Cat Burglar | Playwright | Chrome DevTools | Obscura | BrowserMCP / BrowserTools | Browserbase / browser-use / Skyvern |
| --- | --- | --- | --- | --- | --- | --- |
| General interaction | Strong | Strong | Strong | Partial | Partial | Strong |
| Devtools and perf | Partial | Partial | Strong | None | Partial / Strong | None |
| Stealth | Strong | None | None | Strong | Strong, real Chrome | Strong, cloud |
| Your logged-in Chrome | Partial | Partial | Partial | Partial | Strong | Partial |
| Cloud, proxies, CAPTCHA | None | None | None | Waitlist | None | Strong |
| Diff and change observation | Strong | None | None | None | None | None |
| Site memory and workflows | Strong | None | None | None | None | Partial |
| Origin, injection, and risk policy | Strong | Partial | None | Partial | Partial | Partial |
| Reproducible eval harness | Strong | None | None | None | None | None |
| OpenCode Code Mode fit | Strong | Partial | Partial | Partial | Partial | Strong |
Short version: Playwright has the ecosystem and cross-browser reach. Chrome DevTools has the deepest debugging. Obscura has the smallest footprint. BrowserMCP and BrowserTools use the Chrome you are already in. Browserbase, browser-use, Skyvern, and Hyperbrowser handle cloud scale and natural-language automation. Cat Burglar is the one that combines stealth engine choice, adaptive observation, site memory, safety guards, and a local benchmark in one server.
## What it does
**Lifecycle**: `open`, `close`, `navigate`, `back`, `forward`, `reload`, `status`, `tabs`
**Reading**: `snapshot`, `markdown`, `extract`, `evaluate`, `screenshot`, `pdf`
**Interaction**: `click`, `fill`, `type`, `press`, `select`, `hover`, `scroll`, `upload`, `wait`
**State**: `cookies`, `storage`, `stealth`, `memory`
**Diagnostics**: `network`, `console`, `perf`
`extract` runs in five modes: `links`, `tables`, `jsonld`, `meta`, `text`, and a CSS map for custom fields. `perf` covers web vitals, CDP runtime metrics, slowest resources, and Chrome traces. `trace_stop` writes the full JSON trace and returns long tasks, top scripts, and layout totals.
## Safety
- SSRF guard on navigation and every request. DNS is resolved before loading, redirects re-check, and results are cached for 60 seconds.
- Private, loopback, link-local, CGNAT, multicast, and reserved ranges are blocked unless `allow_private` is set.
- Cloud metadata endpoints stay blocked even with private-network access on.
- Origin allow and block lists accept exact origins or `https://*.example.com` wildcards.
- `file://` is off by default.
- Page-derived output carries an untrusted-content marker. The server instructions put page content below the user and system prompt in the trust hierarchy.
- High-impact targets such as delete, pay, transfer, send, publish, and deactivate are flagged. With `BROWSER_MCP_RISKY_ACTION_POLICY=ask`, the action needs `confirm:true` after user approval.
- Site memory is agent-authored only. Page text is never ingested, refs are dropped, and typed values become `{{value}}` placeholders.
- Tracker blocking uses Obscura's 3,520-domain PGL list plus common analytics hosts.
These are guardrails, not a sandbox. A model that ignores the trust boundary can still be influenced by a page.
## Research
The design follows a review of web-agent work from 2023 to 2026: evaluation error, observation alignment, change observation, memory, planning, prompting, and prompt injection. The full review with 40 sources is in [`docs/research/2026-11-browser-agent-research.md`](docs/research/2026-11-browser-agent-research.md).
The benchmark and the LLM runner are in `bench/`. Use `npm run bench:compare` for the server table above and `npm run bench:llm` for a model-level run. The LLM runner accepts `--server ours|playwright|chrome-devtools|obscura`, so the same 12 tasks can produce a head-to-head result with your model.
## Develop
```bash
npm run build # tsc -> dist
npm test # 28 unit tests
npm run smoke # MCP stdio end-to-end
npm run bench # full vs adaptive
npm run bench:compare # tool surface, startup, RSS
npm run bench:llm # model-level run, OpenAI-compatible endpoint
BROWSER_MCP_DEBUG=1 node dist/index.js
```
Config file: `~/.config/browser-mcp/config.json`. Example: [`cat-burglar.config.example.json`](cat-burglar.config.example.json). Environment variables override it.
Layout:
```
src/
config.ts env and config file
profiles.ts local / flash / frontier budgets
security.ts SSRF, origins, tracker blocking, route guard
risk.ts high-impact action classification
memory.ts per-origin hints and workflows
diff.ts stable-ref control and text diffs
session.ts engines, tabs, capture, observation, traces
page-scripts.ts snapshot, markdown, stealth probe, vitals
markdown.ts Readability-lite HTML to Markdown
tools/ one module per tool group
data/ generated tracker blocklist
bench/
fixture-server.mjs hermetic app with state verification
tasks.mjs 12 deterministic tasks
run.mjs full vs adaptive report
compare-servers.mjs measured server comparison
llm-agent.mjs optional model policy adapter
docs/research/ evidence review
```
## Credits
| Source | License | What we used |
| --- | --- | --- |
| [Obscura](https://github.com/h4ckf0r0day/obscura) | Apache-2.0 | Ref-token snapshot ideas, markdown and extract semantics, SSRF posture, tracker list data |
| [Playwright MCP](https://github.com/microsoft/playwright-mcp) | Apache-2.0 | Accessibility-first snapshots, persistent profiles, origin and file policy |
| [Chrome DevTools MCP](https://github.com/ChromeDevTools/chrome-devtools-mcp) | Apache-2.0 | Performance metrics and traces, network and console inspection |
| [Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) | Apache-2.0 | Stealth Playwright driver |
| [browser-use](https://github.com/browser-use/browser-use) | MIT | Agent action vocabulary and humanized interaction ideas |
| [unslop](https://github.com/theclaymethod/unslop) | MIT | Writing checks that removed the em dashes and stock phrasing from this README |
The tracker list is the only copied data asset. See [`NOTICE`](NOTICE).
## Status
Version 0.2.0. The local benchmark covers 12 tasks. Public WebArena, Online-Mind2Web, and prompt-injection runs are next. Issues and counterexamples are welcome, especially from models other than the one this was built against.
## License
MIT. See [`LICENSE`](LICENSE).
TDQS
Scored across 30 tools
Most tools have clearly distinct purposes, and the descriptions actively disambiguate the risky pairs (fill vs type, snapshot vs markdown vs extract vs evaluate, click target modes). The main residual overlap is among the several read/extract tools, where an agent must reason about which output format fits, but the guidance is explicit enough to make a correct choice.
All names are single lowercase words with no camelCase/snake_case mixing, which is readable and predictable in style. However, the set mixes action verbs (navigate, click, fill, evaluate) with bare nouns that bundle multiple operations (tabs, cookies, storage, memory, network, perf), so there is no single verb_noun convention to extrapolate from.
At 30 tools this is on the heavy side, and some consolidation is plausible (back/forward/reload could be one history tool; cookies/storage partially overlap, as do screenshot/pdf/snapshot). Still, the surface is a full browser automation stack with stealth, perf, network, and memory subsystems, so nearly every tool maps to a real capability rather than padding.
Coverage is broad: navigation, interaction, extraction, media capture, session persistence, diagnostics, stealth checks, and workflow memory are all present, so there are few dead ends. Gaps are minor but real — no dialog/alert handling, no iframe/frame-scoped operations, and no drag-and-drop or multi-select gesture support.