MCP代码执行工具

使用Claude Agent SDK实现MCP代码执行模式的演示工具,用于生成RPC包装文件并支持AI代理按需发现和使用工具。

  • by olaservo
  • TypeScript
  • 本地部署
  • 10 星标
  • 更新于
在 GitHub 查看 olaservo/code-execution-with-mcp

使用说明

使用Claude Agent SDK的MCP代码执行演示

使用Claude Agent SDK演示MCP代码执行模式。

概述

这个项目的灵感来自 Anthropic的这篇博客文章这篇博客文章没有完整的代码,但它确实包含了很多关于如何实现这种模式的提示。

我希望这个版本保持简洁,并尽可能地与博客保持一致。我决定使用 Claude代理SDK 因为它已经支持Agent Skills、MCP和其他开箱即用的必要功能。

此模式的实现为MCP工具生成RPC包装文件。这些包装器是类型安全的接口,代理可以发现并将其用作代码。

这难道不是MCP是一个巨大的错误/过度设计等的证据吗?

我(有点)惊讶地看到,人们对这篇博客文章的所有反应都认为这是证据 MCP is bad。这只是一种新的使用方式。有时它有意义,有时则没有意义。

我非常喜欢使用经验证据来更好地理解这样的事情,而不是陷入理论辩论。希望对这些想法的具体解释有助于产生更多的证据,这样你就可以自己决定这种解决方案是否适合用例。

我也喜欢 沙纳克乔希他的回应 这条推文 与此主题相关:

MCP处理分发和发现(可安装的应用程序,如连接器,公开功能),而代码模式只处理纯执行(模型使用MCP模式自动生成的SDK生成代码)。MCP更像是一个包装层,您可以获得生态系统效益和可组合性。

⚠️ 安全和沙盒限制

重要提示: 此实现目前确实 不 在Claude Agent SDK中包含开箱即用的可用沙盒配置。这意味着:

  • 无限制访问:执行代码的代理可以完全访问您的文件系统、网络和系统资源
  • 危险分子:没有针对潜在有害操作的内置保护
  • 慎用:仅在您完全理解的任务的可信环境中使用此功能

计划改进:

我们正在积极研究沙盒解决方案,以解决这些局限性:

  1. 拟人沙盒运行时 -一个轻量级的沙盒工具,使用本机操作系统原语(macOS上的沙盒exec,Linux上的bubblewrap)来强制执行文件系统和网络限制
  2. 基于Docker的配置 -一种用于进程隔离的替代容器化方法

建议:

  • 仅在受控的非生产环境中执行代码
  • 在允许执行之前检查生成的代码
  • 避免与不受信任的数据源或提示一起使用
  • 考虑根据您的安全要求实施自己的沙盒解决方案

在集成强大的沙盒之前,请将其视为实验性演示,而不是生产就绪的基础设施。

理解模式

什么是MCP代码执行?

MCP代码执行是构建高效AI代理的实验模式。

  • 将MCP工具作为RPC包装文件公开 在文件系统结构中(类型安全接口,实际实现留在MCP服务器上)
  • 按需发现和加载工具 通过探索文件系统
  • 在代码环境中执行数据处理 在将结果传递给LLM之前
  • 代理发现并构建自己的持久、可重用技能 利用这些工具包装器。

假设

一种假设是,代码执行应该通过以下方式显著减少数据密集型工作流的令牌消耗:

  1. 按需发现工具,而不是预先加载所有定义
  2. 在将结果传递给LLM之前,在代码中处理大型数据集
  3. 无需多次工具调用即可实现复杂的数据转换

此存储库提供这两种模式,因此您可以测试和比较用例的实际结果。

性能结果示例:分析GitHub问题

我在两个真实世界的存储库中测试了这两种方法:

喊出来 @约翰逊1 感谢您提出GitHub问题用例的想法!

大型数据集示例:Claude代码库(5205期未解决问题)

