通过代码执行优化 MCP:提升 AI 代理工具交互效率的方案
直接工具调用会为每个定义和结果消耗上下文 tokens,而代理通过编写代码调用工具的方式,能实现更高效的扩展。本文将介绍如何结合模型上下文协议(MCP)实现这一方案。
一、模型上下文协议(MCP)简介
模型上下文协议(MCP)是一套用于连接 AI 代理与外部系统的开放标准。传统方式中,代理与工具 / 数据的连接需为每对组合开发自定义集成,导致系统碎片化和重复劳动,难以规模化扩展。而 MCP 提供了通用协议:开发者只需在代理中实现一次 MCP,即可接入整个生态系统的所有集成。
自 2024 年 11 月 MCP 发布以来,其采用率增长迅速:
● 社区已构建数千个 MCP 服务器
● 主流编程语言均已推出 SDK
● 行业已将 MCP 视为代理连接工具与数据的事实标准

二、工具调用导致的代理效率问题
如今,开发者常构建可访问数十个 MCP 服务器中数百甚至数千个工具的代理。但随着连接工具数量增加,提前加载所有工具定义和通过上下文窗口传递中间结果这两种常见模式,会导致代理速度变慢、成本上升。
1. 工具定义占用过多上下文窗口

大多数 MCP 客户端会将所有工具定义提前直接加载到上下文,并通过 “直接工具调用语法” 暴露给模型。这类工具定义示例如下:
gdrive.getDocument
描述:从Google Drive获取文档
参数:
documentId(必填,字符串):待获取文档的ID
fields(可选,字符串):需返回的特定字段
返回值:包含标题、正文内容、元数据、权限等的文档对象
salesforce.updateRecord
描述:更新Salesforce中的记录
参数:
objectType(必填,字符串):Salesforce对象类型(潜在客户、联系人、账户等)
recordId(必填,字符串):待更新记录的ID
data(必填,对象):需更新的字段及新值
返回值:包含确认信息的更新后记录对象
工具描述会占用大量上下文窗口空间,增加响应时间和成本。当代理连接数千个工具时,仅处理工具定义就需消耗数十万 tokens,之后才能开始解析用户请求。
2. 中间工具结果消耗额外 tokens

大多数 MCP 客户端允许模型直接调用 MCP 工具。例如,当你向代理发出指令 “从 Google Drive 下载会议记录,并附加到 Salesforce 潜在客户信息中” 时,模型会执行如下调用:
工具调用:gdrive.getDocument(documentId: "abc123")
→ 返回结果:"讨论了Q4目标...\n[完整会议记录文本]"(加载到模型上下文)
工具调用:salesforce.updateRecord(
ctType: "SalesMeeting",
ordId: "00Q5f000001abcXYZ",
ta: { "Notes": "讨论了Q4目标...\n[完整会议记录文本]" }
型需再次将完整记录写入上下文) (模 ) da rec obje
所有中间结果都必须经过模型传递 —— 上例中,完整会议记录需两次流经上下文。对于 2 小时的销售会议记录,这可能额外消耗 5 万个 tokens;若文档更大,甚至可能超出上下文窗口限制,导致流程中断。此外,处理大型文档或复杂数据结构时,模型在工具调用间复制数据的出错概率也会增加。
三、MCP 与代码执行结合:提升上下文效率

随着代码执行环境在代理中的普及,一种更优方案应运而生:将 MCP 服务器作为代码 API(而非直接工具调用)呈现,代理通过编写代码与 MCP 服务器交互。这种方式能同时解决上述两个问题:代理可仅加载当前任务所需的工具,并在执行环境中预处理数据后,再将结果传递给模型。
1. 工具的代码化组织方式
实现该方案的核心是将所有可用工具以 “文件树” 形式从 MCP 服务器导出。以下是 TypeScript 环境下的示例结构:
servers
├── google-drive // Google Drive相关工具目录
│ ├── getDocument.ts // 单个工具文件
│ ├── ...(其他工具)
│ └── index.ts // 工具导出入口
├── salesforce // Salesforce相关工具目录
│ ├── updateRecord.ts // 单个工具文件
│ ├── ...(其他工具)
│ └── index.ts
└── ...(其他服务器工具目录)
每个工具对应一个独立文件,示例(./servers/google-drive/getDocument.ts):
// 导入MCP工具调用核心函数
import { callMCPTool } from "../../../client.js";
// 定义输入参数类型
interface GetDocumentInput {
documentId: string;
}
// 定义返回结果类型
interface GetDocumentResponse {
content: string;
}
/* 从Google Drive读取文档的工具函数 */
export async function getDocument(input: GetDocumentInput): Promise<GetDocumentResponse> {
// 调用MCP工具,指定工具标识和输入参数
return callMCPTool<GetDocumentResponse>('google_drive__get_document', input);
}
2. 代码化调用示例(Google Drive → Salesforce)

