Claude Code 学习站

用代码执行调用 MCP:让 Claude 代理更省 token

Anthropic 官方博客提出用代码执行取代直接工具调用来使用 MCP,通过 TypeScript 文件树、渐进式加载和执行环境内过滤,大幅降低上下文消耗。

本页目录5
要点速览
  • MCP 直接工具调用存在两大问题:海量工具定义占用上下文、中间结果需多次经过模型上下文
  • 解决思路是把 MCP 服务器呈现为代码 API(如 servers/google-drive/getDocument.ts),代理编写代码调用而非逐次工具调用
  • 官方示例中,一次会议记录写入 Salesforce 的操作从约 15 万 token 降到约 2000 token,降幅约 98.7%
  • 渐进式披露(按需读取工具定义)、执行环境内过滤聚合数据、循环与条件控制流,都是可直接落地的技巧
  • 代码执行还能实现敏感数据令牌化保护隐私,以及把常用代码片段存成可复用的「技能」文件
  • 代价是需要额外的沙箱、资源限制与监控基础设施来安全运行代理生成的代码

本文是对 Anthropic 官方文章「Code execution with MCP: Building more efficient agents」的中文要点摘要,完整内容以原文为准:https://www.anthropic.com/engineering/code-execution-with-mcp

背景:MCP 直接调用工具会「烧」很多上下文

MCP(Model Context Protocol)让代理可以连接文件系统、Google Drive、Salesforce、Slack 等各种外部工具。但当代理直接以「工具调用」的方式使用 MCP 时,会遇到两个规模化问题:

  1. 工具定义占位大:代理接入的 MCP 服务器越多,需要预先加载进上下文的工具定义就越多。文章指出,当连接的工具达到数千个规模时,仅工具定义就可能吃掉数十万 token,拉高延迟和成本。
  2. 中间结果被重复传递:多步工具调用中,上一步的完整输出往往要原样再传回模型,才能作为下一步调用的参数。文章给出的例子是:下载一份 2 小时的会议转录文本,再把它写入 Salesforce,转录全文需要在模型上下文里出现两次,仅这一步就可能多消耗约 5 万 token。

核心方案:把 MCP 服务器变成「代码 API」

官方给出的解法是:不要让模型直接发起一次次工具调用,而是把每个 MCP 服务器呈现为一棵 TypeScript 文件树,例如:

servers/
├── google-drive/
│   ├── getDocument.ts
│   └── index.ts
├── salesforce/
│   ├── updateRecord.ts
│   └── index.ts

代理不再逐步调用工具,而是直接编写一段代码,在执行环境里完成整条链路:

const transcript = (await gdrive.getDocument(
  { documentId: 'abc123' }
)).content;
await salesforce.updateRecord({
  objectType: 'SalesMeeting',
  recordId: '00Q5f000001abcXYZ',
  data: { Notes: transcript }
});

转录文本只在执行环境内部流转一次,不需要往返模型上下文。官方给出的对比数字是:同样的会议记录写入任务,从约 15 万 token 降到约 2000 token,降幅约 98.7%。

可落地的几个技巧

渐进式披露(progressive disclosure):不预先把所有工具定义塞进上下文,而是让代理按需「查阅」,比如通过 search_tools 之类的能力配合 detail level 参数,只在真正要用某个工具时才加载它的完整定义。

在执行环境里过滤/聚合数据:拿到一张万行级别的表格后,不要把全部行都交给模型,而是在代码里先筛选、汇总,只把关键的几行结果打印出来:

const allRows = await gdrive.getSheet({ sheetId: 'abc123' });
const pendingOrders = allRows.filter(row =>
  row["Status"] === 'pending'
);
console.log(pendingOrders.slice(0, 5));

一万行数据最终只需要向模型展示 5 行。

更强的控制流:用代码里的循环、条件判断、错误处理代替一连串工具调用的手动编排,比如轮询某个 Slack 频道直到出现「deployment complete」消息为止。

隐私保护:中间结果默认只留在执行环境中,不经过模型;文章还提到可以对敏感数据(邮箱、电话、姓名等 PII)做自动令牌化,用「EMAIL_1」这类占位符替换真实值,模型只看到占位符,真实数据通过 MCP 客户端的查找表在工具之间直接流转,绕开模型上下文。

状态持久化与「技能」复用:代理可以把中间结果写入文件,跨多次执行继续处理;还可以把写好的常用代码片段保存为可复用的「技能」文件,例如把某个「读取表格并存成 CSV」的函数固化下来,以后直接调用而不用每次重新生成代码。

权衡与风险

官方也明确指出,这套方案把复杂度转移到了执行侧:让代理生成并运行代码,需要一个具备沙箱隔离(sandboxing)、资源限制(resource limits)和监控(monitoring)能力的安全执行环境。也就是说,节省下来的 token 成本,要用额外的基础设施建设和运维投入来换。

结论

MCP 本身解决的是「协议层」的连接问题,让代理能统一接入多种工具;但当工具数量和调用链路变长后,工具定义与中间结果会持续挤占上下文。文章的建议是:把软件工程里已经成熟的模式(封装成 API、按需加载、过滤聚合、控制流、持久化)搬到代理与 MCP 的交互方式上,用「写代码调用」替代「逐次工具调用」,是应对规模化场景下上下文效率问题的一个可行方向。官方也欢迎实践者把自己的经验反馈给 MCP 社区。