Skip to main content
Glama
a0yark

JS Reverse MCP

by a0yark
README.md
# JS Reverse MCP

English | [中文](README_zh.md)

> **Pro fork**: Derived from [zhizhuodemao/js-reverse-mcp](https://github.com/zhizhuodemao/js-reverse-mcp) (Patchright stealth core preserved) and selectively ports capabilities from [NoOne-hub/JSReverser-MCP](https://github.com/NoOne-hub/JSReverser-MCP) — hook framework, static deobfuscation, AI analysis, session snapshots, artifacts, case library. See [`LICENSE`](LICENSE) (Apache-2.0) and [`LICENSE.JSReverser-MCP`](LICENSE.JSReverser-MCP) (MIT, NoOne-hub).

A JavaScript reverse engineering MCP server that enables AI coding assistants (Claude, Cursor, Codex) to debug and analyze JavaScript code in web pages.

Built on the [Patchright](https://github.com/nicecaesar/patchright) anti-detection engine with multi-layered anti-bot bypass capabilities, allowing it to work on sites with bot detection such as Zhihu and Google.

## Features

**From upstream (preserved)**
- **Anti-detection browser** — Patchright (Playwright anti-detection fork), 60+ stealth launch arguments
- **Script analysis** — list / search / get / save sources
- **Breakpoint debugging** — `set_breakpoint_on_text`, `break_on_xhr`, `step`, `get_paused_info`
- **Function tracing** — `trace_function` (logpoint mode, doesn't pause)
- **Network & WebSocket** — request initiator stacks, WS message analysis
- **`inject_before_load`** — CDP `Page.addScriptToEvaluateOnNewDocument`

**Pro fork additions**
- **Hook framework** — 5 built-in templates (function/crypto/network/encoding/storage), CDP injection + Symbol-keyed sink + CDP `Runtime.evaluate` drain; survives navigation
- **Static deobfuscation** — `deobfuscate_code` (Babel fast: string-array inline, constant fold, dead branch, `_0xabcd` rename, bracket→dot; optional webcrack deep)
- **Code collection** — `collect_code` (scans for `sign/encrypt/hmac` etc. + AST heuristic ranking)
- **AI assist** (default OFF) — `understand_code` / `risk_panel`, OpenAI / Anthropic / Gemini, dynamic-imported
- **Session snapshots** — `save/restore/dump/load_session_state`, AES-256-GCM round-trip
- **Artifacts tasks** — `start_reverse_task` / `record_evidence` / `export_replay_bundle` / `export_portable_bundle`, shareable `.jrmcp.json` bundles plus single-file portable / replay JS extraction
- **Two-layer stealth** — `--cloak` opts into CloakBrowser source-level fingerprint patches (canvas / WebGL / audio / GPU); `--legacyStealth` is the explicit fork-default fallback that keeps the original stealth-args path
- **Tool profile gating** — `--toolProfile kernel|compact|full` (default `kernel`); restart with `compact` for broader workflow controls, `full` to expose every tool
- **Browser state reset** — `clear_site_data` clears cookies, HTTP cache, persistent storage, and sessionStorage for the current page's HTTP(S) frame origins without reloading
- **Network export** — `export_network_request` writes captured request / response bytes, query params, or a metadata bundle to disk (and into the active task's `evidence/` directory when `taskId` is set)
- **Local-file input for `evaluate_script`** — pipe local files into in-page evaluation for replay and fixture-driven runs

## Requirements

- [Node.js](https://nodejs.org/) v22 or later
- [Chrome](https://www.google.com/chrome/) stable

## Installation

This fork is not published to npm; build from source:

```bash
git clone https://github.com/a0yark/js-reverse-pro-mcp.git
cd js-reverse-pro-mcp
npm install
npm run build
```

### Claude Code

```bash
claude mcp add -s user js-reverse-pro -- node /ABS/PATH/js-reverse-mcp/build/src/index.js
```

### Codex

```bash
codex mcp add js-reverse-pro -- node /ABS/PATH/js-reverse-mcp/build/src/index.js
```

### Generic JSON config

```json
{
  "mcpServers": {
    "js-reverse-pro": {
      "command": "node",
      "args": ["/ABS/PATH/js-reverse-mcp/build/src/index.js"]
    }
  }
}
```

## Anti-Detection

Two coexisting stealth layers, selectable per launch:

- **Legacy stealth-args path** (fork default; also gated by `--legacyStealth`): Patchright + 60+ stealth launch arguments. Matches behavior before `--cloak` existed.
- **Cloak path** (`--cloak`): CloakBrowser stealth-patched Chromium with source-level fingerprint patches (canvas / WebGL / audio / GPU). Binary auto-downloads (~200MB) on first use; identity is persisted per profile in `<profile>/.cloak-seed`. Conflicts with `--browserUrl`, `--wsEndpoint`, `--executablePath`, `--channel`.

| Layer | Description |
|-------|-------------|
| Patchright Engine | C++ level patches, removes `navigator.webdriver`, avoids `Runtime.enable` leaks |
| 60+ Stealth Args | Removes automation signatures, bypasses headless detection, GPU/network/behavior fingerprint spoofing |
| Harmful Args Removal | Excludes `--enable-automation` and 4 other default Playwright arguments |
| Silent CDP Navigation | Navigation tools don't activate CDP domains, captures requests only through Playwright-level listeners |
| Google Referer Spoofing | All navigations automatically include `referer: https://www.google.com/` |
| Persistent Login State | Uses persistent user-data-dir by default, login state preserved across sessions |
| Cloak source patches | `--cloak` only: canvas / WebGL / audio / GPU fingerprint patches applied inside the Chromium build |

### Hook framework stealth discipline (Pro fork)

The 18 new tools follow strict stealth rules:

- **Injection path**: CDP `Page.addScriptToEvaluateOnNewDocument` (same channel as upstream `inject_before_load`); **no** Playwright `addInitScript` / `exposeBinding`
- **Data sink**: `globalThis[Symbol.for('__jr_sink')]` array + CDP `Runtime.evaluate` drain; no fixed string-named property on `window`, no console pollution
- **`Function.prototype.toString` self-cloaking**: a stealth shim uses `WeakMap<wrapped, orig>` so `Function.prototype.toString.call(fetch)` returns `function fetch() { [native code] }`; the patched toString itself is registered, so `Function.prototype.toString.toString()` is also native
- **`storage` template `cookie` option default OFF**: `Object.defineProperty(document, 'cookie')` leaves a non-native descriptor that advanced fingerprinters can detect
- **AI default OFF**: only loads when `--enableAI` and the matching API key are both set; OpenAI / Anthropic / Gemini SDKs are dynamic-imported

See [`src/hooks/stealth-shim.ts`](src/hooks/stealth-shim.ts) header comment and the automated test [`tests/phase-b-smoke.mjs`](tests/phase-b-smoke.mjs).

## Tools (44)

### From upstream (23)

#### Page & Navigation
| Tool | Description |
|---|---|
| `select_page` | List/select pages as debugging context |
| `new_page` | Create new page and navigate |
| `navigate_page` | Navigate / back / forward / reload |
| `select_frame` | List/select frame execution context |
| `take_screenshot` | Page screenshot |

#### Script Analysis
| Tool | Description |
|---|---|
| `list_scripts` | List all loaded JS scripts |
| `get_script_source` | Get source by line range or character offset |
| `save_script_source` | Save full source to local file |
| `search_in_sources` | String/regex search across all scripts |

#### Breakpoint & Execution Control
| Tool | Description |
|---|---|
| `set_breakpoint_on_text` | Set breakpoint by code text search |
| `break_on_xhr` | XHR/Fetch breakpoint by URL pattern |
| `remove_breakpoint` | Remove by ID/URL or all |
| `list_breakpoints` | List active breakpoints |
| `get_paused_info` | Paused state, call stack, scope |
| `pause_or_resume` | Toggle pause/resume |
| `step` | Step over/into/out |

#### Function Tracing & Injection
| Tool | Description |
|---|---|
| `trace_function` | Logpoint trace (doesn't pause) |
| `inject_before_load` | Inject pre-load script (raw CDP) |

#### Network & WebSocket
| Tool | Description |
|---|---|
| `list_network_requests` | List/get-detail network requests |
| `get_request_initiator` | JS call stack for a request |
| `get_websocket_messages` | WS connections / message analysis |

#### Inspection
| Tool | Description |
|---|---|
| `evaluate_script` | Run JS in page (paused / main world / file output / local file input supported) |
| `list_console_messages` | Console messages list/detail |

### Pro fork (21 new)

#### Browser state & network export (2)
| Tool | Description |
|---|---|
| `clear_site_data` | Clear cookies, HTTP cache, persistent storage, sessionStorage for current page's HTTP(S) frame origins (no reload) |
| `export_network_request` | Export captured request / response bytes, query params, or metadata bundle to disk (and to task evidence/ when `taskId` is set) |

#### Hook framework (5)
| Tool | Description |
|---|---|
| `create_hook` | Render hook from template, returns hookId (no inject) |
| `inject_hook` | Attach hook to current page + future navigations |
| `get_hook_data` | Drain page-side sink, return per-hook events |
| `list_hooks` | List hooks + injection state + buffer |
| `clear_hook` | Remove CDP injection / purge buffer / delete hook record |

Built-in templates:
- `function` — wrap any global by dotted `path`
- `crypto` — CryptoJS / WebCrypto / JSEncrypt
- `network` — XHR / fetch / WebSocket, with `urlPattern` filter + call stack
- `encoding` — btoa / atob / TextEncoder / TextDecoder (JSON.\* opt-in)
- `storage` — localStorage / sessionStorage (cookie opt-in)

#### Static analysis (4)
| Tool | Description |
|---|---|
| `deobfuscate_code` | Babel passes (fast, always) + optional webcrack (deep) |
| `collect_code` | Scan functions matching `sign/encrypt/hmac` etc. + score |
| `understand_code` | LLM explains code semantics (default disabled) |
| `risk_panel` | Aggregate hook + task signals into a risk report (default disabled) |

#### Session management (5)
| Tool | Description |
|---|---|
| `save_session_state` | Capture cookies + multi-origin localStorage/sessionStorage to memory |
| `restore_session_state` | Apply snapshot to current context |
| `dump_session_state` | Persist to disk, optional AES-256-GCM encryption |
| `load_session_state` | Load from disk into memory (decrypts) |
| `get_storage` | One-shot read of cookies/local/session/all |

#### Artifacts tasks (5)
| Tool | Description |
|---|---|
| `start_reverse_task` | Create task dir `.jrmcp/tasks/<id>/` + task.json + timeline.jsonl |
| `record_evidence` | Append evidence (>64KB auto-files into evidence/) |
| `export_replay_bundle` | Export `.jrmcp.json` self-describing bundle |
| `export_portable_bundle` | Collapse task evidence into single-file `run/portable.js` (pure extraction) and/or `run/replay.js` (rebuild capture) |
| `list_tasks` | List all tasks under artifacts root |

## Configuration Options

### Upstream flags

| Option | Description | Default |
|---|---|---|
| `--browserUrl, -u` | Connect to running Chrome | - |
| `--wsEndpoint, -w` | WebSocket endpoint | - |
| `--headless` | Headless mode | false |
| `--executablePath, -e` | Custom Chrome path | - |
| `--isolated` | Temporary user-data-dir (fresh each time) | false |
| `--channel` | stable/canary/beta/dev | stable |
| `--viewport` | Viewport size, e.g. `1280x720` | real size |
| `--hideCanvas` | Canvas fingerprint noise | false |
| `--blockWebrtc` | Block WebRTC for IP leak | false |
| `--disableWebgl` | Disable WebGL | false |
| `--noStealth` | Disable stealth args (debug only) | false |
| `--proxyServer` | Proxy config | - |
| `--logFile` | Debug log path | - |

### Pro fork flags

| Option | Description | Default |
|---|---|---|
| `--artifactsDir` | Artifacts root | `${cwd}/.jrmcp/tasks` |
| `--sessionEncryptionKey` | Session-dump default secret (also reads `JRMCP_SESSION_KEY` env) | - |
| `--hookBufferSize` | Per-hook ring buffer capacity | 1000 |
| `--enableAI` | Enable understand_code / risk_panel | false |
| `--llmProvider` | openai / anthropic / gemini | - |
| `--cloak` | Use CloakBrowser stealth-patched Chromium with source-level fingerprint patches (auto-downloads ~200MB) | false |
| `--legacyStealth` | Explicitly select the legacy stealth-args path (conflicts with `--cloak`) | false |
| `--toolProfile` | Which MCP tools are exposed at startup: `kernel` (high-value reverse-workflow tools only), `compact` (broader debugging), `full` (every tool) | `kernel` |
| `--listTools` | Print the names of the tools selected by `--toolProfile` and exit without starting the MCP server | false |

### Example configurations

**Full setup (AI + enhanced stealth):**

```bash
OPENAI_API_KEY=sk-... node build/src/index.js \
  --browserUrl=http://localhost:9222 \
  --enableAI --llmProvider=openai \
  --blockWebrtc \
  --artifactsDir=./my-tasks
```

**Isolated (no persistent login, fresh each time):**

```json
{
  "mcpServers": {
    "js-reverse-pro": {
      "command": "node",
      "args": ["/ABS/PATH/build/src/index.js", "--isolated"]
    }
  }
}
```

### Connect to running Chrome

Launch Chrome (close all Chrome windows first):

**Windows**
```bash
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="%TEMP%\chrome-debug"
```

**macOS**
```bash
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug
```

Then connect with `--browserUrl=http://127.0.0.1:9222`.

## Workflow Cases

See [`scripts/cases/`](scripts/cases/) for end-to-end workflows:

- [JD H5 — `h5st` parameter chain](scripts/cases/jd-h5st.md) (URL sort + AES-CBC + timestamp watermark)
- [TikTok web — `a_bogus`](scripts/cases/tiktok-a-bogus.md) (custom obfuscation VM + sha256/md5)
- [Xiaohongshu web — `x-s` / `x-t` / `x-mns`](scripts/cases/xhs-falcon.md) (Falcon, with toString reflection caveat)

General workflow:

```text
1. start_reverse_task creates a task
2. new_page to target site → list_network_requests to find signed requests
3. get_request_initiator to inspect call stack
4. create_hook(network, urlPattern=...) + inject_hook
5. trigger requests → get_hook_data for context
6. create_hook(crypto) + inject_hook → see which algorithms run
7. get_script_source for the chunk → deobfuscate_code(level=deep)
8. collect_code on deobfuscated source to locate the sign function
9. understand_code (optional) lets the LLM explain it
10. record_evidence per finding
11. export_replay_bundle (shareable `.jrmcp.json`) or export_portable_bundle (single-file `run/portable.js` / `run/replay.js`)
```

## Troubleshooting

### Blocked by anti-bot systems

If you are blocked when visiting certain sites (e.g. Zhihu returning error 40362):

1. **Clear the contaminated profile**: delete `~/.cache/chrome-devtools-mcp/chrome-profile`
2. **Use isolated mode**: add `--isolated`
3. **Enable Canvas noise**: add `--hideCanvas`

### Hook captures no events

1. Confirm `inject_hook` returned `INJECTED` (use `list_hooks`)
2. Confirm rendered hook source has no syntax errors (start with `--logFile`)
3. Re-injecting the same hook on a loaded page nests the wrap; `clear_hook(hookId)` first or reload

### deobfuscate_code level=deep complains about webcrack

webcrack is an optional dependency that itself requires `@babel/core`. This fork already promotes `@babel/core` to dependencies; if it still complains, run `npm install` to refresh.

## Security Notice

This tool exposes browser content to MCP clients, allowing inspection, debugging, and modification of any data in the browser. Do not use it on pages containing sensitive information.

## License

[Apache-2.0](LICENSE), inherited from upstream [zhizhuodemao/js-reverse-mcp](https://github.com/zhizhuodemao/js-reverse-mcp).

Pro fork ports reference [NoOne-hub/JSReverser-MCP](https://github.com/NoOne-hub/JSReverser-MCP) (MIT, original license preserved at [`LICENSE.JSReverser-MCP`](LICENSE.JSReverser-MCP)).