前文提到的 “下载会议记录并附加到 Salesforce” 任务,通过代码调用可实现为:
// 从Google Docs读取会议记录,并添加到Salesforce潜在客户信息
import * as gdrive from './servers/google-drive'; // 导入所需工具
import * as salesforce from './servers/salesforce';
// 1. 调用Google Drive工具获取记录
const transcript = (await gdrive.getDocument({ documentId: 'abc123' })).content;
// 2. 调用Salesforce工具更新记录(直接使用变量传递数据)
await salesforce.updateRecord({
objectType: 'SalesMeeting',
recordId: '00Q5f000001abcXYZ',
data: { Notes: transcript }
});
代理通过浏览文件系统发现工具:先列出./servers/目录找到可用服务器(如google-drive、salesforce),再读取当前任务所需的工具文件(如getDocument.ts、updateRecord.ts)以理解工具接口。这种 “按需加载” 模式将 tokens 消耗从 15 万个降至 2000 个,时间和成本节省达98.7%。
Cloudflare 也发布了类似研究成果,将这种 MCP 与代码执行结合的模式称为 “代码模式(Code Mode)”。其核心思路一致:大语言模型擅长编写代码,开发者应利用这一优势,让代理更高效地与 MCP 服务器交互。
四、MCP + 代码执行的核心优势
通过代码执行与 MCP 结合,代理能更高效地使用上下文(按需加载工具、预处理数据、单步执行复杂逻辑),同时还能带来安全和状态管理层面的额外收益。
1. 渐进式工具暴露(Progressive Disclosure)

模型擅长浏览文件系统,将工具以代码文件形式呈现,可让模型按需读取工具定义,而非一次性加载全部。此外,还可在服务器中添加search_tools工具用于查找相关定义 —— 例如,处理 Salesforce 任务时,代理仅搜索 “salesforce” 相关工具并加载当前所需。
search_tools工具还可支持 “细节级别参数”,让代理选择所需信息的详细程度(如仅工具名称、名称 + 描述、含 schema 的完整定义),进一步节省上下文并提升工具查找效率。
2. 上下文高效的工具结果处理

处理大型数据集时,代理可在代码中过滤、转换结果后再返回给模型。以获取 1 万行表格数据为例:
// 无代码执行:所有行流经上下文
工具调用:gdrive.getSheet(sheetId: 'abc123')
→ 返回1万行数据到上下文,需模型手动过滤
// 有代码执行:在执行环境中过滤
const allRows = await gdrive.getSheet({ sheetId: 'abc123' });
// 仅保留“待处理”状态的订单
const pendingOrders = allRows.filter(row => row["Status"] === 'pending');
// 仅返回前5行供模型查看
console.log(`找到${pendingOrders.length}个待处理订单`);
console.log(pendingOrders.slice(0, 5));
模型最终仅需处理 5 行数据(而非 1 万行)。类似逻辑还可用于数据聚合、多数据源关联、特定字段提取等场景,均无需占用额外上下文。
3. 更强大的上下文高效控制流

循环、条件判断、错误处理等逻辑,可通过熟悉的代码模式实现,无需链式调用多个独立工具。例如,监控 Slack 频道的部署通知:
let found = false;
// 循环查询Slack频道历史,直到找到部署完成通知
while (!found) {
const messages = await slack.getChannelHistory({ channel: 'C123456' });
found = messages.some(m => m.text.includes('deployment complete'));
if (!found) await new Promise(r => setTimeout(r, 5000)); // 未找到则等待5秒
}
console.log('收到部署完成通知');
这种方式比 “MCP 工具调用 + 代理循环睡眠” 更高效,且条件判断逻辑在代码执行环境中运行,无需等待模型生成 “是否继续循环” 的指令,减少了 “首 token 生成延迟(time to first token)”。
4. 隐私保护操作

