rea-lite
by MuyuYanran
README.md
# dsh-rea-mcp-lite
> 把 [REA](https://www.npmjs.com/package/rea-agents) 的 **170 个逆向工程 MCP 工具**压成 **4 个元工具 + 按需加载的功能族**,
> 默认省下 **99.4%** 的工具上下文(~70k tok → **411 tok**),而 170 个工具**一个都没丢**,全部仍可调用。
> 功能族支持**运行时热切换**(`notifications/tools/list_changed`),无需重启 DSH。
A tiny zero-dependency MCP stdio proxy that turns REA's 170 tools into a progressive-disclosure
gateway: 4 meta-tools by default (~411 tokens instead of ~70k), every tool still reachable,
with hot-swappable tool bundles and no DSH restart.
---
## 效果
| 场景 | 模型可见工具 | 约 token |
| --- | --- | --- |
| **默认(仅元工具)** | 4 | **411** |
| 只加载 `web` 族 | 19 | 3.1k |
| 加载全部(裁剪后) | 174 | 49.4k |
| 不用本代理(直连 REA) | 170 | ~70.3k |
## 为什么不是"压缩 schema"
实测 REA 的 `tools/list`(`REA_MCP_INPUT_SCHEMA_PROFILE=compact`):
| 口径 | 字符数 | 占比 |
| --- | --- | --- |
| REA 实际发出的报文 | 1,772,584 | 100%(≈492k tok) |
| `outputSchema` | 1,493,900 | **84.3%** |
| `inputSchema` | 183,725 | 10.4% |
| `description` | 58,934 | 3.3% |
| **真正进模型上下文的(name + description + inputSchema)** | **253,136** | **≈70.3k tok** |
DSH 组装 LLM 请求时只映射三个字段 —— `@deepseek-ai/dsh-llm-deepseek/lib/index.js`:
```js
tools: options.tools.map((tool) => ({
name: tool.name,
description: tool.description,
input_schema: tool.parameters, // ← 即 MCP 的 inputSchema
...tool.deferLoading === true ? { defer_loading: true } : {}
}))
```
`outputSchema` 只在 DSH 内部用于校验工具返回值,**完全不占上下文**。所以:
- 492k 报文里 84% 是"看着吓人但不花钱"的部分;
- 砍掉它省 **0%**,裁掉 `pattern`/`format` 之类只省 **9%**,把描述压成一句话省 31%;
- 真正的成本是 **工具的数量**(170 个)。
于是本项目的核心是 **按需暴露(progressive disclosure)**,而不是压缩 schema。schema 瘦身只作为附带优化(`lite` 档位)。
## 工作原理
```
DSH ──stdio JSON-RPC──> proxy.mjs ──stdio JSON-RPC──> rea.mjs mcp(子进程)
```
零依赖,手写按行切分的 JSON-RPC(MCP stdio 就是一行一个 JSON,无 Content-Length 帧)。
| 消息 | 处理 |
| --- | --- |
| `initialize` | 转发;改写 `serverInfo.name` 为 `rea-lite`,并注入一段 `instructions` 教模型用网关 |
| `tools/list` | **不转发**,由代理用缓存的 REA 工具表 + 配置现场合成(元工具 + 已加载族,按 `lite` 裁剪) |
| `tools/call` | 名字是元工具就地处理;其余原样转发,结果原样回传(含图片、结构化错误) |
| 其它 | `notifications/*`、`resources/*`、`ping` 双向直通 |
| 热更新 | 族变化 → 代理发 `notifications/tools/list_changed`;DSH 的 MCP client 对它注册了 `onChanged: () => refreshTools()`,因此换工具集无需重启 |
## 安装
```bash
git clone https://github.com/MuyuYanran/dsh-rea-mcp-lite.git
cd dsh-rea-mcp-lite
```
**(1) 准备 REA**(本代理只是网关,真正干活的还是 REA):
```bash
npm i -g rea-agents # 或 pnpm add rea-agents;也可用 npx
# 也可以用官方技能:rea setup --skill --yes
```
**(2) 写配置**:复制 `config.example.json` 为 `config.json`,把 `child.args[0]` 改成你的 `rea.mjs` 绝对路径、`env.JAVA_HOME` 改成你的 JDK 路径。
```bash
cp config.example.json config.json # Windows: Copy-Item config.example.json config.json
```
```jsonc
{
"activeBundles": [], // 常驻功能族;[] = 只挂 4 个元工具
"lite": "mild", // off | mild | aggressive
"child": {
"command": "node", // 建议写 node 的绝对路径
"args": ["<...>/node_modules/rea-agents/scripts/rea.mjs", "mcp"],
"env": { "REA_MCP_INPUT_SCHEMA_PROFILE": "compact", "JAVA_HOME": "<你的 JDK>" }
}
}
```
**(3) 注册到 DSH**:在 `~/.dsh/cordis.patch.yml` 加一条(`serverName` 用 `rea` 可与 REA 官方技能里的引用保持一致):
```yaml
- insert:
- id: rea-mcp
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: rea
transport: stdio
command: 'C:\Program Files\nodejs\node.exe' # 或 DSH 内置 node 的绝对路径
args:
- 'D:\path\to\dsh-rea-mcp-lite\src\proxy.mjs'
env:
REA_LITE_CONFIG: 'D:\path\to\dsh-rea-mcp-lite\config.json'
JAVA_HOME: 'D:\path\to\jdk'
failOnStartupError: false
```
**(4) 重启 DSH**(cordis 配置只在启动时读取)。重启后 REA 会以 4 个元工具出现。
> 不想用网关时:把 `args` 换成 `rea.mjs` + `mcp` 并删掉 `REA_LITE_CONFIG` 即可直连。
## 4 个元工具
| 工具 | 作用 |
| --- | --- |
| `search_tools` | 在全部 170 个工具里搜(名字 + 说明),返回一句话用途、必填参数、所属族、是否已加载 |
| `tool_schema` | 取某工具的**完整** inputSchema(未裁剪),用于精确构造复杂参数 |
| `call_tool` | 调用**任意**工具(含未加载族的):`{"tool":"analyze_function","arguments":{"procedure":"main"}}` |
| `bundles` | 列出/切换功能族:`action = list \| activate \| deactivate \| set` |
**不需要先加载功能族就能调用工具** —— 功能族只决定"先把哪些工具的说明书摆到桌面上"。
## 功能族
下图按 **REA 6.4.0(170 个工具)** 统计;6.5.0/6.7.0 新增的族见下方「版本适配」。
| 族 | 工具数 | 约 token | 内容 |
| --- | --- | --- | --- |
| `nav` | 22 | 3.2k | 会话与导航(打开目标、游标、字节、段表、字节搜索) |
| `native` | 44 | 5.6k | 原生静态分析(伪代码、汇编、交叉引用、注解) |
| `apple` | 12 | 1.9k | Objective-C / Swift / plist / 动态库 |
| `managed` | 7 | 5.5k | .NET / 托管程序集 / 托管-原生边界 |
| `js` | 13 | 12.3k | JavaScript / Electron 应用图与语义追踪 |
| `web` | 15 | 2.7k | 网页/浏览器(CDP、网络、脚本、sourcemap) |
| `android` | 36 | 4.3k | Android / ADB / JEB(设备、日志、APK、Flutter、HarmonyOS) |
| `evidence` | 16 | 12.2k | 证据治理(对比、验证、未知项、重建账本) |
| `runtime` | 11 | 6.2k | 运行时捕获与包体(进程、固件、EVM) |
| `all` | 170 | 49.4k | 全部(等价于直连 REA,但 schema 更瘦) |
> 族之间有重叠(`apple` 的工具也属 `native`,`runtime` 的部分工具同属 `js`/`web`),所以各族 token 相加 > `all`。
### 版本适配(REA 6.5.0 起)
REA 6.7.0 有 **207 个工具**,比 6.4.0 多 37 个。这些已全部归族,另立三个新族:
| 族 | 工具数 | 内容 | 注意 |
| --- | --- | --- | --- |
| `debug` | 20 | Frida / GDB / Rizin / Cutter 动态调试 | 需目标进程或设备就绪;会改目标状态 |
| `formats` | 7 | Go / Rust / WASM / SQLite / Electron fuses / objdump | 部分依赖外部工具链 |
| `capture` | 6 | PCAP / TShark / sigrok 逻辑信号 | 需装 TShark 4.x / sigrok-cli |
`search_bytes` 归入 `nav`,`identify_flutter_build` / `inspect_dart_aot` / `project_harmony_application_graph` 归入 `android`。
**用旧版 REA 时**,这三个族显示 `0 个工具` 并标注「当前 REA 版本无此族」——那不是坏了,是版本没有这些能力。
族是静态清单(`src/bundles.mjs`)。再升级后新增的未归族工具,会出现在 `bundles` 输出的"未归族"里、以及 `node src/cli.mjs status` 的提示里;已归族但当前版本没有的,会显示为缺失而不是报错。
## 自定义分组
内置族按「REA 的能力域」划分,但每个人的常用工具组合不一样。自定义分组让你把任意工具编成一组,之后像内置族一样一键开关。
**在设置页建/改**:「设置 → REA 工具 → 自定义分组」→ 新建或点「编辑」:
- 左边是**全部工具**,每个都带一句话定义与成本,勾选即加入(不必先搜索再选)。
- 右边是本组已选,点 × 移除;也支持把左边的工具**拖进**右边。
- 「整族并入」可把某个内置族的工具一次性全加进来或全移出去,省得一个个点。
- 改组名时,原来勾着的这个组会跟着改名,不会悄悄失效。
- 顶部筛选框按名字或说明过滤,170~207 个工具里找起来也快。
> 为什么以勾选为主、拖动为辅:WCAG 2.5.7(AA)要求所有拖动操作都必须有非拖动的替代路径,纯拖拽界面对键盘和触屏用户不可用。所以这里是「勾选能用,拖动更好用」。
**用 CLI 建**:
```bash
node src/cli.mjs custom list # 看已有的
node src/cli.mjs custom add --id web-audit --label "网页审计" --tools inspect_web_page,analyze_web_bundle,export_web_scripts
node src/cli.mjs custom remove web-audit
```
**直接改配置**:`config.json` 的 `customBundles` 字段:
```json
{
"customBundles": {
"web-audit": { "label": "网页审计", "hint": "", "tools": ["inspect_web_page", "analyze_web_bundle"] }
}
}
```
约束:
- 组名小写字母开头,只含小写字母、数字、`_`、`-`,最长 32;不能与内置族或 `all` 重名。
- 工具名必须真实存在(按当前 REA 版本校验),否则保存时直接报「未知工具」,不会存下一个调不通的组。
- 自定义组与内置族**并列**,不改写内置族;同一个工具可以同时属于内置族和多个自定义组。
- 保存后与改 `activeBundles` 一样走配置监听,约一秒热切换。
## 三种用法
**A. 默认网关模式(推荐)** — 只挂 4 个元工具(411 tok),一切靠 `call_tool` 打:
```bash
node src/cli.mjs none
```
**B. 常用族常驻** — 例如天天做 Electron 逆向,让 `nav,js` 常驻(16.5k tok),其余按需:
```bash
node src/cli.mjs use nav js
```
**C. 全量** — 需要模型一次性看到所有工具(例如让它自己规划长流程):
```bash
node src/cli.mjs all
```
```bash
node src/cli.mjs list # 带实时 token 估算的功能族一览
node src/cli.mjs status # 当前配置
```
## 四种开关方式(都热更新,无需重启 DSH)
1. **设置页点选**(最省事,见下一节)——「设置 → REA 工具」勾选功能族,实时看到每个族的 token 成本与实测变化。
2. **直接对模型说** —— "把 web 和 js 族加载上" → 模型调 `bundles {"action":"activate","bundles":["web","js"]}` → 代理发 `list_changed` → DSH 当场换工具集。
3. **改 `config.json`** —— 代理 `fs.watch` 该文件,约 150ms 内生效。
4. **CLI** —— `node src/cli.mjs use web js`(本质就是改 config.json)。
运行时切换会写回 `config.json`(`persistRuntimeSwitch: true`),重启后保持。
## 设置页(可选,推荐)
仓库里的 `dsh-plugin/` 是一个 DSH 插件(细节见 [dsh-plugin/README.md](dsh-plugin/README.md)),装好后在 **设置 → REA 工具** 多出一页:
- 勾选框切换功能族,每个族标注工具数与约 token;顶部合计「元工具 + 已勾选族」以及相对全量加载省下的百分比。
- **族内工具可展开**:点「看族内工具」列出该族每个工具名与各自成本,不用为"这族到底有什么"去翻 REA 文档。
- **风险与版本标记**:`debug`/`capture`/`formats` 标「需目标环境」;当前 REA 版本没有的族标「当前 REA 版本无此族」(而不是让你以为勾了没反应)。
- **会话内实测**:DSH 工具注册表里真实存在的 `mcp__rea__*` 数量与字符数 —— 保存后约 1 秒跟随变化,用来确认热切换真的生效。
- 状态灯显示 rea 服务器是 已连接 / 已在 MCP 管理里停用 / 启用但未连上;被停用时额外给出"去 MCP 管理启用"的指引。
- 「查工具属于哪个族」搜索框:在 170 个工具里按名字或说明搜。
```bash
# 装进某个 profile(这里以 desktop 为例;web 同理)—— 依赖与 dsh.profile.bundles 都会自动登记
dsh plugin --profile desktop add link:/path/to/dsh-rea-mcp-lite/dsh-plugin
```
装好后重启一次 DSH 让插件加载;之后改勾选**不需要**重启 —— 面板写 `config.json`,代理监听文件后热切换,约 1 秒生效。
- 面板只管「加载哪些族」,不管 MCP 服务器本身的启停(那是「MCP 管理」的事)。当前 profile 里 `rea` 被停用时,面板会明确提示。
- 插件默认从「自己的上一级目录」找网关;网关装在别处时,设环境变量 `REA_LITE_ROOT` 指向网关目录即可。
- 面板读的是 `data/tools-model.json`(工具成本快照:170 个工具的字符数 + 一句话描述 + 所属族),**不含完整 schema**,30 KB 上下。代理每次连上 REA 都会刷新它;也可以手动 `npm run snapshot` 重新生成。
## 配置项
| 键 | 默认 | 说明 |
| --- | --- | --- |
| `activeBundles` | `[]` | 常驻功能族 |
| `metaTools` | `true` | 是否暴露 4 个元工具(关掉就只剩 `activeBundles`) |
| `lite` | `"mild"` | `off` \| `mild` \| `aggressive`,暴露时的 schema/描述裁剪力度 |
| `shortDescription` | `true` | 只给一句话描述(完整说明仍可从 `tool_schema` 拿到) |
| `persistRuntimeSwitch` | `true` | 运行时切换是否写回配置文件 |
| `toolCallTimeoutMs` | `600000` | 单次工具调用超时(反编译类工具有时会跑很久) |
| `child` | — | 怎么拉起真正的 REA:`command` / `args` / `env` |
- `lite` 的 `mild` 去掉 `pattern`/`format`/`minLength`… 并把描述截到 80 字;`aggressive` 再砍 `examples`/`default` 并截到 50 字。被裁掉的约束 REA 服务端仍会校验,报参数错误时用 `tool_schema` 取原始 schema。
- 环境变量:`REA_LITE_CONFIG` 指定配置文件位置,`REA_LITE_LAUNCHER` 覆盖 REA 启动器路径。
## 自检
```bash
node test/probe.mjs # 16 项集成测试:握手 / 元工具 / 搜索 / 转发 / 热加载 / 热卸载 / 全量
node test/plugin.test.mjs # 41 项插件自检:假 Cordis 上下文驱动面板 API + 渲染设置页
node test/check-config.mjs # 用真实 config.json 拉起 REA,确认 170 工具可达
node src/cli.mjs snapshot # 重新生成工具成本快照 data/tools-model.json
```
## 配套的模型侧说明
- `skill/SKILL.md` —— 独立的"网关模式"技能,教模型用这 4 个元工具(可复制到 `~/.agents/skills/rea-tool-gateway/`)。
- 若已安装 REA 官方技能 `reverse-engineer-anything`,建议在其 `SKILL.md` 顶部加一段说明:工具列表只剩 4 个元工具时,照常用 `call_tool` 干活(本仓库 README 的措辞可直接借用)。
## 已知限制
- 首次握手会向 REA 拉一次 170 工具全表(约 1.7MB,实测 ~4.5s 完成握手+列表),之后走缓存。
- 被裁剪的 schema 关键字让**调用前**的参数预校验变松,错误会晚一步由 REA 抛出(表现为工具返回结构化错误)。
- 仅代理 stdio 类 MCP;`mode: "direct"` 是预留字段,未实现绕过网关的分支。
- DSH 的 LLM 层已有 `defer_loading` 字段(`tool.deferLoading`)。若将来 DSH 允许在 MCP 工具上开启它,原生方案会比本代理更省;目前用不上,列为后续方向。
## 本机部署记录(作者环境)
- 代理接管的条目在本机 `~/.dsh/cordis.patch.yml` 的 `rea-mcp`(`serverName: rea`),备份为 `cordis.patch.yml.bak-rea-lite-*`。
- `probe/` 里是推导上面那些数字的分析脚本与原始抓包(大体积文件已 `.gitignore`)。
- 分析脚本:`probe/inspect-asar.mjs`(解析 Electron `app.asar`)、`probe/asar-grep.mjs`(在 asar 内做内容检索)、`probe/analyze-tools.mjs`、`probe/inspect-shape.mjs`、`probe/write-names.mjs`。
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues