# JSHook MCP 使用 SOP

## 概述
jshookmcp (`npx -y @jshookmcp/jshook@latest`) — MCP协议服务器，暴露174~387个工具覆盖浏览器/CDP/逆向/反混淆/网络分析。**非GA原生工具**，通过`code_run`启动进程+JSON-RPC stdio通信临时调用。

## 启动

### 环境变量（关键！）
| 变量 | 值 | 说明 |
|------|-----|------|
| `MCP_TOOL_PROFILE` | `workflow`(推荐) / `full` / `search` | 工具集大小，**不是** `JSHOOK_BASE_PROFILE` |
| `MCP_TRANSPORT` | `stdio`(默认) / `http` | 传输模式。HTTP模式在Win下需处理好Accept头(406易出错) |

### stdio 启动（推荐）
```python
import subprocess, json, time, threading

proc = subprocess.Popen(
    ['npx.cmd', '-y', '@jshookmcp/jshook@latest'],
    env={'MCP_TOOL_PROFILE': 'workflow'},
    stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
time.sleep(5)  # 等启动
```

## MCP 通信协议

### JSON-RPC 2.0 over stdio
- 请求: `{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}` + `\n`
- 响应: 单行JSON
- **不要用 `select.select`** (Windows不支持pipe的select)! 用`readline()`或threading读

### 关键坑：reader线程
```python
buf = []
def reader():
    while True:
        line = proc.stdout.readline()
        if not line: break
        buf.append(line.decode().strip())

def mcp(method, params=None, timeout=25):
    msg = {"jsonrpc":"2.0","id":1,"method":method}
    if params: msg["params"] = params
    proc.stdin.write((json.dumps(msg) + "\n").encode())
    proc.stdin.flush()
    deadline = time.time() + timeout
    while time.time() < deadline:
        for i, item in enumerate(buf):
            try:
                resp = json.loads(item)
                buf.pop(i)
                return resp
            except: pass
        time.sleep(0.1)
    raise TimeoutError(f"Timeout: {method}")
```

### initialize 参数
```python
mcp("initialize", {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": {"name": "test", "version": "1.0"}  # version是string, 非int!
})
```

## 工具架构（两层）

### 第一层: 发现工具 (workflow profile 下直接可调)
| 方法 | 用途 |
|------|------|
| `tools/list` | 列出所有已激活工具及其inputSchema |
| `tools/call` | 调用工具: `{"name":"xxx", "arguments":{...}}` |

### 第二层: jshook的元工具（search profile 下必须用）
| 工具 | 用途 |
|------|------|
| `search_tools(query, top_k?)` | 按关键词搜索可用工具 |
| `describe_tool(name)` | 查工具详情和参数 |
| `activate_tools(names[])` | 激活指定工具 |
| `activate_domain(domain, ttlMinutes?)` | 激活整个域的工具 |
| `call_tool(name, args?)` | 调用已激活的工具 |
| `deactivate_tools(names[])` | 释放工具释放上下文 |

**workflow profile 下可直接 `tools/call` 调用工具，跳过元工具层**

## 实战验证过的工具

### ⭐⭐⭐⭐⭐ 最有用
| 工具 | 实测 | 说明 |
|------|------|------|
| `search_in_scripts` | ✅ | 在已收集的JS中搜关键词，返回上下文（逆向神器） |
| `page_evaluate` | ✅ | 在页面执行JS，**参数名是`code`不是`expression`** |
| `page_navigate` | ✅ | 导航到URL，返回页面标题 |
| `browser_launch` | ✅ | CDP桥接到已有浏览器(`wsEndpoint`参数) |

### ⭐⭐⭐⭐ 有用
| 工具 | 实测 | 说明 |
|------|------|------|
| `browser_list_tabs` | ✅ | 列出所有CDP标签页 |
| `network_enable` | ✅ | 开启网络监控（需新导航才捕获） |
| `browser_list_cdp_targets` | ✅ | 列出CDP targets |
| `page_inject_script` | ✅ | 注入JS到页面 |
| `http_plain_request` | ✅ | 直接HTTP请求（CloudFlare下受限） |

### ⭐⭐⭐ 有限
| 工具 | 实测 | 问题 |
|------|------|------|
| `collect_code` | ⚠️ | 需url参数，大文件超时 |
| `deobfuscate` | ⚠️ | >100KB JS 超时 |
| `network_get_requests` | ⚠️ | CDP session不同步，需先导航 |
| `extract_function_tree` | ⚠️ | 需scriptId/functionName |
| `detect_obfuscation` | ⚠️ | 参数路径问题 |

