使用Claude Agent SDK演示MCP代码执行模式。
该项目灵感来源于Anthropic的一篇博客文章。该博客文章没有提供完整的代码,但提供了许多关于如何实现这一模式的提示。
我想要保持这个版本尽可能简单,并且与博客内容一致。我决定使用Claude Agent SDK,因为它已经支持代理技能、MCP和其他必要的功能。
此模式的实现生成了用于MCP工具的RPC包装文件。这些包装器是类型安全接口,代理可以发现并作为代码使用它们。
看到对这篇博客文章的所有反应,认为“MCP不好”,我感到(某种程度上)惊讶。这只是使用它的一种新方式。有时这有意义,有时则不然。
我更喜欢用实证证据来更好地理解这类问题,而不是陷入理论辩论。希望这些想法的具体解释能帮助生成更多证据,以便你自己判断这种解决方案是否适合某个用例。
我还喜欢Shaunak Joshi在他的回应这条推文时的观点:
MCP处理分发和发现(安装应用程序如连接器暴露能力),而代码模式仅处理纯执行(模型使用从MCP模式自动生成的SDK生成代码)。因此,MCP更像是一个打包层,你可以同时获得生态系统的好处和可组合性。
重要: 当前实现不包括开箱即用的工作沙箱配置在Claude Agent SDK中。这意味着:
计划改进:
我们正在积极研究解决这些限制的沙箱解决方案:
建议:
直到强大的沙箱集成完成,将其视为实验演示而非生产就绪基础设施。
MCP代码执行是一种构建高效AI代理的实验模式。
一个假设是代码执行应该显著减少数据密集型工作流程中的令牌消耗,通过:
此存储库提供了两种模式,以便你可以测试并比较实际结果以适应你的用例。
我在两个真实世界仓库中测试了这两种方法:
感谢@johncburns1提出GitHub问题用例的想法!
Claude Code仓库提供了很好的压力测试,当时有大约5,000个开放问题,这是一个足够大的数据集,可以揭示两种方法之间的基本架构差异。
每种方法5次运行的结果:
代码执行在所有5次运行中都成功了。Claude一致创建了可重复使用的技能,并使用list_issues工具包装器成功获取和处理了所有问题。
直接MCP在所有5次运行中都失败了,尝试一次性将所有问题加载到上下文中时遇到了上下文溢出错误。
| 方法 | 成功率 | 平均持续时间 | 平均成本 | 输出 |
|---|---|---|---|---|
| 代码执行 | 100% (5/5) | 343秒 (5.7分钟) | 0.34美元 | 完整报告 + JSON |
| 直接MCP | 0% (5/5) | 失败前31秒 | N/A | N/A |
为什么直接MCP失败:
为什么代码执行成功:
list_issues工具包装器按批次获取问题当使用包含约45个开放问题的anthropic-sdk-python仓库进行测试时,两种方法都成功了,但代码执行仍然显著优于直接MCP:
| 指标 | 代码执行 | 直接MCP | 优势 |
|---|---|---|---|
| 成功率 | 100% (5/5) | 100% (5/5) | 平局 |
| 平均持续时间 | 76秒 | 269秒 | 快3.5倍 |
| 平均成本 | 0.18美元 | 0.54美元 | 便宜67% |
| 平均轮数 | 11.6 | 4.8 | 轮数多2.4倍 |
| 输出令牌 | 2,053 | 15,287 | 少7.4倍 |
| 输出质量 | 结构化,简洁 | 叙事性,详细 | 不同风格 |
输出样式差异:
两种方法产生了明显不同的报告风格:
两者准确地捕获了相同的数据(全部45个问题,正确的分类),但直接MCP的7.4倍更高的输出令牌数量反映了更冗长的解释性文本,而不是额外的见解。
复杂性权衡:
代码执行需要2.4倍更多的代理轮数(11.6比4.8),反映了以下开销:
然而,这种复杂性开销被执行速度和成本节省所抵消。额外的轮数发生得很快,因为代理将计算委托给了代码,而不是生成大量的分析文本。
即使直接MCP在技术上可以成功,代码执行也提供了显著更好的性能。在这个例子中的主要权衡是“结构化”与“叙事性”的输出风格。
我对这个结果感到意外,因为我原本预计直接MCP在小数据集上会有优势,因为上下文限制不是问题。相反,代码执行由于数据处理的效率提升,即使在小规模下也超过了其额外的复杂性开销。
技能优化: 代码执行方法的平均轮数(11.6)可以通过优化技能实现来减少。当前代理有时会迭代地编写和调试代码,而更高效的流程可能在较少的轮数内生成工作的代码。
你可以在本仓库的发布标签页中找到由这些实验生成的日志、指标和工作区文件的完整压缩包。
基于这些GitHub问题分析实验:
对于这个特定用例(分析仓库问题):
开放问题:
这些结果只涵盖了任务的一种类型。需要更多的测试:
值得测试的假设:
代码执行可能在以下情况下有益:
但这仍然是假设,尚未证明结论。具体情况可能有所不同。
帮助建立证据:
如果你用不同的用例测试此模式,请在MCP社区讨论中分享你的发现,或在此仓库中打开一个问题。更多的数据点将帮助社区了解何时采用这种方法是有意义的。
代理的代码 传输 MCP服务器
───────────── ───────── ──────────
import * as github from '...';
issues = await github.list_issues()
→ callMCPTool()
→ MCP客户端 ═=HTTP/Stdio═> GitHub API调用
认证
← 返回数据 <══════════ 数据处理
filtered = issues.filter(...) [停留在代码中]
sorted = filtered.sort(...) [停留在代码中]
console.log(sorted) → 回到LLM上下文
大数据集一次获取,然后在代码中处理,只有最终结果返回到LLM。这减少了与多次调用工具或在上下文中分析整个数据集相比的令牌消耗,以及完成任务所需的轮数。
创建一个.env文件:
GITHUB_PAT=your_github_personal_access_token # 如果使用此仓库中的GitHub MCP服务器配置,则需要。需要读取目标仓库问题的权限。
ANTHROPIC_API_KEY=your_anthropic_api_key # 或者:使用AWS Bedrock设置
CLAUDE_CODE_USE_BEDROCK=0 # 设置为1以使用AWS Bedrock模型
ANTHROPIC_DEFAULT_HAIKU_MODEL=us.anthropic.claude-haiku-4-5-20251001-v1:0 # 如果使用AWS Bedrock,最新Haiku不会默认使用,需要显式设置
模型选择:
代理根据CLAUDE_CODE_USE_BEDROCK环境变量自动选择适当的模型:
当CLAUDE_CODE_USE_BEDROCK=0(默认):使用标准Anthropic API模型
claude-haiku-4-5-20251001claude-sonnet-4-5-20250929当CLAUDE_CODE_USE_B_1:使用AWS Bedrock模型
us.anthropic.claude-haiku-4-5-20251001-v1:0us.anthropic.claude-sonnet-4-5-20250929-v1:0npm install
必需 - 包装器未提交到git(除了client.ts):
npm run generate-wrappers
这将连接到GitHub MCP服务器并为所有40个工具生成TypeScript包装器。
注意: 代理在执行前自动运行ensure-wrappers.ts,它:
.metadata.json文件中跟踪元数据代理支持两种执行模式,用于比较MCP代码执行与基线MCP工具调用方法。详细的会话日志捕获了两种模式的详细指标,使您可以直接比较令牌使用情况、成本和执行模式。
# 默认:代码执行模式,带有默认任务
npm start
# 直接MCP模式
npm run start:mcp # 直接MCP模式
# 分析结果任务(用于比较实验结果)
npm run start:analyze-results
# CLI选项
tsx agent.ts --task=task-analyze-results.md # 指定不同的任务文件
tsx agent.ts --model=haiku # 使用Haiku而不是Sonnet
tsx agent.ts --help # 显示所有选项
任务输入文件:
某些任务(如task-analyze-results.md)需要输入文件。要指定输入文件,请直接编辑任务文件中的路径prompts/task-analyze-results.md。
### 会话日志
每次运行都会自动创建带有唯一会话ID的详细会话日志:
```bash
会话ID:code-execution-2025-11-18T15-32-10-123Z
日志文件:
logs/{session-id}.json - 用于程序分析的完整结构化数据logs/{session-id}.md - 便于审阅的人类可读markdown格式捕获的内容:
失败运行追踪:
对于因错误而失败的运行,会话ID会被前缀为FAILED__以便于识别。日志文件和任何归档的工作区数据都会使用此前缀,使得诊断问题变得简单。
用途: 会话日志让你能够精确重现执行期间发生了什么,比较不同运行,并调试代理行为。它们特别适用于比较代码执行与直接MCP模式。
为了验证你的MCP配置是否正确,代理会在启动时自动检查包装器可用性和连通性。如果有问题,你会看到详细的错误消息。
你也可以手动重新生成包装器以测试连通性:
npm run generate-wrappers
这将连接到服务器并重新生成所有工具包装器。如果成功,你会看到显示生成工具数量的输出。
npm start
代理将:
workspace_archive/prompts/加载模式特定的系统提示和任务.mcp.json文件配置要连接的MCP服务器:
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${GITHUB_PAT}"
}
}
}
}
注意: 加载配置时会替换环境变量(例如,${GITHUB_PAT})。
客户端支持:
type: "http"):使用StreamableHTTPClientTransport连接远程服务器type: "stdio"):使用StdioClientTransport连接本地子进程服务器servers/client.ts实现了callMCPTool():
export async function callMCPTool<T = any>(
serverName: string, // 'github'
toolName: string, // 'get_me'(短名称)
input: any
): Promise<T> {
// 获取或创建MCP客户端连接
let client = mcpClients.get(serverName);
if (!client) {
client = await connectToMCPServer(serverName);
mcpClients.set(serverName, client);
}
// 调用MCP工具
const result = await client.callTool({
name: toolName,
arguments: input
});
// 解析并返回结果
if (result.content && result.content[0]) {
const content = result.content[0];
if ('text' in content && content.text) {
try {
return JSON.parse(content.text);
} catch {
return content.text as T;
}
}
}
return result as T;
}
在生成包装器的过程中,脚本会从MCP服务器的InitializeResult(如果提供)中捕获服务器指令,并将它们保存到servers/{server-name}/README.md。
代理应在使用该服务器的工具之前阅读这些指令,以确保正确的使用模式。你可以查看[这篇博客](https://blog.modelcontextprotocol.io/posts/2025