接入 MCP
对应源码
| 扩展环节 | 源码 |
|---|---|
| MCP 配置模型 | nanobot/config/schema.py::MCPServerConfig |
| 连接、关闭和运行时控制 | nanobot/agent/loop.py::_connect_mcp、close_mcp、nanobot/agent/tools/mcp.py |
| MCP Tool/Resource/Prompt 包装 | nanobot/agent/tools/mcp.py::MCPToolWrapper、MCPResourceWrapper、MCPPromptWrapper |
| 验证与重连 | tests/agent/test_mcp_transient_retry.py、tests/tools/test_mcp_probe.py、tests/tools/test_mcp_tool.py |
| WebUI preset 接入 | nanobot/webui/mcp_presets_api.py、tests/webui/test_mcp_presets_api.py |
学习目标
读完后,你应当能够:
- 判断何时应配置 MCP,而不是自己写 Tool;
- 解释三种 transport 的连接和关闭边界;
- 预测 MCP tool/resource/prompt 的 nanobot 名称;
- 配置最小 allowlist,并排查 Windows、SSRF 和重连问题。
前置知识
- 知道 MCP server 向 client 暴露 tools、resources 和 prompts;
- 知道 stdio 是本机子进程通信,HTTP/SSE 是网络通信;
- 理解 ToolRegistry 和 AsyncExitStack 的用途。
若需要 MCP 协议的完整规范,请阅读官方 MCP 文档;本篇只解释 nanobot client 如何接入。
1. MCP 在 nanobot 中的位置
MCP 不改变 AgentRunner 的模型—工具循环。连接成功后,MCP capability 被包装成普通 Tool,注册到同一个 ToolRegistry。
flowchart LR A["MCP Server"] --> B["ClientSession"] B --> C["list_tools / resources / prompts"] C --> D["MCP*Wrapper"] D --> E["ToolRegistry"] E --> F["AgentRunner"]
因此,Runner 不需要知道某个 Tool 是 Python 内置还是 MCP。
2. 配置模型
nanobot/config/schema.py::MCPServerConfig 当前字段:
| 字段 | 用途 |
|---|---|
type | stdio、sse、streamableHttp,可省略 |
command / args / env / cwd | stdio server |
url / headers | HTTP/SSE server |
toolTimeout | 单次 capability 调用超时,默认 30 秒 |
enabledTools | 允许注册的名称;默认 ["*"] |
配置位于 tools.mcpServers.<server-name>。
自动判断规则:
- 有
command→ stdio; - 有
url且路径以/sse结尾 → SSE; - 其他 URL → streamable HTTP。
最好显式写 type,避免端点路径变化后语义漂移。
3. 名称包装
当前命名大致是:
| MCP capability | nanobot Tool 名称 |
|---|---|
tool read_file | mcp_<server>_read_file |
resource schema | mcp_<server>_resource_schema |
prompt review | mcp_<server>_prompt_review |
server 和 capability 名会被 _sanitize_name() 处理,仅保留模型 Provider 普遍接受的字母、数字、下划线和连字符,并折叠重复下划线。
enabledTools 可以写:
- 原始 MCP tool name;
- 包装后的 nanobot name;
"*";- 空列表表示一个也不注册。
未知 allowlist 项会记录可用 raw/wrapped names,便于定位拼写问题。
4. 每个 server 独立生命周期
connect_mcp_servers() 为每个 server 创建独立 AsyncExitStack。原因是 MCP/AnyIO transport 有自己的 cancel scope;多个 server 共用一个 stack 容易产生关闭冲突。
连接成功后:
- 建立 transport;
- 创建
ClientSession; initialize();- 列出 capabilities;
- 包装并注册;
- 保存 stack。
AgentLoop.close_mcp() 在拥有连接的 task 中关闭这些 stack。
一个 server 失败不会阻止其他 server 继续连接。
5. stdio 的信任边界
stdio 配置本质上授权 nanobot 启动本机命令:
{
"tools": {
"mcpServers": {
"example": {
"type": "stdio",
"command": "python",
"args": ["-m", "my_mcp_server"],
"enabledTools": ["read_data"]
}
}
}
}安全检查:
- command/package 是否可信;
env中是否传入了超出需要的 secret;cwd是否会让 server 读到不应访问的文件;- server 暴露的工具是否最小化;
- 安装脚本是否会执行任意 postinstall。
SSRF guard 不适用于 stdio,因为它不是 HTTP 请求。这不代表 stdio 更安全;它拥有本机进程权限。
Windows
Windows 上 npx、npm、pnpm、yarn、bunx 和 .cmd/.bat launcher 会被归一为:
cmd.exe /d /c <command> ...已有 exe/com 或显式 cmd/PowerShell 不再重复包装。相关函数:
_normalize_windows_stdio_command()
6. HTTP/SSE 的安全边界
连接前,nanobot 使用 validate_url_target() 阻止:
- localhost/loopback;
- 私网;
- CGNAT;
- link-local;
- metadata 地址。
每个 outgoing HTTP request 还有 event hook,再次检查 redirect target。确实需要可信内网 MCP 时,用 tools.ssrfWhitelist 放行尽可能窄的 CIDR,例如单主机 /32 或 /128。
不要为方便放行整个私网
MCP capability 通常能读取或修改数据。广泛 allowlist 会把 prompt injection 转化为内网访问能力。
7. 暂时性错误与重连
MCP wrapper 对已识别的断流、broken pipe、connection reset/refused 等暂时性连接错误重试一次。
若重试暴露出 session terminated:
- 注销该 server 的旧 Tool;
- 关闭旧 stack;
- 重连 server;
- 重新注册 capability;
- 再交回可用 wrapper。
它不是任意异常的无限重试。ValueError、普通业务错误等不会被当成瞬时 transport 故障。
8. 选择 MCP 还是原生 Tool
优先 MCP:
- 已有稳定 MCP server;
- 多个 Agent/client 需要复用;
- capability 与 nanobot 发布周期独立;
- server 自己管理复杂 SDK 和认证。
优先原生 Tool:
- 逻辑极小、只为 nanobot 服务;
- 需要直接复用 ToolContext 或 workspace policy;
- 不希望引入额外进程/网络生命周期;
- 需要与 core checkpoint/event 做紧密集成。
9. 最小验证
- 配置能通过 Pydantic;
- server 可连接并列出 capabilities;
enabledTools只注册预期名称;- 参数 schema 被规范化;
- 调用结果正确;
- timeout 生效;
- HTTP 私网和重定向被拦截;
- 断开后按预期重试/重连;
- 正常关闭没有残留进程。
源码:
nanobot/agent/tools/mcp.pynanobot/config/schema.py::MCPServerConfignanobot/agent/loop.py::_connect_mcpnanobot/agent/loop.py::close_mcp
测试:
tests/tools/test_mcp_tool.pytests/tools/test_mcp_probe.pytests/agent/test_mcp_transient_retry.pytests/webui/test_mcp_presets_api.py
官方配置参考:
自检
- MCP Tool 为什么不需要 AgentRunner 的特殊分支?
enabledTools=[]与["*"]有何差别?- stdio server 为什么不受 SSRF guard 保护?
- Windows 下
npx为什么需要cmd.exe /d /c包装?
下一篇:新增 Provider