zimreaper
Provides integration with OpenAI models to power the built-in autonomous agent loop for browser tasks, allowing users to specify a provider and model (e.g., --provider openai --model gpt-4.1) via the browser_task tool.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@zimreaperopen app.target.com, click login, and capture the network log"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
A real Chromium browser that an AI agent drives with a real mouse, a real keyboard and a real wheel — over MCP — built for bug hunting.
Install · Connect an agent · Tools · Bug bounty · Trust boundary · Troubleshooting
Most "AI browser" projects call element.click() from a script and hope the page believes it. Frameworks check isTrusted, bot walls score the event trail, and half the app never notices. ZIMREAPER drives Chromium the way a person does: Input.dispatchMouseEvent with real press/release/click counts, real key events with proper codes and modifiers, real wheel deltas, real drag paths. Every click you see in the audit log actually happened in the compositor.
On top of that it gives the agent senses a person does not have: a token-budgeted accessibility snapshot with stable refs, diffs instead of re-reads, a network log of the XHRs the SPA actually made, forked sessions that share one login, and replayable traces that refuse to click when the page drifted.
Why the real-input part matters
Mechanism | DOM automation | ZIMREAPER |
Click path |
|
|
Framework handlers | often skipped | fire, because the event came from the browser |
|
|
|
Bot signals | no pointer trail | mouse moves, wheel deltas, key timings |
Drag & drop | synthesised | real path with intermediate moves |
What the agent sees | HTML string | AX snapshot + refs + diff + network + screenshot |
Can a human take over | no | yes — Take over button, kill switch on Esc |
Related MCP server: OpenBrowser
Architecture
One control plane on ws://127.0.0.1:7420, one MCP server with 21 tools, one Chromium. The agent gets tools, not a shell. The human keeps a window, a take-over button and a kill switch.
Install
One file, no installer, no root. Build it with npm run package:linux (see Build from source), or grab the binary from Releases.
chmod +x ZimReaper-0.1.0-x86_64.AppImage
mv ZimReaper-0.1.0-x86_64.AppImage ~/Applications/
~/Applications/ZimReaper-0.1.0-x86_64.AppImageA browser window opens and a control server starts. First run writes your state directory:
~/.zimreaper/
├── endpoint.json # {"url":"ws://127.0.0.1:7420","pid":…} how clients find it
├── control.token # bearer token, mode 0600
├── policy.json # what the agent may do, per origin
├── audit/ # hash-chained JSONL, one file per day
└── artifacts/ # screenshots and captured textCheck it is alive:
~/Applications/ZimReaper-0.1.0-x86_64.AppImage infosession s8d373bdbffcf
backend electron
policy standard
version 0.1.0 (protocol 0.1.0)
ID A TITLE URL
t1 * Google https://www.google.com/Leave the window open while an agent works. That window is the browser the agent is driving — close it and the session ends.
No FUSE? Run
./ZimReaper-0.1.0-x86_64.AppImage --appimage-extract-and-run. Kernel without user namespaces (containers)? AddZIMREAPER_NO_SANDBOX=1.
Connect your agent
Every client speaks the same MCP server. Point the command at the AppImage with the single argument mcp. Add --launch when there is no human at the window and the agent should own a headless browser.
Client | How it connects | Config file |
Claude Desktop | stdio MCP server |
|
Claude Code |
| project or user scope |
Hermes |
|
|
Codex CLI |
|
|
Cursor |
|
|
Cline / Continue | extension MCP settings | per-extension |
VS Code |
|
|
Anything else | stdio, | — |
{
"mcpServers": {
"zimreaper": {
"command": "/home/YOU/Applications/ZimReaper-0.1.0-x86_64.AppImage",
"args": ["mcp"]
}
}
}claude mcp add zimreaper -- /home/YOU/Applications/ZimReaper-0.1.0-x86_64.AppImage mcphermes mcp add zimreaper \
--command /home/YOU/Applications/ZimReaper-0.1.0-x86_64.AppImage \
--args mcp[mcp_servers.zimreaper]
command = "/home/YOU/Applications/ZimReaper-0.1.0-x86_64.AppImage"
args = ["mcp"]{
"mcpServers": {
"zimreaper": {
"command": "/home/YOU/Applications/ZimReaper-0.1.0-x86_64.AppImage",
"args": ["mcp"]
}
}
}{
"servers": {
"zimreaper": {
"type": "stdio",
"command": "/home/YOU/Applications/ZimReaper-0.1.0-x86_64.AppImage",
"args": ["mcp"]
}
}
}# drive the browser yourself over the control plane
zimreaper info # session, backend, policy, open tabs
zimreaper attach http://127.0.0.1:9222 # wrap an existing Chromium debug endpoint
# or let ZIMREAPER run the loop with your own model
zimreaper run "find the cheapest 27in monitor on this site" \
--url https://example.com --provider openai --model gpt-4.1Tell the agent how to use it
MCP gives the agent tools, not instincts. Paste this into your CLAUDE.md, AGENTS.md or system prompt — it is the difference between a useful run and 40 wasted snapshots:
You have a real browser through the zimreaper MCP server.
1. browser_snapshot before acting. Refs die with the next snapshot.
2. Prefer browser_diff over re-snapshotting — you only need what changed.
3. Click with a ref. Use browser_extract for tables, not 40 clicks.
4. browser_wait for text or selectors. Never sleep-loop.
5. Do not browser_eval what a click can do honestly.
6. On BLOCKED_BY_POLICY, stop and call browser_ask_human.
7. After two failures on the same element, re-snapshot and rethink.
8. Report the URL and the text you actually verified.The full version, with the failure each rule prevents, is in docs/agent-prompt.md.
The 21 tools
Group | Tools |
Perception |
|
Input |
|
Navigation |
|
Network & session |
|
Escape hatches |
|
Tool | Behaviour |
| Go to a URL, wait for a lifecycle event, snapshot after |
| Page as compact ref-tagged text; refs are generation-scoped |
| Only refs and text that changed since a snapshot id |
| PNG of page or clip; |
| Structured rows from repeating elements: row selector + field selectors |
| Real mouse click by ref, CSS selector or viewport point |
| Type into a target, optionally clear first and submit with Enter |
| Key or chord: |
| Move the real cursor over a target and hold |
| Drag with intermediate mouse moves, not a teleport |
| Real wheel deltas on the page or a target |
|
|
|
|
| Selector, text, URL fragment, network idle, expression, or a ref disappearing |
| Resize, optionally with mobile device scale |
|
|
| Read, set, clear — for the tab or a URL |
| Clone the current login into independent tabs |
| Evaluate JS in the page (policy-gated, audited) |
| Run a full autonomous task with the built-in loop (needs a model) |
| Pause and hand the keyboard to the human |
Built for bug bounty
The browser is where the bug is. ZIMREAPER is shaped around that:
Proxy-first capture. Point it at your proxy and everything the browser does — including loopback — goes through it:
ZIMREAPER_PROXY=http://127.0.0.1:8080 ZIMREAPER_INSECURE=1 \
~/Applications/ZimReaper-0.1.0-x86_64.AppImage--proxy-bypass-list=<-loopback> is applied for you, because Chromium otherwise silently skips the proxy for 127.0.0.1 — exactly where local API targets live. Verified against mitmproxy for both HTTP and TLS-intercepted HTTPS.
Scope as code. Put the program's scope in ~/.zimreaper/policy.json and out-of-scope hosts are refused before a request leaves the machine:
{
"mode": "strict",
"origins": {
"app.target.com": { "tier": "transact", "allowUploads": true },
"api.target.com": { "tier": "interact" },
"*.target.com": { "tier": "read" }
},
"maxActionsPerMinute": 120
}Parallel authz tests from one login. browser_fork_session clones the authenticated state into independent tabs — test the IDOR as user B while user A stays logged in, without re-authenticating.
Traffic the UI hides. browser_network op=log returns the XHRs, and op=body returns the payloads. Money fields, hidden params and client-side trust decisions are visible.
PoCs that replay. Record a trace, replay it in a fresh tab, and the player re-binds refs by role, name and ordinal. If the page drifted, it reports a divergence instead of clicking something else:
zimreaper replay ~/hunts/acme/login.jsonl --url https://app.target.com/login6/6 steps replayed in 1951msEvidence that holds up. Every action is in a hash-chained audit log (zimreaper audit verify), screenshots are on disk, and the trace is a file you can attach to the report.
This is not a scanner. It finds nothing on its own and it will not make you anonymous. It is the browser half of a manual hunt, with an agent doing the boring parts while you watch. Full workflow in docs/bug-bounty.md.
The trust boundary
Three tiers per origin, three global modes, one kill switch.
Tier | The agent may |
| navigate, snapshot, extract, screenshot |
| click, type, scroll, drag — but not submit payments or delete |
| everything, including destructive patterns |
Mode | Default for unknown origins |
|
|
|
|
|
|
Exact hosts win over *.domain wildcards, and a wildcard covers subdomains but not the apex — so list the apex explicitly. Anything unmatched falls back to the mode default. Destructive actions raise an approval request that surfaces in the HUD. Take over pauses the agent and hands you the mouse. Esc kills the session immediately. The audit chain is append-only and verifiable:
zimreaper audit verifyaudit chain ok (1284 entries) in /home/you/.zimreaperHeadless and server use
For a box with no monitor — a VPS, a container, a CI job:
ZIMREAPER_SERVE=1 ZIMREAPER_NO_SANDBOX=1 ~/Applications/ZimReaper-0.1.0-x86_64.AppImageThe window exists but is not shown (opacity 0, skipTaskbar). It has to stay mapped: an unmapped Electron window never acknowledges Input.dispatchMouseEvent, so agent clicks would hang until timeout. Agents then attach over the control plane exactly as they do locally.
Troubleshooting
ls -l ~/.zimreaper/ # endpoint.json and control.token should exist
zimreaper info # prints the live url, pid and sessionIf the AppImage was started with a custom ZIMREAPER_HOME, clients must use the same value.
Usually a policy refusal or a stale ref. Check the audit tail — a BLOCKED_BY_POLICY entry names the origin and the rule. Otherwise the ref is from an older snapshot generation: snapshot again.
The client caches the tool list at startup. Restart it after editing the MCP config. Then check the server alone:
ZIMREAPER_HOME=/tmp/zr-check ~/Applications/ZimReaper-0.1.0-x86_64.AppImage mcp --launchIt should log a control server on an ephemeral port, then wait for JSON-RPC on stdin.
Chromium bypasses proxies for loopback by default; ZIMREAPER passes --proxy-bypass-list=<-loopback> when ZIMREAPER_PROXY is set, so check the proxy is actually listening on the interface you gave it. For HTTPS you need the proxy CA trusted, or ZIMREAPER_INSECURE=1.
dlopen(): error loading libfuse.so.2 → --appimage-extract-and-run.
The SUID sandbox helper binary was found, but is not configured correctly → ZIMREAPER_NO_SANDBOX=1.
Build from source
Node 22+. The AppImage is built by a script that walks the real runtime dependency closure, so no bundler surprises.
git clone https://github.com/Mr-Destroyer/ZIMREAPER.git
cd ZIMREAPER
npm ci
npm run build # tsc -b across all packages
npm test # 229 tests
npm run package:linux # → dist/ZimReaper-0.1.0-x86_64.AppImageVerification scripts, each of which proves something specific:
npm run check:appimage # CLI, MCP --launch, serve mode, attach — 4/4
node scripts/p0-smoke.mjs # protocol + control plane + real input — 14/14
node scripts/mcp-check.mjs # 21 tools over MCP, full login — PASS
node scripts/capabilities-check.mjs # 6/6
node scripts/replay-check.mjs # record, replay, divergence — 4/4packages/protocol frozen wire contract, 40 actions · 14 queries · 20 events
packages/core CDP backend, executor, control server, discovery
packages/perception AX snapshot, stable refs, diff, token budget
packages/policy tiers, approvals, hash-chained audit, vault
packages/replay trace recorder, ref re-binding player, JSONL
packages/agent provider-agnostic loop (OpenAI-compatible + Anthropic)
packages/mcp-server the 21 tools, stdio and HTTP transports
packages/cli mcp | run | attach | info | audit verify | replay
apps/desktop Electron shell, tabs, HUD, take-over, kill switch
fixtures/site the test site every check runs againstAuthor
Mr-Destroyer · github.com/Mr-Destroyer YouTube · @study_hard69
MIT licensed — see LICENSE.
Built with Electron, Chromium and a healthy disrespect for element.click().
This server cannot be deployed
Maintenance
Related MCP Connectors
Control a real browser from AI agents: navigate, click, type, screenshot, read the page.
Live browser debugging for AI assistants — DOM, console, network via MCP.
Hosted browser for AI agents: screenshots, post-JS DOM, console, WCAG. No install, no API key.
Official Browserless MCP server: browser automation, scraping, screenshots and PDFs for AI agents.
Related MCP Servers
- AlicenseAqualityAmaintenanceMCP server that lets AI agents drive your real Chromium browser with your existing signed-in sessions, providing visible, local, and inspectable automation for tasks like navigation, clicking, typing, and form filling.251Apache 2.0
- AlicenseNot gradedqualityAmaintenanceA zero-dependency MCP server that drives a real Chrome browser through a companion extension, enabling AI agents to automate real user sessions with trusted input events, compact accessibility-tree snapshots, and 14 tools for navigation, interaction, scripting, and inspection.206 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents and clients to control a real Chromium browser through MCP tool calls, including navigation, clicking, typing, JavaScript evaluation, screenshots, and DOM snapshots. Supports multiple isolated sessions over authenticated HTTP with no disk writes.Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to control a real, persistent Chrome browser over MCP, perceiving pages as accessibility trees and interacting with elements by their accessible names without automation fingerprints or site-specific glue.983,589 npm1MIT