这 克劳德代码仓库 提供了一个良好的压力测试 约5k个未决问题 在我进行测试时,这是一个足够大的数据集,可以揭示方法之间的基本架构差异。

每种方法5次运行的结果:

代码在所有5次运行中都成功执行。Claude始终如一地创造可重用的技能,并使用 list_issues 工具包装器,用于成功获取和处理所有问题。

Direct MCP在所有5次运行中都失败了,在尝试同时将所有问题加载到上下文中后出现了上下文溢出错误。

方法 成功率 平均持续时间 平均成本 产出
代码执行 100%(5/5) 343秒(5.7分钟) 0.34美元 完成报告+JSON
直接MCP 0%(5/5) 31秒失败 N/A N/A

直接MCP失败的原因:

  • 试图同时将所有5205个问题加载到上下文中
  • 在达到200K代币限制之前达到~600-800个问题(第3回合)

为什么代码执行成功:

  • 编写TypeScript,使用list_issues工具包装器批量获取问题
  • 内存中已处理和聚合的数据(价值约52万个令牌)
  • 仅将摘要统计信息传递给模型(约10K个令牌)

小数据集:Anthropic SDK Python(45期)

当使用 anthropic-sdk-python 存储库(约45个开放问题),两种方法都成功了,但代码执行仍然明显优于:

度量 代码执行 直接MCP 优势
成功率 100%(5/5) 100%(5/6) 平局
平均持续时间 76秒 269秒 3.5倍更快
平均成本 0.18 0.54美元 便宜67%
平均转弯数 11.6 4.8 2.4倍以上转弯
输出令牌 2053 15287 7.4×更少
输出质量 结构化、简洁 叙述性、详细 不同风格

输出样式差异:

这些方法产生了明显不同的报告风格:

  • 代码执行: 生成结构化、以数据为中心的报告(约9.7KB),针对快速扫描和机器解析进行了优化。
  • 直接MCP: 生成更丰富的叙述性报告(~15.8-19.5KB),具有更多的背景和解释。

两者都准确地捕获了相同的数据(所有45个问题,正确的分类),但直接MCP的7.4倍更高的输出令牌计数反映了更详细的解释性文本,而不是额外的见解。

复杂性权衡:

代码执行需要2.4倍的代理轮数(11.6比4.8),反映了以下开销:

  • 编写和调试TypeScript代码
  • 执行脚本和查看输出
  • 迭代数据处理逻辑

然而,这种复杂性开销被执行速度和成本节约所抵消。额外的转变发生得很快,因为代理将计算委托给代码,而不是生成大量的分析文本。

即使直接MCP在技术上可以成功,代码执行也能提供更好的性能。这个例子中的主要权衡是“结构化”与“叙事性”的输出风格。

我对这一结果感到非常惊讶,因为我预计直接MCP在不受上下文限制的小型数据集上具有优势。相反,即使在小规模上,脱离上下文数据处理带来的代码执行效率提高也超过了其额外的复杂性开销。

未来改进

技能优化: 通过优化Skill实现,代码执行方法的循环次数(平均11.6次)可能会减少。当前代理有时会迭代地编写和调试代码,而更高效的工作流可能会以更少的轮次生成工作代码。

完整实验数据

你可以找到 完整zip存档 这些实验生成的日志、指标和工作区文件位于此仓库的“发布”选项卡中。

这些结果表明了什么

基于这些GitHub问题分析实验:

对于此特定用例(分析存储库问题):

  • 代码执行是处理大型数据集(5K+问题)的唯一可行方法
  • 即使在小数据集(45个问题)上,代码执行速度也比直接MCP高3.5倍,成本高67%
  • 主要的权衡是结构化与叙述性输出风格,代码执行需要更多的代理轮换

开放式问题:

这些结果仅涵盖一种类型的任务。需要更多的测试:

  • 不同的数据类型(结构化与非结构化)
  • 不同的操作(CRUD与分析)
  • 不同的MCP服务器(文件系统、数据库、API)
  • 实时与批处理工作负载
  • 需要来回迭代的任务

