MCP 的 Code Execution 模式:把工具当代码库调用
当 MCP server 从 3 个增长到 30 个,直接工具调用的主要成本会从“模型思考”变成“工具定义和中间结果占满 context”。Code Execution 模式把 MCP 暴露为可浏览的代码接口,让 Agent 按需读定义、在执行环境处理数据,只把结论送回模型。
两种数据路径
直接 MCP 调用
定义全部进 context → 模型调用 → 结果全部进 context → 模型再过滤
Code Execution with MCP
文件系统发现接口 → 沙箱代码调用 → 本地过滤/聚合 → 结论进 context
| 维度 | 直接调用 | Code Execution 模式 |
|---|---|---|
| 工具发现 | 预加载 schema | 按需浏览文件或搜索 |
| 中间数据 | 进入模型 context | 留在执行环境 |
| 组合逻辑 | 多轮模型判断 | 循环、条件、函数 |
| 状态 | 对话历史 | 文件或执行环境变量 |
| 基础设施 | MCP client 即可 | 还需要安全沙箱和资源限制 |
Anthropic 官方示例把同类任务从 150,000 token 降到 2,000 token。这个数字是特定任务的结果,不是每个系统都能复制的承诺;真正应测的是你自己的定义体积、结果大小和调用链长度。
把 MCP server 映射为代码目录
一种清晰的目录约定如下:
servers/
├── google-drive/
│ ├── getDocument.ts
│ └── getSheet.ts
├── salesforce/
│ ├── findProspect.ts
│ └── updateRecord.ts
└── index.ts
每个文件只暴露类型、用途和包装后的调用函数:
import { callMCPTool } from '../../client';
export interface GetDocumentInput {
documentId: string;
}
export interface GetDocumentResponse {
content: string;
modifiedAt: string;
}
/** Read one Google Drive document. Do not use for folders. */
export async function getDocument(input: GetDocumentInput): Promise<GetDocumentResponse> {
return callMCPTool('google_drive__get_document', input);
}
Agent 不需要读取整个 MCP catalog。它先列出 servers/,再打开与任务有关的文件,这就是 progressive disclosure。
示例:从会议记录更新 CRM
import * as drive from './servers/google-drive';
import * as crm from './servers/salesforce';
const transcript = await drive.getDocument({ documentId: 'doc-123' });
const summary = transcript.content
.split('\n')
.filter(line => line.startsWith('Decision:') || line.startsWith('Owner:'))
.join('\n');
await crm.updateRecord({
objectType: 'SalesMeeting',
recordId: 'meeting-456',
data: {
Notes: summary,
SourceModifiedAt: transcript.modifiedAt
}
});
完整 transcript 没有进入模型。需要模型判断的只有“应该提取哪些信息”;机械过滤和写入由代码完成。
安全边界必须先于 token 优化
Agent 生成的代码不应直接运行在应用服务器主进程里。最低控制包括:
| 控制 | 最低要求 |
|---|---|
| 文件系统 | 临时工作目录;默认只读;显式输出目录 |
| 网络 | 只允许必要 MCP endpoint;拒绝任意出站 |
| 凭证 | 短时、最小权限;不把长期密钥放入环境变量 |
| 资源 | CPU、内存、执行时间、输出大小上限 |
| 工具权限 | 按任务授权;读写工具分离;高风险写入需审批 |
| 审计 | 保存代码、工具调用、结果摘要和最终写操作 |
不要把“中间数据没有进入 LLM”误写成“数据绝对安全”。数据仍然经过执行环境、MCP server 和日志系统,必须分别做数据分类与保留策略。
错误处理与幂等
批处理代码最容易把一次错误扩大成 500 次错误。写入工具至少需要:
type WriteResult = {
idempotencyKey: string;
status: 'created' | 'already_applied' | 'rejected';
evidenceId?: string;
};
执行策略:
- 先读、后算、最后写。
- 写入前生成 idempotency key。
- 高风险操作先输出 dry-run diff。
- 限制单次批量大小。
- 部分失败时记录成功项,不盲目全量重跑。
何时不要使用
- 只有 3–5 个工具且结果很小。
- 模型必须逐条阅读中间结果才能判断。
- 团队暂时没有可信的代码沙箱。
- 工具包含不可撤销写操作,却没有审批和幂等设计。
- 省下的 token 少于执行环境的固定运维成本。
上线验收
- 工具定义可以按需发现,不需要全量预载。
- 10,000 行数据先在执行环境过滤,再进入模型。
- 沙箱超时后任务能明确失败,不会无限等待。
- 写操作支持 dry run、审批和幂等。
- Trace 能关联生成代码、MCP 调用和业务结果。
- 用真实任务比较 token、P95 延迟、正确率和运维成本。
动手练习
选择一个“从表格找出逾期客户并创建 CRM follow-up”的流程:
- 为 Sheet 与 CRM 建立最小 TypeScript wrapper。
- 在代码里过滤逾期超过 14 天且金额大于阈值的行。
- 输出 dry-run JSON,不直接写 CRM。
- 加入人工批准后再执行批量写入。
- 验证重复执行不会创建重复任务。
相关阅读
参考资料
📚 相关资源
❓ 常见问题
点击问题,查看本章对应的实践答案。
Code execution 模式和传统 MCP 工具调用的核心区别是什么?
传统方式把全部工具定义预载进上下文、每个中间结果都流经模型;code execution 把 MCP server 呈现为文件系统里的代码 API,Agent 按需读定义、写代码调用,数据在执行环境流转,模型只看最终结果。
Token 降幅 150k → 2k 的数据出处是哪里?
Anthropic 官方工程博客《Code execution with MCP》(2025-11-04)的实测数据:同类任务 token 消耗从 150,000 降到 2,000,节省 98.7%。
什么场景不值得上 code execution 模式?
工具少、返回数据小的场景直接调用更简单;模型确实需要对中间数据做推理时数据本来就该进上下文。该模式要求自建沙箱、资源限制和监控,这些运维成本要和 token 节省对着算。
Code execution with MCP 和 Programmatic Tool Calling 是一回事吗?
前者是需要自建执行环境的架构模式;PTC 是 Claude Developer Platform 的 API 产品化实现——Claude 在托管沙箱里写 Python 编排工具,官方实测复杂研究任务 token 平均降 37%(43,588 → 27,297)。