MCP 协议在工具层的集成方案
MCP 协议在工具层的集成方案
学习目标
- 理解 ChatDev 的 ToolManager 统一抽象层
- 掌握 MCP Remote(HTTP SSE)和 MCP Local(Stdio)两种接入方式的实现差异
- 了解工具注册、Schema 导出与缓存机制
- 能够在 YAML 中配置 MCP 工具
前置知识
本章涉及 MCP 协议的通用原理,建议先阅读:
下文聚焦 ChatDev 的具体 ToolManager 实现。
项目实践
ToolManager 统一抽象
ChatDev 的 ToolManager 在 runtime/node/agent/tool/tool_manager.py 中统一管理三种工具类型:
| 工具类型 | YAML 配置 | 实现方式 |
|---|---|---|
| Function Tool | type: function | 从 functions/function_calling/ 目录加载 Python 函数 |
| MCP Remote | type: mcp_remote | 通过 HTTP SSE 连接远程 MCP Server |
| MCP Local | type: mcp_local | 通过 Stdio 启动本地 MCP Server 进程 |
Function Calling 工具
Function 工具是 ChatDev 的默认工具类型:
tooling: - type: function config: tools: - name: apply_text_edits # 内置工具 - name: uv_related:All # 工具组通配符 - name: deep_research:All # 自定义工具组 timeout: 30.0内置工具列表位于 functions/function_calling/:
| 工具 | 功能 |
|---|---|
apply_text_edits | 文件内容编辑 |
create_folder | 创建目录 |
delete_path | 删除路径 |
read_file_segment | 文件片段读取 |
search_in_files | 文件搜索 |
save_file | 文件保存 |
execute_code | Python 代码执行 |
web_search | 网页搜索(DDGS) |
get_webpage_content | 网页内容获取 |
MCP Remote 接入
tooling: - type: mcp_remote config: server: "http://localhost:8080/mcp" headers: Authorization: "Bearer ${MCP_TOKEN}" timeout: 10.0实现细节:
ToolManager ↓StreamableHttpTransport(server_url, headers) ↓FastMCP Client ↓async with client: return await client.list_tools()重试策略:指数退避,最多 3 次尝试:
尝试 1: 等待 0.5s尝试 2: 等待 1.0s尝试 3: 等待 2.0sMCP Local 接入
tooling: - type: mcp_local config: command: "uv" args: ["run", "python", "mcp_server.py"] cwd: "./mcp_example" env: API_KEY: "${API_KEY}" inherit_env: true startup_timeout: 10.0 wait_for_log: "Server started"实现细节:
ToolManager ↓StdioTransport(command, args, cwd, env) ↓启动子进程 → 等待 wait_for_log 信号 ↓FastMCP Client ↓list_tools()生命周期管理:
- Stdio 客户端在工具调用时启动
- 进程退出时自动清理缓存
startup_timeout控制等待 Server 就绪的最长时间
工具缓存机制
ToolManager 使用两种缓存:
| 缓存类型 | 缓存内容 | 缓存键 |
|---|---|---|
| FunctionManager 缓存 | Python 函数对象 | functions_dir 路径 |
| MCP 工具缓存 | MCP Server 返回的工具列表 | launch_key(配置哈希) |
MCP 缓存特别重要——避免每次工具调用都重新连接 Server 和获取工具列表。
Schema 注册
所有工具类型都通过 schema_registry 模块注册 Schema:
register_model_provider_schema(name, label=label, summary=summary)这使得前端可以自动生成工具配置 UI。
问题与规避
| 问题 | 表现 | 规避策略 |
|---|---|---|
| MCP Server 启动超时 | startup_timeout 过期后报错 | 增大 startup_timeout 或优化 Server 启动速度 |
| MCP 工具列表缓存过期 | Server 更新后客户端未感知 | 通过 launch_key 变更强制刷新缓存 |
| HTTP 重试风暴 | MCP Remote 不可用时持续重试 | 最多 3 次 + 指数退避限制 |
| Stdio 进程泄漏 | 客户端断开后子进程残留 | 进程退出时自动清理 |
| 工具组通配符解析失败 | name: xxx:All 中组不存在 | 确保工具组在 functions/ 中已定义 |
设计取舍
FastMCP vs 原生 MCP SDK
ChatDev 选择使用 fastmcp 而非原生 mcp SDK:
| 维度 | FastMCP | 原生 MCP SDK |
|---|---|---|
| API 简洁度 | 高(封装了传输层细节) | 低(需手动管理传输) |
| StreamableHttpTransport | 原生支持 | 需额外配置 |
| StdioTransport | 原生支持 | 需手动实现 |
| ChatDev 的选择 | ✅ 使用 | ❌ 仅间接依赖 |
三种工具类型并存的原因
- Function:最快(本地调用),但需编写 Python 代码
- MCP Remote:适合微服务架构(MCP Server 独立部署)
- MCP Local:适合轻量级场景(按需启动,用完即停)