值得检验的假设:

代码执行可能在以下情况下有益:

  • 数据集大小未知或可能很大
  • 分析前需要过滤/转换数据
  • 需要连接多个数据源
  • 将重复相同的工作流程

但这些仍然是假设,而不是经过验证的结论。您的里程数可能会有所不同。

帮助建立证据:

如果您使用不同的用例测试此模式,请在 MCP社区讨论 或者在此回购中发行。更多的数据点将有助于社区了解这种方法何时可行。

执行流程

Agent's Code                      Transport              MCP Server
─────────────                     ─────────              ──────────
import * as github from '...';

issues = await github.list_issues()
  → callMCPTool()
    → MCP Client                  ══HTTP/Stdio═>        GitHub API calls
                                                         Authentication
    ← Returns data                <══════════            Data processing

filtered = issues.filter(...)     [Stays in code]
sorted = filtered.sort(...)       [Stays in code]

console.log(sorted)               → Back to LLM context

大型数据集被提取一次,在代码中处理,只有最终结果返回给LLM。与进行多个工具调用或在上下文中分析完整数据集相比,这减少了令牌消耗,以及完成任务所需的周转次数。


设置

1.环境变量

创建一个 .env 文件:

GITHUB_PAT=your_github_personal_access_token # Needed if using the GitHub MCP Server config in this repo.  Permissions to read issues for the target repo is required.
ANTHROPIC_API_KEY=your_anthropic_api_key # OR: use AWS Bedrock setting
CLAUDE_CODE_USE_BEDROCK=0  # Set to 1 to use AWS Bedrock models
ANTHROPIC_DEFAULT_HAIKU_MODEL=us.anthropic.claude-haiku-4-5-20251001-v1:0 # If using AWS Bedrock, latest Haiku is not used by default and needs to be explicitly set
SKILLS_REF_BIN=/path/to/agentskills # Optional. Absolute path to the agentskills validator if not on PATH. See section 3 below.

型号选择:

代理根据以下内容自动选择合适的模型 CLAUDE_CODE_USE_BEDROCK 环境变量:

  • 当 CLAUDE_CODE_USE_BEDROCK=0 (默认):使用标准Anthropic API模型

    • claude-haiku-4-5-20251001
    • claude-sonnet-4-5-20250929
  • 当 CLAUDE_CODE_USE_BEDROCK=1:使用AWS基岩模型

    • us.anthropic.claude-haiku-4-5-20251001-v1:0
    • us.anthropic.claude-sonnet-4-5-20250929-v1:0

2.安装依赖项

npm install

3.安装技能验证器(推荐)

当代理在下面创建新技能时 .claude/skills/,系统提示要求它使用来自的引用验证器验证结果 代理技能 项目。PyPI包是 skills-ref 安装的CLI二进制文件是 agentskills. 安装它。 如果没有它,代理的创作技能可能会出现前端错误,这些错误会悄无声息地使它们无法被发现(例如 name 字段与目录名不匹配、命名规则违规或描述超过1024个字符限制)——内联检查表中的错误类型 prompts/system-code-execution.md 不太可能可靠地捕获。如果验证器不存在,包装器会优雅地降级(代理会报告并继续),但你会放弃机械安全网。

需要 Python≥3.11. pipx (每个CLI隔离的venv)是最干净的选项:

pipx install skills-ref

选择:

  • uv tool install skills-ref
  • 直接: py -3.11 -m pip install --user skills-ref 在Windows上,或 python3.11 -m pip install --user skills-ref 其他地方。

验证 agentskills --help.

如果 agentskills 二进制未打开 PATH (常见于 pip install --user 在Windows上,警告将指向例如。 %APPDATA%\Python\Python311\Scripts),或者将该目录添加到 PATH 或设置 SKILLS_REF_BIN 在运行代理程序之前,请先将文件复制到绝对exe路径。包装在 scripts/validate-skill.ts 荣誉 SKILLS_REF_BIN 如果设置。

