chrome-hook MCP
by d3sh1n
README.md
# chrome-hook MCP
A local [MCP](https://modelcontextprotocol.io) server that drives real Chromium
browsers (Chrome / Brave) for **multi-account testing** and **full network capture**,
with a real-browser attach path for strict bot protection.
See [CHANGELOG.md](./CHANGELOG.md) for recent behavior changes (optional proxy, no
legacy auth seeds, public-doc hygiene).
Built on **patchright** (a drop-in Playwright with CDP-layer anti-detection patches),
falling back to stock Playwright when patchright is absent.
**Cross-platform** (macOS / Windows / Linux). Chrome and Brave binaries are
**auto-detected** from standard install locations and `PATH` — no hardcoded paths.
The stealth UA is derived from the installed browser's version and the host OS.
> **Local-only.** Everything runs on your machine. Captured traffic, cookies, auth
> state and browser profiles are written to local disk and are **never uploaded**
> anywhere. Do not commit the runtime dirs (see `.gitignore`).
---
## What it does
- **Named browser slots** (`a`, `b`, …) run side by side so you can test two accounts
at once **without logging out** (servers often revoke tokens on logout — use two
slots instead of switching).
- **Project-scoped** auth/profiles (`project=` isolates cookies per site so accounts
never bleed across projects).
- **Full capture** by default: every HTTP(S) request/response + WebSocket frame,
streamed to `network.jsonl`, saved bodies, and an exportable HAR.
- **Strict bot protection**: patchright driver + a real-browser *attach* mode (Plan A)
that gets past managed challenges a cold automated browser can never solve.
- **Optional proxy**: off by default; set `CHROME_HOOK_PROXY` or `proxy=` when needed.
---
## Install
```bash
pip install -r requirements.txt
# patchright ships its own patched driver; using channel="chrome"/"brave" (real
# binaries) means you do NOT need a separate `patchright install chromium` download.
```
Requires a real Google Chrome and/or Brave installed. Their binaries are
auto-located on macOS, Windows, and Linux (standard install dirs + `PATH`).
### Register with your MCP host (e.g. Grok CLI)
```toml
[mcp_servers.chrome-hook]
command = "python3"
# Prefer the installed runtime path (Grok loads this); keep in sync with repo.
args = ["/ABS/PATH/TO/chrome-hook-mcp/server.py"] # or ~/.grok/tools/chrome-hook-mcp/server.py
enabled = true
startup_timeout_sec = 45
tool_timeout_sec = 180
# Proxy is optional (off by default). Uncomment when you need Burp/Clash:
# env = { CHROME_HOOK_PROXY = "http://127.0.0.1:8080" }
```
After editing `server.py`, copy to the installed path if needed, then **restart the MCP host**.
Agent workflows live in companion skill **`chrome-hook-browser`** (`SKILL.md` in this repo and `~/.grok/skills/chrome-hook-browser/`).
---
## Two ways to use it
### 1. Normal launch (most sites)
```text
browser_launch(project="acme", slot="a", headless=false,
start_url="https://example.com/")
browser_launch(project="acme", slot="b", headless=false,
start_url="https://example.com/") # second account, no logout
# ... log in each window the first time ...
browser_persist(slot="a"); browser_persist(slot="b") # save auth
# later runs auto-restore — no re-login while the token is valid
browser_request(..., slot="a")
```
Anti-bot hardening applied automatically: patchright driver; UA auto-matched to the
installed Chrome major version; `locale`/`timezone_id` left native under patchright
(forcing e.g. `en-US` on an Asia machine is itself a Cloudflare mismatch flag); a JS
stealth init-script is injected only on the stock-Playwright fallback.
### 2. Plan A — attach to a real, warm browser (strict bot protection)
Some sites hand a **cold/freshly-launched** automated browser an **unsolvable**
managed challenge — even under patchright, even if you click the checkbox by hand.
The reliable fix is to attach to a genuine, already-warmed browser that you passed
the challenge in.
```text
# step 1 — spawn YOUR real browser with a remote-debugging port + dedicated profile
browser_open_real(project="demo", slot="a",
url="https://example.com/",
port=9222, browser="brave") # browser: "chrome" | "brave"
# -> solve the challenge by hand in the window that opens
# step 2 — MCP connects over CDP and captures the warm session
browser_attach(project="demo", slot="a", port=9222)
```
Why this works, and the details that matter:
- **Brave** (`browser="brave"`) often clears strict challenges where a cold Chrome
profile cannot, thanks to its built-in anti-fingerprinting. The dedicated profile
lives in `real-chrome/<project>/<slot>[-brave]/` and is **reused across runs**, so
once you pass the challenge the clearance cookie persists and you rarely have to
solve it again.
- It uses a **separate profile** from your everyday browser (a running browser locks
its own profile and can't expose CDP on it). Your normal browser is untouched.
- A CDP-attached **pre-existing tab emits no Playwright network events**, so attach
opens a **fresh capture page in the same context** — it inherits the context cookies
(incl. clearance tokens), loads past the challenge with no new prompt, and lands on
the tab's URL. Capture happens on that page.
- `browser_close` on an attached slot **only disconnects** — it never closes your real
browser or its original tab, and never overwrites its cookies with stale auth.
`browser_launch(cdp_url="http://127.0.0.1:9222", …)` is the low-level equivalent of
`browser_attach`.
---
## Tools (27)
**Session / slots**
`browser_set_project` · `browser_sessions` · `browser_focus` · `browser_launch` ·
`browser_status` · `browser_close`
**Plan A (real browser)**
`browser_open_real` — spawn real Chrome/Brave with a debug port ·
`browser_attach` — connect over CDP and capture the warm session
**Auth persistence**
`browser_persist` · `browser_restore` · `browser_storage_save` ·
`browser_storage_load` · `browser_cookies_get` · `browser_cookies_set`
**Page actions**
`browser_navigate` · `browser_snapshot` · `browser_click` · `browser_type` ·
`browser_press` · `browser_wait` · `browser_screenshot` · `browser_eval`
**Network capture / replay**
`browser_network_dump` — events under the `hits` key (`count` = total) ·
`browser_network_set_filter` · `browser_network_set_mode` (`all` | `api`) ·
`browser_export_har` · `browser_request` — issue a request in-context
---
## Proxy (optional)
| Item | Value |
|------|--------|
| Default | **No proxy** (direct) |
| Env | `CHROME_HOOK_PROXY` (optional, e.g. `http://127.0.0.1:8080` Burp) |
| Per-call | `browser_launch(proxy=…)` / `browser_open_real(proxy=…)` |
| Force off | `proxy="none"` or `CHROME_HOOK_PROXY=none` |
| MITM | When proxy is set, cert errors are ignored automatically |
| Attach | Does **not** change the real browser's proxy — set it at `open_real` time |
## On-disk layout
```text
chrome-hook-mcp/
server.py # the MCP server (27 tools)
SKILL.md # companion agent skill (chrome-hook-browser)
requirements.txt
README.md
# --- runtime state under ~/.grok/tools/chrome-hook-mcp/ (git-ignored) ---
profiles/<project>/<slot>/ # Chromium user-data for normal launch
auth/<project>/<slot>.json # storage_state auto save/restore
real-chrome/<project>/<slot>[-brave]/ # Plan A dedicated warm profiles
sessions/<project>_<slot>_…/ # per-run network.jsonl + HAR + bodies
```
Defaults: `auto_restore=true`, `auto_save=true` (save on close),
`capture_mode="all"`, body caps 512 KB HTTP / 64 KB WS, **no proxy** unless
`CHROME_HOOK_PROXY` or per-call `proxy=` is set.
---
## Notes & limits
- **Restart the MCP host after editing `server.py`** — a running process keeps the old
code and won't expose new tools.
- Browser-context only (not other OS processes). One Playwright driver thread; multiple
Chromium/Brave processes are fine.
- Ad-hoc testing via one-shot `python3 -c` hits Playwright's thread affinity
(`greenlet: Cannot switch to a different thread`) and `EPIPE` on repeated
`connect_over_cdp`; the long-lived MCP process is fine. To probe internals in a
script, run your function on the driver thread via `server._PW.submit(fn)`.
- **Security:** never upload captured data, cookies, auth state, or profiles. Keep the
runtime dirs local and out of version control.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues