为什么 REA MCP 必须按项目安装:一次 30 万 token 的上下文税
为什么 REA MCP 必须按项目安装:一次 30 万 token 的上下文税
最近我在一个和逆向工程毫无关系的 Flutter 项目里新建 Claude Code 会话。还没开始干活,第一轮请求的 input_tokens 就是 300,574。
我一度以为是项目上下文太大。这个项目有 44 个会话,我抽查的首轮值普遍在 29.3 万–30.1 万;/context 里最大的一块也不是代码,而是 MCP 工具定义。罪魁祸首,是我为了图方便全局安装的 REA MCP。
这篇文章把账算清楚:REA 到底占了多少、为什么全局安装会让每个项目一起买单,以及怎么改成项目级安装。如果你也在 Claude Code 里装过重型 MCP server,这套方法同样适用。
一、先看现象:什么都没做,30 万 token 没了
Claude Code 会把每一轮请求的 usage 写进会话 JSONL。取第一条 assistant 消息:
jq -r 'select(.type=="assistant" and .message.usage) | .message.usage.input_tokens' \
~/.claude/projects/<project>/<session>.jsonl | head -1
我的结果是 300574。这不是估算,是请求实际携带的输入 token。
30 万意味着什么:
- 对 200K 上下文窗口的模型,首轮就装不下;
- 对 1M 窗口的模型,30% 的预算在开工前已经花掉;
- 每一轮对话都会重复携带这批工具定义。Prompt caching 能降低重复计费,但不会减少窗口占用,也不会让首轮变小。
更麻烦的是,这个成本与项目内容无关。一个纯前端项目、一个数据分析脚本、一个只改文档的仓库,只要用了同一份全局配置,首轮都要付这笔钱。
二、根因:四个因素叠在一起
2.1 全局安装,等于所有项目共享
REA 的安装命令 rea mcp add 默认写入 user scope,也就是 ~/.claude.json 顶层的 mcpServers。它的帮助里写得很清楚:要项目级安装,需要加 --no-global。
user-scope server 会被所有项目加载,而且没有项目级豁免。disabledMcpjsonServers 只对项目 .mcp.json 里的 server 生效,管不到全局 server。你无法在某个项目里单独说“这次不要 REA”。
于是结果就是:项目里一行逆向代码都没有,207 个工具照样跟着启动。
2.2 REA 的工具面确实很大:207 个工具,3.09 MB schema
用 REA 自带的诊断命令看工具清单:
rea mcp doctor --json | jq '.checks[] | select(.name=="inventory:tools")'
# expected=207 observed=207 missing=none unexpected=none duplicates=none
207 个工具,默认 tools/list 序列化后是 3,087,952 字节。拆开看:
| 组成部分 | 字节数 | 占比 |
|---|---|---|
| 输出 schema(outputSchema) | 2,264,286 | 73.3% |
| 输入 schema(inputSchema) | 688,973 | 22.3% |
| 工具描述(description) | 90,150 | 2.9% |
| 其他(名称、注解等) | 44,543 | 1.4% |
| 合计 | 3,087,952 | 100% |
体积最大的几个工具:
| 工具 | 序列化字节 |
|---|---|
compare_web_captures |
169,765 |
inspect_analysis_view |
145,262 |
compare_application_versions |
60,096 |
capture_browser_scenario |
51,876 |
reconcile_javascript_runtime |
45,186 |
trace_application_feature |
44,818 |
这不是说 REA 写得臃肿。它面对的是二进制、托管代码、Electron、浏览器抓包等复杂目标,必须把每个工具的输入输出契约写清楚,模型才能正确调用。在需要它的项目里,这是有价值的信息;在不需要它的项目里,这是纯成本。
2.3 Claude Code 默认会把 schema 内联进请求
MCP 工具定义不是在磁盘上放着不动的插件,它们会进入 API 请求的 tools 数组。Claude Code 提供了 tool search / deferred loading:当条件满足时,工具 schema 可以延迟加载,只在真正需要时进入上下文。server 侧也可以用 alwaysLoad: false 强制走 deferred。
但 tool search 有门控条件:需要 Anthropic first-party endpoint、受支持的模型,并且没有禁用相关实验性 beta。非 first-party endpoint 默认关闭。
我的 Claude Code 通过第三方兼容 endpoint 接入模型,ENABLE_TOOL_SEARCH 也没有显式设置,所以 tool search 没有生效,207 个 schema 全部内联。3 MB 的 JSON 经过 tokenizer,最终表现为首轮约 30 万 token 的输入。
这就是放大器:工具数量 × 每个工具的完整契约 × 每个会话。
2.4 每个会话还会各起一个 REA 进程
同一时间,我在机器上看到 10 多个 rea mcp 子进程,每个 Claude Code 会话一个。内存开销不是主因,但它说明这笔成本是乘法:会话越多,工具定义和进程一起翻倍。
三、所以,REA 应该按项目安装
把 MCP server 当成项目依赖,而不是全局常驻服务,理由很直接:
- 上下文窗口是每次会话的预算。工具定义是常驻开销,不是用完即走的磁盘文件。
- 大多数项目不需要逆向工程工具。让它们为 207 个工具付费,没有收益。
- 工具越多,选择噪声越大。不相干的工具会稀释模型对当前任务的注意力。
- 项目级
.mcp.json可审查、可版本化。谁需要,谁在仓库里声明;Code Review 时也看得见。
这不是功能倒退,是把“安装”从一次性动作变成可管理的依赖。
四、具体怎么做
4.1 移除全局 REA
先确认它确实在 user scope:
claude mcp list
claude mcp get rea
然后移除:
claude mcp remove rea -s user
-s user 是明确作用域。删除后重启 Claude Code,新的会话才不会继续加载。
4.2 在真正需要的项目里安装
在项目根目录放一个 .mcp.json:
{
"mcpServers": {
"rea": {
"type": "stdio",
"command": "/path/to/rea",
"args": ["mcp"]
}
}
}
或者用命令写入当前项目:
claude mcp add -s local rea -- /path/to/rea mcp
-s local 只对当前项目生效;-s project 会写入 .mcp.json,可以随仓库共享。首次打开项目时,Claude Code 会要求批准 .mcp.json 里的 server,批准后才会连接。
如果你在 REA 仓库里工作,项目级的 .mcp.json 本来就是最合适的位置;其他项目不再受影响。
4.3 如果确实需要全局:至少裁剪 schema
有些团队会希望 REA 在所有项目可用。那就别用默认的 full/full,REA 提供了两个裁剪开关:
{
"mcpServers": {
"rea": {
"type": "stdio",
"command": "/path/to/rea",
"args": ["mcp"],
"env": {
"REA_MCP_OUTPUT_SCHEMA_PROFILE": "off",
"REA_MCP_INPUT_SCHEMA_PROFILE": "compact"
}
}
}
}
同一份 tools/list 的实测对比:
| 配置 | JSON 字节 | 相对默认 |
|---|---|---|
| 默认 full/full | 3,087,952 | 100% |
REA_MCP_OUTPUT_SCHEMA_PROFILE=off |
820,354 | -73% |
REA_MCP_INPUT_SCHEMA_PROFILE=compact |
2,482,411 | -20% |
| 两者同时 | 214,813 | -93% |
两个开关同时开,工具定义从 3.09 MB 降到约 0.21 MB,减少 93%。
代价要说清楚:
output=off不再向模型广告输出 schema。REA 仍会在服务端按契约校验结果并返回 structured content,但模型少了一份“返回长什么样”的说明,复杂工具的调用可能需要更多试错。input=compact去掉嵌套的注解性描述,保留类型、必填项和枚举等结构信息。对大多数调用够用,但对特别复杂的工具会损失一部分提示。
4.4 有条件的话,用 tool search 延迟加载
在支持的环境中,可以开启 tool search:
{
"env": {
"ENABLE_TOOL_SEARCH": "true"
}
}
同时在 REA 的 server 配置里显式声明:
"alwaysLoad": false
然后重启会话,用 /context 确认出现 MCP tools (deferred),而不是把全部 schema 计入已用 token。
注意:这条路径依赖 Anthropic first-party endpoint 和受支持的模型。第三方兼容 endpoint 下可能默认不可用,显式开启也未必生效。以 /context 的实际结果为准,不要只看配置。
4.5 确定不需要 MCP 的会话
如果某个项目完全不需要任何 MCP server,可以启动时直接隔离:
claude --strict-mcp-config --mcp-config '{"mcpServers":{}}'
这会忽略用户、项目、本地所有 MCP 配置,只使用传入的空配置。适合 CI、脚本化任务或确定不需要 MCP 的会话。
4.6 另一种做法:全局安装 + 按项目禁用
如果你希望只维护一份全局配置,又不想让每个项目都加载 REA,Claude Code 提供了项目级开关:
/mcp disable rea # 在当前项目禁用
/mcp enable rea # 需要时恢复
在不需要 REA 的项目里执行一次 /mcp disable rea,Claude Code 会把 rea 写入该项目在 ~/.claude.json 中的 disabledMcpServers,并在该项目不再加载它。需要恢复时执行 /mcp enable rea。
这是 opt-out 模型:新项目默认启用,你必须记得在每个不需要它的项目里禁用。禁用状态保存在本机的 ~/.claude.json,不随仓库共享;换一台机器或换一个项目路径,需要重新设置。
三种“按项目生效”的方式对比:
| 方式 | 配置位置 | 默认行为 | 团队共享 | 适用场景 |
|---|---|---|---|---|
user scope + /mcp disable |
~/.claude.json 顶层 + 项目条目的 disabledMcpServers |
所有项目默认启用(opt-out) | 否 | 一份全局配置,项目数量少且固定 |
local scope(claude mcp add -s local) |
~/.claude.json 项目条目的 mcpServers |
仅添加的项目启用(opt-in) | 否 | 单机、多项目,按项目添加 |
project scope(.mcp.json) |
项目仓库的 .mcp.json |
仅该项目启用(opt-in) | 是 | 团队协作、需要版本化 |
两个容易混淆的细节:
disabledMcpjsonServers是给.mcp.jsonserver 用的,配置在.claude/settings.local.json;disabledMcpServers是给 user/local scope server 用的,配置在~/.claude.json的项目条目中。两者不要混用。- 不要用
claude mcp remove来“禁用”:它会永久删除 server 配置(env、headers)并清掉 OAuth token。/mcp disable才是可逆的项目级开关。
如果你的目标是“默认不让其他项目付 30 万 token”,user scope + 手动 disable 仍然把责任放在每个项目的操作者身上;local/project scope 的 opt-in 模型更稳。全局安装更适合你愿意配合 /mcp disable,或者同时用 4.3 的 schema 裁剪来降低启用项目的成本。
五、验证:别猜,量一下
配置改动不会影响已经启动的会话。工具列表在会话启动时确定,必须重启才会释放。
重启后:
- 运行
/context,看MCP tools和MCP tools (deferred)两类各占多少。 - 取新会话第一条 assistant 消息的
usage.input_tokens,和之前的 300,574 对比。 - 用
rea mcp doctor --json确认工具数量(当前是 207)。 - 如果保留了全局 REA,再用 4.3 的配置复测
tools/list字节数。
一个容易踩的坑:MAX_MCP_OUTPUT_TOKENS 管的是工具输出的截断,不是工具定义注入。调它解决不了首轮 30 万 token 的问题。
六、结论
REA 是一个重型逆向工程工具箱。它在需要它的项目里很有价值,但不应该成为每个项目的默认背景。
这次排查让我把 MCP server 重新归类了:它不是“装一次、到处可用”的便利工具,而是每次请求都要携带的上下文依赖。依赖应该按项目声明、按需加载、定期测量。
上下文窗口是新的内存。不要把所有的工具都常驻。
文中字节数为 UTF-8 JSON 序列化后的长度;token 数来自 Claude Code 会话的 usage 记录。不同 Claude Code 版本、模型和 tokenizer 会得到不同数值,建议在自己的环境里用 /context 复测。

浙公网安备 33010602011771号