然后,代理通过以下方式调用包装器 npm run validate-skill -- .claude/skills/<skill-name>当写作技巧存在时,代理人遵循的创作指南 prompts/skill-authoring-reference.md,从上游文件中提炼出来。

注:上游标签 skills-ref 作为演示库,而不是生产门。将其输出视为建议。

4.生成MCP包装

必需的 -包装器未被检入git:

npm run generate-wrappers

这将连接到GitHub MCP服务器,并为所有工具生成TypeScript包装器。

包装器生成、运行时桥和过时生命周期由 独立 @olaservo/mcp-wrappers 包裹。 npm run generate-wrappers 和 npm run ensure-wrappers 是薄薄的包装 mcp-wrappers generate / mcp-wrappers ensure CLI;生成的工具文件导入 callMCPTool 从 @olaservo/mcp-wrappers/runtime.

注: 代理自动运行 ensureWrappers() (从包中)执行前,其中:

  • 仅在包装丢失或过期时才重新生成包装
  • 如果再生失败(例如,网络问题),则回退到现有的包装器
  • 跟踪中的元数据 .metadata.json 文件

用法

执行模式

该代理支持两种执行模式,用于将MCP代码执行与基线MCP工具调用方法进行比较。
详细的会话日志记录了这两种模式的详细指标,可以直接比较令牌使用、成本和执行模式。

运行代理

# Default: Code execution mode with default task
npm start

# Direct MCP mode
npm run start:mcp           # Direct MCP mode

# Analyze results task (for comparing experiment results)
npm run start:analyze-results

# CLI options
tsx agent.ts --task=task-analyze-results.md  # Specify a different task file
tsx agent.ts --model=haiku                   # Use Haiku instead of Sonnet
tsx agent.ts --help                          # Show all options

任务输入文件:

一些任务(如 task-analyze-results.md)需要输入文件。要指定输入文件,请在任务文件中直接编辑路径 prompts/task-analyze-results.md.


### Session Logging

Each run automatically creates detailed session logs with a unique session ID:

