Rever Browser
Drive a running Rever Browser app from this Claude Code session to get the user what they want out of the web. Your default way of working is to operate the browser directly — navigate, type, click — and read results straight from the rendered DOM. On top of that you can reverse-engineer web APIs (read captured traffic, decode tokens, hook scripts, produce reproducible client code) when a task genuinely calls for it. All of this is exposed through ~140 MCP tools.
DOM-first is the default. API reversing is a powerful mode you switch into on demand — see "Strategy" below.
Prerequisite
The Rever Browser app must be running — it publishes its MCP endpoint on startup to:
~/Library/Application Support/rever-browser/mcp-endpoint.json
The port is OS-assigned (changes each launch), so always resolve it from that file. If the file is missing, tell the user to launch Rever Browser first.
Connect
Preferred — register as native MCP tools (tools appear as mcp__rever__*):
claude mcp add --transport http rever "$(python3 -c "import json,os;print(json.load(open(os.path.expanduser('~/Library/Application Support/rever-browser/mcp-endpoint.json')))['url'])")"
Re-run this whenever Rever Browser restarts (the port changes). MCP servers load at session start, so if the tools aren't visible yet, start a fresh claude session.
Quick calls without registration — this skill bundles a helper (rever.py, in this skill's folder). It resolves the endpoint each call, so it works immediately in the current session:
python3 <this-skill-dir>/rever.py --list # list tools
python3 <this-skill-dir>/rever.py browser_navigate '{"url":"https://example.com"}'
python3 <this-skill-dir>/rever.py list_requests
Iron rule: investigate before you ask
The user has a live browser tab open next to this session. Whenever a request is ambiguous — "this", "here", "what failed", "fix it", "the page", "just now" — your FIRST action is always to look, not to ask a clarifying question. Cheapest first, in order:
browser_snapshot — current URL, title, accessibility tree. Tells you what's on screen.
list_requests({ since: <recent ms> }) — what just got captured.
console_logs({ since }) / console_exceptions() — JS errors that just happened.
Ask a clarifying question only after you have looked and still cannot reasonably guess the intent. "What do you mean?" is almost never the right first reply here.
Strategy: DOM-first by default
Your first instinct is to drive the browser and read the rendered page, not to hunt for a JSON API. Most "search X", "get the results", "pull the list", "what does this page show" tasks are solved entirely in the DOM.
Default flow (DOM-first):
- Drive the page to the state you need —
browser_navigate, then browser_type (+ submit), browser_click, browser_scroll. One step at a time; each interaction returns a fresh snapshot.
- Read the result from the rendered DOM —
dom_extract for structured lists (results, tables, cards), browser_snapshot for an overview, browser_evaluate for anything custom.
- Present the extracted data. You do not need an API to answer.
Interact like a human; read with JS. To act on the page — click, type, submit — always use browser_click / browser_type (on a snapshot rN ref) or browser_click_selector / browser_type_selector (on a CSS selector when you have no ref). These move a real cursor and fire trusted events, and survive bot detection. Never click or set values via browser_evaluate / raw JS (el.click(), el.value = …, dispatchEvent) — it skips the cursor, fires untrusted events, and silently fails on framework-controlled inputs. browser_evaluate and dom_extract are read-only. If a snapshot is too big for a clean ref, do not fall back to JS — locate the element by CSS selector and use the _selector tools.
Stuck on what the page is doing? Use vision_judge — it screenshots the page and asks a vision model. Good for "did my search actually run / did results render?", spotting a modal/captcha/ad overlay, or reading text baked into images.
Server-rendered (SSR) sites — do not get stuck. Many sites (portals, news, gov, older sites) render everything server-side: the traffic is mostly document/HTML with few or no JSON XHR/Fetch calls. On these, there is no JSON API to reverse — the DOM is the answer. Never conclude "there's no API so this is hard" and stop. dom_extract the rendered result and deliver it.
When to switch into API-reversing mode. Only when:
- The user explicitly asks — "analyze the API", "reverse this endpoint", "make a client/reproducible script", "how does this get signed".
- DOM-first genuinely can't reach the goal — bulk collection across pagination/infinite scroll, reproducing an authenticated call programmatically, or the data only exists in an XHR/Fetch JSON payload.
One-line rule: need the result in front of you? → DOM-first. Need to reproduce/automate it in code? → API mode.
Tool map
- Browser —
browser_navigate, browser_snapshot (accessibility tree with rN refs), browser_click/browser_type/browser_scroll (by ref), browser_click_selector/browser_type_selector (by CSS), browser_wait_for, browser_screenshot, browser_evaluate, vision_judge, set_viewport, browser_tabs.
- DOM read —
dom_extract (structured scrape by selector), browser_evaluate (read-only custom JS).
- Traffic —
list_requests, get_request, get_request_initiator, request_diff, find_api_base, fetch_body, har_export, replay_request.
- Bundle analysis —
list_scripts, grep_script/grep_scripts, extract_context, detect_bundler, deobfuscate_script / deob_auto, get_original_source, list_sources, resolve_source.
- Auth & crypto —
auth_dump, decode_token, protobuf_decode, crypto_trace_start/list/stop, crypto_chain, hmac_compute, hash_iter, scan_secrets.
- Reproduce & fuzz —
repeater_send (browser-context replay), replay_request (Node fetch), graphql_introspect, burst_send, intruder_run, crlf_test, lfi_probe, path_probe, payload_probe, create_macro / export_python_client.
- Intercept & override —
intercept_add/pending/continue/fulfill/fail/remove, override_add/list/remove, inject_run_now/add/toggle/remove, header_preset_save/apply/disable.
- Debugger —
bp_add, bp_eval_in_frame, bp_step_*, bp_resume, bp_status.
- Console / storage / cookies —
console_logs/eval/exceptions, cookie_list/set/delete, local_storage_*, session_storage_*, sw_list/unregister/caches.
- WebSocket —
list_websockets, get_ws_frames, ws_send.
- Findings —
finding_add, finding_list, finding_export, security_inspect.
Run --list for the full, current set.
Workflow defaults
- DOM before API. First ask "can I just read this off the page?" A single filtered
list_requests({ since }) as recon (per the iron rule) is always fine — but don't do API-reversing work (diff / replay / codegen) unless you're in API mode.
- One step at a time in browser control: navigate → wait/snapshot → confirm → next. Don't chain 5 actions blindly.
- Filter, don't dump. Always pass
host, since, methodOrType, or limit to list_requests — the store holds ~500 entries and an unfiltered dump buries the signal. Skip static assets (.css, .js, .png, .woff, ad/analytics) unless asked. API candidates are usually XHR/Fetch with a JSON body/response, fired right after a user action, often carrying Authorization or a cookie session.
- Bot-detection sites: rely on the user's own session — never automate login on Instagram, X, etc.
- Confirm before a real side effect. You are driving the user's real authenticated session, so one call can place an order, send a message, delete data, or hammer a target. Before any of these, state in one line what will happen and to which target, then wait for the user:
replay_request / repeater_send of a non-GET / state-changing request (POST/PUT/PATCH/DELETE, or a GET with obvious side effects).
- Delivering a client (
export_python_client) that reproduces a non-idempotent call — say plainly it will act on their account if run.
- Any active probe:
intruder_run, burst_send, payload_probe, crlf_test, path_probe, lfi_probe — name the target and rough request volume, and agree a scope/throttle first.
- Read-only work never needs this gate: navigate, snapshot,
dom_extract, list_requests/get_request, a single GET replay, decoding, source grep. Don't get timid on ordinary recon — the gate is only for state changes and aggressive probing.
- Tear down your hooks when done.
inject_add snippets keep running on every page load, intercept_add in block/modify keeps stalling/rewriting traffic, bp_add leaves execution paused, override_add / dom_set_* keep altering the page — all silently break the user's normal browsing. When the task that set them up is finished, remove/toggle them off and resume any paused request. Leave the browser as you found it.
- Deliverables land in your scratch cwd — standalone Python/Node clients, curl one-liners, HAR/JSON dumps. Don't scatter files into the user's project unasked.
Reproducing a signed/encrypted request — first-divergence discipline
When your local repro of a signed/encrypted value doesn't match what the real browser produced, never report "almost working." Instead:
- Pin the first point of divergence — param ordering, seconds-vs-ms timestamp, salt position/encoding, key-derivation input, trailing newline.
- Use
console_eval to compare intermediate values, and crypto_trace_start/list/stop to recover the real key/message/signature at call time (start tracing → trigger the signing action → list). Misses hand-rolled pure-JS crypto.
- Record each environment patch you apply to close the gap, then state explicitly whether you now have a stable repro and what gap (if any) remains — turning "almost done" into a concrete next step.
Don't talk yourself out of the right move (excuse → rebuttal)
| The excuse you'll reach for |
The rule (do this instead) |
"Snapshot is huge / no clean rN ref — I'll just el.click() or el.value=… via browser_evaluate." |
Forbidden. Raw JS fires untrusted events and silently fails on framework inputs. Find the element by CSS selector and use browser_click_selector / browser_type_selector. |
| "No JSON XHR/Fetch here, so this page has no API — I'll stop and say it's hard." |
SSR means the DOM is the answer. Don't stop. dom_extract the rendered result and deliver it. |
| "The request is vague ('this', 'why broken', 'fix it') — let me ask what they mean." |
Look first. browser_snapshot → list_requests({since}) → console_exceptions(), then answer. A clarifying question is the last resort. |
"Let me list_requests with no filter and read everything to be safe." |
Filter, don't dump. Always pass host / since / methodOrType / limit. |
"They asked for block/modify, but log is safer — I'll just log it." |
Never silently downgrade a destructive parameter the user asked for. Requested mode:"block" → use block. If risky, say so and let them decide. |
| "I've basically got it — I'll paste the client and move on." |
Run the self-audit below first. "Basically done" with a leaked token or an uncited claim is not done. |
Before you claim it's done (self-audit)
Before you say "done", silently confirm each — if any answer is "no", fix it before replying:
- Evidence cited? Every claim about a request carries its
requestId; every DOM result carries the CSS selector + page URL.
- Secrets masked? No full token, cookie, password, card number, or national ID in output (
Authorization: Bearer ********).
- Reproducible? A delivered client/script runs as-is (real endpoint, required headers) — not a sketch.
- Read vs. done? You actually ran the tools and saw the result — not "I would run X".
- Side effect confirmed? If this turn would replay a state-changing request, deliver a non-idempotent client, or run an active probe, you told the user what it does to which target and got a go-ahead first.
Deliverables & output style
DOM extraction (the default ask): present the data as a table/list, cite the CSS selector (and page URL) so it's reproducible, and say how far you paginated/scrolled if you collected more.
API client (only when asked to analyze the API): confirm which request you picked (cite requestId); table of endpoint · method · required headers · body schema · response schema; one client function in the language requested (default: Python requests).
- Bullets and tables, not prose. Cite
requestId for every claim. Code blocks carry a language tag. Keep responses tight — the user can ask for depth.
- Always mask secrets in output. Never echo full tokens, passwords, card numbers, national IDs.
Troubleshooting
- "No MCP endpoint found" → Rever Browser isn't running. Launch the app.
- Tools missing after
claude mcp add → MCP loads at session start; open a new claude session, or use the rever.py helper for the current one.
- Connection refused / stale → Rever restarted and the port changed. Re-run the connect command.
1---2name: rever3description: Reverse-engineer web APIs by driving a running Rever Browser instance — connect to its published MCP endpoint and use its browser-automation, network-capture, and JS-bundle-analysis tools. Use when the user types /rever, asks to reverse or analyze a website's API, capture or inspect its network traffic, deobfuscate its JavaScript, or reproduce its requests with Rever Browser.4---56# Rever Browser78Drive a running **Rever Browser** app from this Claude Code session to get the user what they want out of the web. Your default way of working is to **operate the browser directly** — navigate, type, click — and read results straight from the rendered DOM. On top of that you can **reverse-engineer web APIs** (read captured traffic, decode tokens, hook scripts, produce reproducible client code) when a task genuinely calls for it. All of this is exposed through ~140 MCP tools.910**DOM-first is the default. API reversing is a powerful mode you switch into on demand** — see "Strategy" below.1112## Prerequisite1314The **Rever Browser app must be running** — it publishes its MCP endpoint on startup to:1516```17~/Library/Application Support/rever-browser/mcp-endpoint.json18```1920The port is OS-assigned (changes each launch), so always resolve it from that file. If the file is missing, tell the user to launch Rever Browser first.2122## Connect2324**Preferred — register as native MCP tools** (tools appear as `mcp__rever__*`):2526```bash27claude mcp add --transport http rever "$(python3 -c "import json,os;print(json.load(open(os.path.expanduser('~/Library/Application Support/rever-browser/mcp-endpoint.json')))['url'])")"28```2930Re-run this whenever Rever Browser restarts (the port changes). MCP servers load at session start, so if the tools aren't visible yet, start a fresh `claude` session.3132**Quick calls without registration** — this skill bundles a helper (`rever.py`, in this skill's folder). It resolves the endpoint each call, so it works immediately in the current session:3334```bash35python3 <this-skill-dir>/rever.py --list # list tools36python3 <this-skill-dir>/rever.py browser_navigate '{"url":"https://example.com"}'37python3 <this-skill-dir>/rever.py list_requests38```3940## Iron rule: investigate before you ask4142The user has a live browser tab open next to this session. **Whenever a request is ambiguous — "this", "here", "what failed", "fix it", "the page", "just now" — your FIRST action is always to look, not to ask a clarifying question.** Cheapest first, in order:43441. `browser_snapshot` — current URL, title, accessibility tree. Tells you what's on screen.452. `list_requests({ since: <recent ms> })` — what just got captured.463. `console_logs({ since })` / `console_exceptions()` — JS errors that just happened.4748Ask a clarifying question only after you have looked and still cannot reasonably guess the intent. "What do you mean?" is almost never the right first reply here.4950## Strategy: DOM-first by default5152Your first instinct is to **drive the browser and read the rendered page**, not to hunt for a JSON API. Most "search X", "get the results", "pull the list", "what does this page show" tasks are solved entirely in the DOM.5354**Default flow (DOM-first):**551. Drive the page to the state you need — `browser_navigate`, then `browser_type` (+ submit), `browser_click`, `browser_scroll`. One step at a time; each interaction returns a fresh snapshot.562. Read the result from the rendered DOM — `dom_extract` for structured lists (results, tables, cards), `browser_snapshot` for an overview, `browser_evaluate` for anything custom.573. Present the extracted data. You do **not** need an API to answer.5859**Interact like a human; read with JS.** To *act* on the page — click, type, submit — always use `browser_click` / `browser_type` (on a snapshot `rN` ref) or `browser_click_selector` / `browser_type_selector` (on a CSS selector when you have no ref). These move a real cursor and fire trusted events, and survive bot detection. **Never click or set values via `browser_evaluate` / raw JS** (`el.click()`, `el.value = …`, `dispatchEvent`) — it skips the cursor, fires untrusted events, and silently fails on framework-controlled inputs. `browser_evaluate` and `dom_extract` are **read-only**. If a snapshot is too big for a clean ref, do **not** fall back to JS — locate the element by CSS selector and use the `_selector` tools.6061**Stuck on what the page is doing?** Use `vision_judge` — it screenshots the page and asks a vision model. Good for "did my search actually run / did results render?", spotting a modal/captcha/ad overlay, or reading text baked into images.6263**Server-rendered (SSR) sites — do not get stuck.** Many sites (portals, news, gov, older sites) render everything server-side: the traffic is mostly `document`/HTML with few or no JSON XHR/Fetch calls. On these, **there is no JSON API to reverse — the DOM is the answer.** Never conclude "there's no API so this is hard" and stop. `dom_extract` the rendered result and deliver it.6465**When to switch into API-reversing mode.** Only when:66- The user **explicitly** asks — "analyze the API", "reverse this endpoint", "make a client/reproducible script", "how does this get signed".67- DOM-first genuinely can't reach the goal — bulk collection across pagination/infinite scroll, reproducing an authenticated call programmatically, or the data only exists in an XHR/Fetch JSON payload.6869**One-line rule:** need the result in front of you? → DOM-first. Need to reproduce/automate it in code? → API mode.7071## Tool map7273- **Browser** — `browser_navigate`, `browser_snapshot` (accessibility tree with `rN` refs), `browser_click`/`browser_type`/`browser_scroll` (by ref), `browser_click_selector`/`browser_type_selector` (by CSS), `browser_wait_for`, `browser_screenshot`, `browser_evaluate`, `vision_judge`, `set_viewport`, `browser_tabs`.74- **DOM read** — `dom_extract` (structured scrape by selector), `browser_evaluate` (read-only custom JS).75- **Traffic** — `list_requests`, `get_request`, `get_request_initiator`, `request_diff`, `find_api_base`, `fetch_body`, `har_export`, `replay_request`.76- **Bundle analysis** — `list_scripts`, `grep_script`/`grep_scripts`, `extract_context`, `detect_bundler`, `deobfuscate_script` / `deob_auto`, `get_original_source`, `list_sources`, `resolve_source`.77- **Auth & crypto** — `auth_dump`, `decode_token`, `protobuf_decode`, `crypto_trace_start/list/stop`, `crypto_chain`, `hmac_compute`, `hash_iter`, `scan_secrets`.78- **Reproduce & fuzz** — `repeater_send` (browser-context replay), `replay_request` (Node fetch), `graphql_introspect`, `burst_send`, `intruder_run`, `crlf_test`, `lfi_probe`, `path_probe`, `payload_probe`, `create_macro` / `export_python_client`.79- **Intercept & override** — `intercept_add`/`pending`/`continue`/`fulfill`/`fail`/`remove`, `override_add`/`list`/`remove`, `inject_run_now`/`add`/`toggle`/`remove`, `header_preset_save`/`apply`/`disable`.80- **Debugger** — `bp_add`, `bp_eval_in_frame`, `bp_step_*`, `bp_resume`, `bp_status`.81- **Console / storage / cookies** — `console_logs`/`eval`/`exceptions`, `cookie_list`/`set`/`delete`, `local_storage_*`, `session_storage_*`, `sw_list`/`unregister`/`caches`.82- **WebSocket** — `list_websockets`, `get_ws_frames`, `ws_send`.83- **Findings** — `finding_add`, `finding_list`, `finding_export`, `security_inspect`.8485Run `--list` for the full, current set.8687## Workflow defaults8889- **DOM before API.** First ask "can I just read this off the page?" A single filtered `list_requests({ since })` as recon (per the iron rule) is always fine — but don't do API-reversing *work* (diff / replay / codegen) unless you're in API mode.90- **One step at a time** in browser control: navigate → wait/snapshot → confirm → next. Don't chain 5 actions blindly.91- **Filter, don't dump.** Always pass `host`, `since`, `methodOrType`, or `limit` to `list_requests` — the store holds ~500 entries and an unfiltered dump buries the signal. Skip static assets (`.css`, `.js`, `.png`, `.woff`, ad/analytics) unless asked. **API candidates** are usually XHR/Fetch with a JSON body/response, fired right after a user action, often carrying `Authorization` or a cookie session.92- **Bot-detection sites**: rely on the user's own session — never automate login on Instagram, X, etc.93- **Confirm before a real side effect.** You are driving the user's *real authenticated session*, so one call can place an order, send a message, delete data, or hammer a target. **Before** any of these, state in one line what will happen and to which target, then wait for the user:94 - `replay_request` / `repeater_send` of a **non-GET / state-changing** request (POST/PUT/PATCH/DELETE, or a GET with obvious side effects).95 - Delivering a client (`export_python_client`) that reproduces a **non-idempotent** call — say plainly it will act on their account if run.96 - Any active probe: `intruder_run`, `burst_send`, `payload_probe`, `crlf_test`, `path_probe`, `lfi_probe` — name the target and rough request volume, and agree a scope/throttle first.97 - **Read-only work never needs this gate**: navigate, snapshot, `dom_extract`, `list_requests`/`get_request`, a single GET replay, decoding, source grep. Don't get timid on ordinary recon — the gate is only for state changes and aggressive probing.98- **Tear down your hooks when done.** `inject_add` snippets keep running on every page load, `intercept_add` in `block`/`modify` keeps stalling/rewriting traffic, `bp_add` leaves execution paused, `override_add` / `dom_set_*` keep altering the page — all silently break the user's normal browsing. When the task that set them up is finished, remove/toggle them off and resume any paused request. Leave the browser as you found it.99- **Deliverables land in your scratch cwd** — standalone Python/Node clients, curl one-liners, HAR/JSON dumps. Don't scatter files into the user's project unasked.100101## Reproducing a signed/encrypted request — first-divergence discipline102103When your local repro of a signed/encrypted value doesn't match what the real browser produced, never report "almost working." Instead:1041. Pin the **first** point of divergence — param ordering, seconds-vs-ms timestamp, salt position/encoding, key-derivation input, trailing newline.1052. Use `console_eval` to compare intermediate values, and `crypto_trace_start`/`list`/`stop` to recover the real key/message/signature at call time (start tracing → trigger the signing action → list). Misses hand-rolled pure-JS crypto.1063. Record each environment patch you apply to close the gap, then state explicitly whether you now have a **stable** repro and what gap (if any) remains — turning "almost done" into a concrete next step.107108## Don't talk yourself out of the right move (excuse → rebuttal)109110| The excuse you'll reach for | The rule (do this instead) |111|---|---|112| "Snapshot is huge / no clean `rN` ref — I'll just `el.click()` or `el.value=…` via `browser_evaluate`." | **Forbidden.** Raw JS fires untrusted events and silently fails on framework inputs. Find the element by CSS selector and use `browser_click_selector` / `browser_type_selector`. |113| "No JSON XHR/Fetch here, so this page has no API — I'll stop and say it's hard." | **SSR means the DOM *is* the answer.** Don't stop. `dom_extract` the rendered result and deliver it. |114| "The request is vague ('this', 'why broken', 'fix it') — let me ask what they mean." | **Look first.** `browser_snapshot` → `list_requests({since})` → `console_exceptions()`, then answer. A clarifying question is the last resort. |115| "Let me `list_requests` with no filter and read everything to be safe." | **Filter, don't dump.** Always pass `host` / `since` / `methodOrType` / `limit`. |116| "They asked for `block`/`modify`, but `log` is safer — I'll just log it." | **Never silently downgrade a destructive parameter the user asked for.** Requested `mode:"block"` → use `block`. If risky, say so and let them decide. |117| "I've basically got it — I'll paste the client and move on." | Run the self-audit below first. "Basically done" with a leaked token or an uncited claim is not done. |118119## Before you claim it's done (self-audit)120121Before you say "done", silently confirm each — if any answer is "no", fix it *before* replying:122123- **Evidence cited?** Every claim about a request carries its `requestId`; every DOM result carries the CSS `selector` + page URL.124- **Secrets masked?** No full token, cookie, password, card number, or national ID in output (`Authorization: Bearer ********`).125- **Reproducible?** A delivered client/script runs as-is (real endpoint, required headers) — not a sketch.126- **Read vs. done?** You actually ran the tools and saw the result — not "I would run X".127- **Side effect confirmed?** If this turn would replay a state-changing request, deliver a non-idempotent client, or run an active probe, you told the user what it does to which target and got a go-ahead first.128129## Deliverables & output style130131**DOM extraction (the default ask):** present the data as a table/list, cite the CSS `selector` (and page URL) so it's reproducible, and say how far you paginated/scrolled if you collected more.132133**API client (only when asked to analyze the API):** confirm which request you picked (cite `requestId`); table of endpoint · method · required headers · body schema · response schema; one client function in the language requested (default: Python `requests`).134135- **Bullets and tables, not prose.** Cite `requestId` for every claim. Code blocks carry a language tag. Keep responses tight — the user can ask for depth.136- **Always mask secrets** in output. Never echo full tokens, passwords, card numbers, national IDs.137138## Troubleshooting139140- **"No MCP endpoint found"** → Rever Browser isn't running. Launch the app.141- **Tools missing after `claude mcp add`** → MCP loads at session start; open a new `claude` session, or use the `rever.py` helper for the current one.142- **Connection refused / stale** → Rever restarted and the port changed. Re-run the connect command.