## 典型工作流

### 流程1: JS逆向/找API端点
```
1. page_navigate → dammba.com
2. page_evaluate → fetch(app.js) → 获取完整JS
3. search_in_scripts → 搜 action/ api/ down/ pay/ ajax
4. page_evaluate → 直接调 admin-ajax.php 试action
```

### 流程2: 网络抓包
```
1. browser_launch(wsEndpoint) → 连Edge
2. page_navigate → 目标URL
3. network_enable → 开监控
4. 等几秒 → network_get_requests
```
**注意**: network_enable必须在目标页面加载前打开，否则捕获不到

### 流程3: CDP桥接
```python
# 获取Edge WS端点
info = json.loads(urllib.request.urlopen("http://127.0.0.1:9223/json/version").read())
ws = info["webSocketDebuggerUrl"]
# 传给jshook
mcp("tools/call", {"name":"browser_launch", "arguments":{"wsEndpoint": ws}})
```

## 避坑清单

### 1. 环境变量
- ❌ `JSHOOK_BASE_PROFILE=search` → **不生效**（变量名错误）
- ✅ `MCP_TOOL_PROFILE=workflow` → 174工具

### 2. Windows stdio
- ❌ `select.select([proc.stdout])` → **OSError**（Windows pipe不支持select）
- ✅ `threading + readline + list buf`

### 3. HTTP transport
- ❌ HTTP 406 Not Acceptable（MCP HTTP transport在Win下有兼容问题）
- ✅ 用默认stdio

### 4. page_evaluate
- ❌ 参数名 `expression` → 报错
- ✅ 参数名 `code`

### 5. browser_launch
- ❌ 传 `debugUrl` → 报错
- ✅ 传 `wsEndpoint`（CDP WebSocket URL）
- ✅ 或传 `autoConnect: true` 自动发现

### 6. 进程管理
- jshook用Node.js (`npx`)，启动约5秒
- Windows上 `DETACHED_PROCESS` + `CREATE_NO_WINDOW` 可后台运行
- `stdin EOF` 会导致jshook退出 → 用 `DEVNULL` 或保持pipe打开

### 7. CloudFlare
- jshook的 `http_plain_request` 直接请求会被CloudFlare拦截(403)
- 必须在浏览器上下文中用 `page_evaluate + fetch` 绕过

## 工具域一览 (workflow profile, 174 tools)
```
browser_   (15) 浏览器/CDP操控: launch, list_tabs, navigate, evaluate, inject...
network_   (16) 网络监控: enable, get_requests, intercept, http_request_build...
deobfuscate (1) JS反混淆 + detect_obfuscation, js_deobfuscate_jsvmp/pipeline...
v8_         (8) V8堆分析: heap_snapshot_capture/analyze/diff, object_inspect...
binary_     (4) 二进制分析: detect_format, entropy_analysis, encode/decode...
stealth_    (5) 隐身: inject, set_user_agent, captcha_detect...
collect_    (2) 代码收集: collect_code, collect_pages...
js_         (4) JS深度分析: bundle_search, bundle_extract, solve_constraints...
api_        (1) api_probe_batch
breakpoint  (1) breakpoint
blackbox_   (3) blackbox_add/common/list
```

## document_start 首包捕获门禁

Network 先启用 + new-document 注入 + reload/导航后读取 `window.__gaTelemetry`。

### realm 覆盖边界（R161/R166本地实测）
- `Page.addScriptToEvaluateOnNewDocument` 可覆盖 main window 与新建 same-origin iframe；读取 iframe 遥测需按 frame execution context/同源消息分别汇总，不能只看顶层 `window.__gaTelemetry`。
- dedicated worker 不会自动获得页面级 new-document hook：若服务端/Network 已见 worker 请求但 JS telemetry 为 0，不要判定无请求，先按 worker realm 漏捕处理。
- 目标参数疑似在 worker 内生成时，改用 CDP Target auto-attach/worker target 注入、Worker 构造器改写，或 Network+Debugger 旁路；未覆盖 worker 前不得宣称 JS Hook 全覆盖。
- same-origin classic worker 可用 `Worker` 构造器改写为同源 wrapper URL：wrapper 先装 telemetry hook，再 `importScripts(originalUrl)`；worker telemetry 用 worker 自身 `postMessage` 回传。
- 不要用 Blob wrapper 承载原 worker 入口：会把 worker 内相对 URL 基准改为 `blob:`，破坏 `/api` 等相对 fetch/XHR；page target 的 CDP Network 也不等于 worker 网络全量，需服务端日志或 worker target 对照。