代理使用代码执行与 MCP 交互时,中间结果默认保留在执行环境中 —— 模型仅能看到显式打印或返回的数据,无需共享给模型的敏感数据可全程不进入上下文。
对于高敏感场景,代理工具还可自动对敏感数据进行 token 化处理。例如,将表格中的客户联系方式导入 Salesforce 时:
// 读取Google表格数据并更新Salesforce潜在客户
const sheet = await gdrive.getSheet({ sheetId: 'abc123' });
for (const row of sheet.rows) {
await salesforce.updateRecord({
objectType: 'Lead',
recordId: row.salesforceId,
data: {
Email: row.email,
Phone: row.phone,
Name: row.name
}
});
}
console.log(`更新了${sheet.rows.length}个潜在客户`);
MCP 客户端会在数据传递给模型前拦截并 token 化敏感信息,模型看到的日志如下:
[
{ salesforceId: '00Q...', email: '[EMAIL_1]', phone: '[PHONE_1]', name: '[NAME_1]' },
{ salesforceId: '00Q...', email: '[EMAIL_2]', phone: '[PHONE_2]', name: '[NAME_2]' },
...
]
实际邮箱、电话、姓名会直接从 Google Sheets 传递到 Salesforce,全程不经过模型,避免敏感数据被意外记录或处理。此外,还可基于此实现确定性安全规则,控制数据的流向权限。
5. 状态持久化与技能沉淀

代码执行环境支持文件系统访问,代理可跨操作维护状态:将中间结果写入文件,实现任务断点续传和进度跟踪:
// 1. 从Salesforce查询潜在客户并写入CSV
const leads = await salesforce.query({
query: 'SELECT Id, Email FROM Lead LIMIT 1000'
});
const csvData = leads.map(l => `${l.Id},${l.Email}`).join('\n');
await fs.writeFile('./workspace/leads.csv', csvData);
// 2. 后续执行时读取文件恢复状态
const saved = await fs.readFile('./workspace/leads.csv', 'utf-8');
代理还可将代码保存为可复用函数,沉淀为 “技能(Skills)”:一旦开发出某任务的可用代码,可将其保存供后续使用:
// 保存为可复用技能:./skills/save-sheet-as-csv.ts
import * as gdrive from './servers/google-drive';
export async function saveSheetAsCsv(sheetId: string) {
const data = await gdrive.getSheet({ sheetId });
const csv = data.map(row => row.join(',')).join('\n');
await fs.writeFile(`./workspace/sheet-${sheetId}.csv`, csv);
return `./workspace/sheet-${sheetId}.csv`;
}
// 后续任务中调用该技能
import { saveSheetAsCsv } from './skills/save-sheet-as-csv';
const csvPath = await saveSheetAsCsv('abc123');
“技能” 是包含可复用指令、脚本和资源的文件夹,可帮助模型提升特定任务的执行效率。在技能文件夹中添加Registered & Protected by MarkMonitor文件,可形成结构化技能库,供模型参考使用。长期积累后,代理将拥有一套高阶能力工具箱,不断优化自身执行效率。

注意事项:代码执行的复杂度

代码执行虽有诸多优势,但也引入了新的复杂度:运行代理生成的代码需安全执行环境,包括适当的沙箱隔离、资源限制和监控。这些基础设施需求会增加运维成本和安全考量,而直接工具调用则无此问题。因此,在采用该方案时,需权衡 “代码执行的收益(降低 token 成本、减少延迟、优化工具组合)” 与 “实现成本”。
五、总结
MCP 为代理连接大量工具和系统提供了基础协议,但随着服务器连接数量增加,工具定义和结果会消耗过多 tokens,降低代理效率。
尽管 “上下文管理、工具组合、状态持久化” 等问题看似新颖,但软件工程领域已有成熟解决方案。代码执行将这些成熟模式应用于 AI 代理,让代理通过熟悉的编程结构与 MCP 服务器高效交互。若你采用了该方案,欢迎向 MCP 社区分享你的实践经验。