```bash
Session ID: code-execution-2025-11-18T15-32-10-123Z

日志文件:

  • logs/{session-id}.json -用于程序分析的完整结构化数据
  • logs/{session-id}.md -人类可读的标记格式供审查

捕获的内容:

  • 每个工具都有名称和完整的输入参数
  • 每一条辅助信息和推理
  • 工具完成状态和错误
  • 每个步骤的计时信息
  • 最终指标(成本、代币、持续时间)

运行跟踪失败: 对于因错误而失败的运行,会话ID前缀为 FAILED__ 为了便于识别。日志文件和任何存档的工作区数据都使用此前缀,使诊断问题变得简单。

用途: 会话日志允许您准确回放执行过程中发生的事情,比较不同的运行,并调试代理行为。在比较代码执行与直接mcp模式时,它们特别有用。

验证MCP设置

为了验证您的MCP配置是否正确,代理将在启动时自动检查包装器的可用性和连接。如果有问题,您将看到详细的错误消息。

您还可以手动重新生成包装器以测试连接:

npm run generate-wrappers

这将连接到服务器并重新生成所有工具包装器。如果成功,您将看到显示生成的工具数量的输出。

运行你的第一个任务

npm start

代理人将:

  1. 将以前的工作区内容(如果有的话)存档到 workspace_archive/
  2. 清除工作区目录以进行干净运行
  3. 确保MCP包装是最新的(如果过时则重新生成)
  4. 加载模式特定的系统提示和任务 prompts/
  5. 使用所选执行模式执行任务
  6. 显示全面的指标并保存会话日志

运作原理

MCP配置

这 .mcp.json 文件配置要连接到哪些MCP服务器:

{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": {
        "Authorization": "Bearer ${GITHUB_PAT}"
      }
    },
    "guildbridge": {
      "type": "http",
      "url": "https://guildbridge.modelcontextprotocol.io/mcp",
      "auth": "oauth"
    }
  }
}

注: 环境变量(例如。, ${GITHUB_PAT})在加载配置时被替换。

服务器身份验证样式 (由 @olaservo/mcp-wrappers):

  • 静态凭证 — headers 带有令牌/PAT(例如。 github).
  • OAuth 2.1 — "auth": "oauth"该库运行动态客户端注册+PKCE, 打开浏览器一次,并在下缓存令牌 ~/.mcp-wrappers/oauth/ (见 包README).

⚠️ guildbridge 这只是一个例子,不是开放式访问。 这是MCP 贡献者Discord桥接和访问仅限于MCP维护者/贡献者-- 大多数用户无法对其进行授权。它是为了演示如何配置 OAuth 2.1服务器(并为 task-auth-correlation.md 示例)。删除 guildbridge 条目,或用您可以访问的OAuth服务器替换它。

运输支持

客户端支持:

  • HTTP传输 (type: "http"):用途 StreamableHTTPClientTransport 用于远程服务器
  • 标准运输 (type: "stdio"):用途 StdioClientTransport 用于本地子流程服务器

RPC客户端网桥

运行时桥位于 @olaservo/mcp-wrappers 包(通过生成导入 包装纸,如 @olaservo/mcp-wrappers/runtime).它实现了 callMCPTool():

export async function callMCPTool<T = any>(
  serverName: string,
  toolName: string,
  input: any
): Promise<T> {
  let client = mcpClients.get(serverName);
  if (!client) {
    client = await connectToMCPServer(serverName);
    mcpClients.set(serverName, client);
  }

  const result = await client.callTool({
    name: toolName,
    arguments: input
  });

  // Tool signalled failure — throw so generated code can use try/catch.
  if (result.isError) {
    const msg = Array.isArray(result.content)
      ? result.content
          .filter((c: any) => c.type === 'text')
          .map((c: any) => c.text)
          .join('\n')
      : '';
    throw new Error(
      `${serverName}.${toolName} failed: ${msg || 'tool call failed'}`
    );
  }

  // Per MCP spec: a tool that declares outputSchema MUST return structuredContent.
  // Prefer it over re-parsing the back-compat TextContent mirror.
  if (result.structuredContent !== undefined) {
    return result.structuredContent as T;
  }

  // Fallback: parse the content blocks. Join all text chunks (not just [0]),
  // then JSON.parse if the joined text parses cleanly.
  if (Array.isArray(result.content) && result.content.length > 0) {
    const allText = result.content.every((c: any) => c.type === 'text');
    if (allText) {
      const text = result.content
        .map((c: any) => ('text' in c ? c.text : ''))
        .join('\n');
      try {
        return JSON.parse(text);
      } catch {
        return text as T;
      }
    }
    // Mixed content (text + image/audio/resource) — hand back the array,
    // since binary blocks have no clean plain-value representation.
    return result.content as T;
  }

  return result as T;
}

服务器说明

在包装器生成过程中,脚本捕获 服务器指令 从MCP服务器 InitializeResult (如果提供)并将其保存到 servers/{server-name}/README.md.

代理在使用该服务器的工具之前应阅读这些说明,以确保正确的使用模式。您可以查看 这个博客 有关此处通常包含的内容的更多信息。


代理技能发现

Claude Agent SDK会自动从以下位置发现技能 .claude/skills/ 当 settingSources 已配置。

例子:

const options = {
  settingSources: ['project' as const],  // Loads .claude/settings.json and discovers skills
  allowedTools: ["Bash", "Read", "Write", "List", "Skill"],  // Include "Skill" tool
  // ... other options
};

下一步

设置完成后,您可以:

  1. 编辑 prompts/task-default.md 更改代理应执行的任务
  2. 比较执行模式 通过在两种模式下运行相同的任务并分析会话日志
  3. 添加更多MCP服务器 到 .mcp.json (支持HTTP和stdio传输)并重新生成包装器
  4. 模型实验 使用 --model=haiku 或 --model=sonnet

资源

官方文档

相关文章