ChatDev 2.0 DevAll MCP Tooling 实战指南:Remote HTTP 与 Local stdio 双模式配置、FastMCP 集成与安全运维
【免费下载链接】ChatDevChatDev 2.0: Dev All through LLM-powered Multi-Agent Collaboration项目地址: https://gitcode.com/GitHub_Trending/ch/ChatDev
本篇技术指南以 MCP Tooling 指南 为骨架,系统讲解 ChatDev 2.0(DevAll)中 MCP(Model Context Protocol)工具绑定的完整配置体系:mcp_remote(HTTP 远端)与mcp_local(stdio 本地进程)两种模式、McpRemoteConfig/McpLocalConfig全部字段、FastMCP 示例服务器的搭建与接入,以及安全、运维与调试的落地实践。读完本文,你将能够在 YAML 工作流中为 Agent 节点接入任意标准 MCP 服务,并理解工具清单加载、进程生命周期与结果归一化的底层实现。
1. 双模式总览:Remote (HTTP) 与 Local (stdio)
MCP 工具在 DevAll 中被明确拆分为两种模式,分别对应tooling.type: mcp_remote与tooling.type: mcp_local。旧的type: mcpschema 已下线,在 YAML 与文档中必须全部迁移到上述两种模式之一。
| 模式 | Tooling type | 适用场景 | 关键字段 |
|---|---|---|---|
| Remote | mcp_remote | 已部署的 HTTP(S) MCP 服务器(如 FastMCP、Claude Desktop Connector、自建代理) | server、headers、timeout |
| Local | mcp_local | 通过 stdio 握手的本地可执行脚本(Blender MCP、CLI 工具等) | command、args、cwd、env等进程字段 |
两种模式的取舍很清晰:Remote 适合已上线、可被多个客户端共享的服务端点;Local 适合需要拉起本地进程、与宿主机工具(如 Blender)直接交互的场景。在 Tooling 模块总览(docs/user_guide/zh/modules/tooling/README.md)中,二者的定位进一步被区分为"直连 HTTP 服务"与"拉起本地进程并通过 stdio 连接"。
从配置模型上看,ToolingConfig的type字段通过tooling_type_registry注册表路由到对应的配置类——目前注册了function、mcp_remote、mcp_local三类(见 entity/configs/node/tooling.py#L568-L582)。type的枚举选项与描述会动态注入到配置 schema 中,这意味着你在 Web UI 配置表单里看到的选项与 YAML 解析器接受的取值完全一致。
2. McpRemoteConfig:接入 HTTP(S) 远端 MCP 服务器
2.1 字段说明
| 字段 | 说明 |
|---|---|
server | 必填,MCP HTTP(S) 端点,例如https://api.example.com/mcp。 |
headers | 可选,附加 HTTP 头(如Authorization)。 |
timeout | 可选,单次工具调用超时时间(秒)。 |
除上述文档字段外,从源码 entity/configs/node/tooling.py#L310-L422 可以看到McpRemoteConfig还定义了以下高级字段:
cache_ttl:工具清单缓存秒数,默认0.0,schema 描述为"0 表示禁用缓存以便热更新"。tool_sources:仅包含meta.source命中列表的 MCP 工具;省略时默认值为["mcp_tools"]。
2.2 YAML 配置示例
nodes: - id: remote_mcp type: agent config: tooling: type: mcp_remote config: server: https://mcp.mycompany.com/mcp headers: Authorization: Bearer ${MY_MCP_TOKEN} timeout: 15注意tooling在节点配置中是一个列表,可同时挂载多个工具源,每个条目包含type、config以及可选的prefix(用于给该源的所有工具加前缀,避免多源工具名冲突,详见第 5 节)。
DevAll 会在列举/调用工具时连接该 URL,并携带headers。若服务器不可达,将直接抛出错误,不再尝试本地回退——这是 Remote 模式与"自动发现工具"之间的明确契约。
2.3 底层调用链
工具清单的抓取实现在 runtime/node/agent/tool/tool_manager.py#L65-L91 的_fetch_mcp_tools_http:
- 使用 FastMCP 的
StreamableHttpTransport建立 HTTP 传输,headers会原样透传给每次请求; - 未显式配置
timeout时,使用默认值DEFAULT_MCP_HTTP_TIMEOUT = 10.0秒; - 内置3 次重试,重试间隔呈指数退避(0.5s → 1s → 2s),全部失败后抛出最后一次异常。
工具清单抓取后缓存在_mcp_tool_cache中,缓存键由cache_key()生成,其载荷为server、排序后的headers与timeout的哈希(见 entity/configs/node/tooling.py#L416-L422)。也就是说,只要这三者不变,同一远端服务器不会重复握手。
3. McpLocalConfig:拉起本地 stdio MCP 进程
3.1 字段说明
mcp_local直接在config下声明进程参数:
command/args:可执行文件与参数(如uvx blender-mcp)。cwd:可选工作目录。env/inherit_env:定制子进程环境;默认继承父进程后再覆盖。startup_timeout:等待wait_for_log命中的最长秒数。wait_for_log:stdout 正则,用于判定"就绪"。
从源码 entity/configs/node/tooling.py#L425-L566 可以看到McpLocalConfig的默认值与校验规则:
args默认空列表,且每一项必须是字符串;inherit_env默认True;若设为False,子进程将以空环境启动,仅在env中注入显式声明的变量(实现见 tool_manager.py#L507-L518 的_StdioClientWrapper:os.environ.copy() if config.inherit_env else {},再用config.env覆盖);startup_timeout默认10.0秒;- 另有
cache_ttl字段,语义与 Remote 一致。
3.2 YAML 配置示例
nodes: - id: local_mcp type: agent config: tooling: type: mcp_local config: command: uvx args: - blender-mcp cwd: ${REPO_ROOT} wait_for_log: "MCP ready" startup_timeout: 8运行期间 DevAll 会保持该进程常驻,并通过 stdio 传输 MCP 数据帧。这在仓库的 3D 生成工作流中有真实落地案例:yaml_instance/blender_3d_builder_simple.yaml#L219-L224 中的Procedural Architect与Reviewer节点都通过mcp_local+uvx blender-mcp直接驱动本机 Blender。
3.3 进程生命周期实现
_StdioClientWrapper(runtime/node/agent/tool/tool_manager.py#L507-L558)是 Local 模式的核心:
StdioTransport以keep_alive=True建立传输,客户端按launch_key(由command/args/cwd/env/inherit_env/startup_timeout/wait_for_log计算,见 tooling.py#L556-L566)缓存在_mcp_stdio_clients中,同一配置复用同一子进程;- 每个 stdio 客户端在独立 daemon 线程中运行自己的 asyncio 事件循环,
list_tools/call_tool通过run_coroutine_threadsafe投递到该循环执行,并用asyncio.Lock串行化调用; - 进程终止由 DevAll 负责(
close()会向事件循环投递关闭协程并回收线程),因此本地脚本必须能正确处理SIGTERM/SIGKILL,避免留下僵尸进程或未落盘数据。
4. FastMCP 示例服务器:一键起一个 MCP 服务
仓库自带一个极简的 FastMCP 示例服务器 mcp_example/mcp_server.py:
from fastmcp import FastMCP import random from datetime import datetime from typing import Dict, Optional # Initialize MCP server mcp = FastMCP( "Company Simple MCP Server", # api_route="/mcp/", debug=True ) @mcp.tool def rand_num(a: int, b: int) -> int: """Generate a random number between a and b.""" num = random.randint(a, b) print(num) return num if __name__ == "__main__": print("Starting simple MCP server...") print("Run with: uv run fastmcp run simple_server.py --transport 'streamable-http' --port 8001") # mcp.run(transport="streamable-http", host="127.0.0.1", port=8001) mcp.run()启动命令:
uv run fastmcp run mcp_example/mcp_server.py --transport streamable-http --port 8010接入方式有两种:
- 以 Remote 模式使用:只需将
server指向http://127.0.0.1:8010/mcp; - 以 Local 模式使用:可将
command设置为uv run fastmcp run ...,并保持transport=stdio(fastmcp run默认即 stdio 传输)。
一个与上文配套的完整 Remote 工作流示例是 yaml_instance/demo_mcp.yaml:节点 A(诗人)通过mcp_remote挂载http://127.0.0.1:8001/mcp上的rand_num工具获取随机数并据此写诗,节点 B(评论家)对诗歌进行批评分析,两个节点通过edges串联。该示例同时展示了 MCP 工具与 LLM 角色分工的组合方式——把"取数"这类确定性操作外包给 MCP 工具,让 Agent 专注于创作与推理。
5. 工具清单与执行链路:从 YAML 到 LLM 工具调用的源码路径
5.1 配置解析
YAML 中的tooling列表会被解析为ToolingConfig(entity/configs/node/tooling.py#L585-L660):
- 依据
type从tooling_type_registry取得对应配置类; - 缺失
config块或type非法时抛出ConfigError; prefix可选——若多个工具源出现重名工具,ToolManager会在构建 spec 时检测重复并抛出错误,提示"请使用唯一prefix"(tool_manager.py#L127-L143),最终工具名形如mcp1_rand_num。
5.2 工具清单(spec)构建
get_tool_specs(tool_manager.py#L97-L145)按类型分发:
mcp_remote→_build_mcp_remote_specs:抓取远端工具列表,将每个工具的name、description、inputSchema转换为 Provider 无关的ToolSpec,并在元数据中记录source: "mcp"、server与mode: "remote";mcp_local→_build_mcp_local_specs:走 stdio 客户端list_tools(),元数据记录mode: "local"。
这些 spec 最终注入到 LLM 的函数调用 schema 中,由模型自主决定何时调用哪个工具。
5.3 工具执行与结果归一化
execute_tool(tool_manager.py#L147-L174)按类型分发到_execute_mcp_remote_tool/_execute_mcp_local_tool。值得关注的是返回值的归一化处理(_normalize_mcp_result与_convert_mcp_content_to_blocks,tool_manager.py#L308-L423):
- 文本内容(
TextContent)→ 直接转为文本消息块; - 图片 / 音频(
ImageContent/AudioContent)→ base64 解码后注册为附件(依赖tool_context中的AttachmentStore,即 utils/attachments.py),生成带 mime 类型的附件消息块; - 内嵌资源(
EmbeddedResource)→ 文本资源保留 URI 与 mime 类型;Blob 资源按 mime 推断为图片/音频/视频/文件; - 资源链接(
ResourceLink)→ 转为带 URI 的数据块。
这意味着 MCP 服务器返回的富媒体结果(如 Blender 渲染截图、音频片段)可以无缝进入 Agent 的消息流与附件体系,供后续节点或用户查看。
6. 安全与运维实践
- 网络暴露:Remote 模式建议置于 HTTPS 反向代理之后,并结合 API Key / ACL;Local 模式进程仍可访问宿主机文件,请限制其权限(例如以最小权限用户运行、避免
cwd指向敏感目录)。 - 资源回收:Local 模式由 DevAll 负责终止子进程,务必确保脚本可以正确处理
SIGTERM/SIGKILL,及时清理临时文件与外部连接。 - 日志定位:为
wait_for_log输出清晰的"ready"日志(如示例中的Starting simple MCP server...),便于在超时时快速排查是进程未启动、还是就绪判定失败。 - 鉴权:Remote 模式通过
headers传递 Token(如Authorization: Bearer ${MY_MCP_TOKEN});Local 模式可在env中注入密钥,注意不要将密钥写入仓库——仓库采用环境变量占位符(如${REPO_ROOT}、${API_KEY})的惯例可参考 yaml_instance/demo_mcp.yaml 与 utils/env_loader.py。 - 多会话:MCP 服务若不支持多客户端,可在模型或工具层设置
max_concurrency=1,并在 YAML 中复用同一配置——结合上文提到的 stdio 客户端按launch_key复用机制,多个节点共享同一个本地进程可避免重复拉起。
7. 调试步骤
- 端点自检:Remote 模式用 curl 或
fastmcp client测试 HTTP 端点;Local 模式先单独运行可执行文件,确认 stdout 中出现与wait_for_log匹配的文本。 - 启动观察:启动 DevAll(可加
--reload),观察后端日志是否打印工具清单——工具发现失败通常发生在这一阶段,错误信息会直接指向握手失败原因。 - 调用追踪:若调用失败,查看 Web UI 中的工具请求/响应,或在
logs/中搜索对应 session 的结构化日志,核对是传输层错误、鉴权失败还是工具参数 schema 不匹配。
8. 小结
MCP Tooling 是 DevAll 连接外部工具生态的桥梁:mcp_remote面向已部署的 HTTP(S) 服务,配置简洁、共享方便;mcp_local面向本地进程,通过 stdio 常驻连接实现与 Blender 等桌面工具的深度集成。理解McpRemoteConfig/McpLocalConfig的字段语义、工具清单缓存与结果归一化机制,是写出可上线、可排障的 Agent 工作流的前提。相关配置模型的完整定义可进一步查阅 entity/configs/node/tooling.py 与 runtime/node/agent/tool/tool_manager.py,实战示例则见 yaml_instance/demo_mcp.yaml 与 yaml_instance/blender_3d_builder_simple.yaml。
【免费下载链接】ChatDevChatDev 2.0: Dev All through LLM-powered Multi-Agent Collaboration项目地址: https://gitcode.com/GitHub_Trending/ch/ChatDev
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考