不改主循环,挂上外部工具:react-agent-mini 怎么接 MCP
先读概念篇(推荐):MCP 到底是什么?六大能力,每个讲清「怎么用」
概念篇把 MCP 六大能力讲完了。本篇只谈一件事:
react-agent-mini 如何在不改
query()的前提下,把外部 stdio MCP Tools 接进 ReAct 循环。
mini 当时只实现了 Tools + Stdio;Resources / Prompts 的接线见续篇:概念里的 Resources / Prompts,终于进 REPL 了。概念演示 Server 仍在 examples/mcp-tour-server。
mini 接的是哪一块?(Tools 接线篇视角)
概念篇里的能力 本篇(Tools) 续篇(capabilities)
Tools ✅ ✅
Resources (未接) ✅ List/Read 工具 + slash 挂载
Prompts (未接) ✅ REPL slash
Sampling / Roots / Elicitation ❌ ❌
传输 ✅ 仅 stdio ✅ 仅 stdio
策略:先打通「动态工具表」这条主干——和内置 Echo / Read 走同一条 runToolUse。
一张图看清链路
.mcp.json(配置:连哪些 server,用什么命令启动)
↓
loadMcpTools() → stdio 启动子进程 → list_tools
↓
adaptMcpTool():每个 MCP tool → 内部 Tool 形状
↓
sessionTools() = builtin(Echo/Read/…)+ mcp__* 工具
↓
模型 tool_use → runToolUse → 门卫(第六篇)→ 转发 call_tool
↓
tool_result 回注 → query() 继续
对 query() 来说,MCP 工具和普通 Read 没区别。
变化只在 启动接线 和 适配层。
动手:5 分钟接上计算器
仓库示例:examples/mcp-calc-server(只暴露 add / multiply)。
Step 0:先确认 Server 能跑
node examples/mcp-calc-server/smoke.mjs
期望:
tools: add, multiply
add(17,25): {"content":[{"type":"text","text":"17 + 25 = 42"}]}
Step 1:写配置
cp .mcp.json.example .mcp.json
{
"mcpServers": {
"calc": {
"command": "node",
"args": ["examples/mcp-calc-server/server.js"]
}
}
}
| 字段 | 含义 |
|---|---|
calc |
server id → 工具名前缀 |
command + args |
如何拉起 stdio Server |
| 无配置文件 | 跳过 MCP,零开销 |
Step 2:启动 Agent
bun run dev
REPL 里问:
用计算器工具算一下 17 加 25,必须调用 MCP 工具,不要心算
Step 3:对照链路
启动 → list_tools → 注册 mcp__calc__add / mcp__calc__multiply
模型 → tool_use mcp__calc__add { a: 17, b: 25 }
门卫 → Allow? (y/N) → y
转发 → call_tool → "17 + 25 = 42" → tool_result → 自然语言回答
headless:
ALLOW_WRITE=1 bun run dev -- "用 mcp__calc__add 计算 17+25"
Step 4:自己加 subtract
在 server.js 的 list / call 各加一项,重启 Agent(不热重载),再问「100 减 37」。
实现要点
工具名:mcp__<server>__<tool>(会 normalize)
避免和内置 Read / Write 冲突;公开名还会做 normalizeNameForMCP(. 与空格 → _)。这样工具名更像普通标识符,模型更容易稳定引用:
server "my.calc" + tool "do add" → mcp__my_calc__do_add
普通情况:mcp__calc__add
模型看到的是规范化后的全名;适配层映射回 Server 原始 tool 名再 call_tool。
适配器:伪装成内部 Tool
adaptMcpTool 做四件事:起名、描述、宽松 Zod + 出站 JSON Schema、call() 转发 MCP。
adaptMcpTool('calc', { name: 'add', description: '...' }, callTool)
// → { name: 'mcp__calc__add', call: () => 转发 MCP, ... }
之后与 Echo 同一条流水线:查找 → 校验 → 门卫 → call → tool_result。
权限:复用第六篇门卫
MCP 默认 isReadOnly: false:
| 模式 | 行为 |
|---|---|
| REPL | y/N |
| headless | 默认拒绝,除非 ALLOW_WRITE=1 |
readOnlyHint: true 时映射为只读、自动放行。
启动接线
const [{ systemPrompt, skills }, mcp] = await Promise.all([
loadSessionContext(),
loadMcpTools(),
])
const tools = sessionTools(mcp.tools)
// …
finally { await closeMcp?.() }
连接有超时(list 15s、call 30s);单个 server 失败则 stderr 警告并跳过。
和主循环的关系
L1 CLI → .mcp.json、合并 tools、门卫
L2 query() → 不变
L3 runToolUse → 不变
L4 适配 → services/mcp/
MCP 不改变 ReAct 循环,只改变「工具表从哪来」。
刻意没做什么?
| 没做 | 说明 |
|---|---|
| Resources / Prompts / … | 当时未接;现见续篇 + examples/mcp-tour-server |
| Streamable HTTP / OAuth | 仅本地 stdio |
| 热重载 | 改配置需重启 |
完整版 Claude Code / Cursor 会做全能力;mini 验证的是:
配置 → 连接 → list_tools → 适配 Tool → 合并 → 复用 runToolUse
系列拼图
| 篇 | 能力 |
|---|---|
| 1–6 | 循环 → 代码库 → REPL → 上下文 → Skills → 门卫 |
| 7 | MCP 概念 |
| 8 | 本篇:Tools 接线 |
| 8b | Resources / Prompts 接线 |
| 8 | 本篇:mini 接线实现 |
带走什么?
- mini 只接 Tools + Stdio,够理解「外挂设备」怎么进循环。
- 适配器 + 门卫复用 → 下游零感知。
- 想弄清 Resources / Prompts 怎么进 REPL → 续篇,跑
mcp-tour-server。
仓库与链接
- GitHub:react-agent-mini
- 概念篇:MCP 能力讲解
- 续篇:Resources / Prompts 接线
- 计算器(接 Agent):examples/mcp-calc-server
- 概念 tour:examples/mcp-tour-server
- 配置模板:
.mcp.json.example - MCP 规范
欢迎 Star、Issue 和 PR。
本文基于 react-agent-mini 变更 v3-mcp;工具名 normalize 已按 v4-claude-align 校正。Resources/Prompts 见续篇。
更多推荐


所有评论(0)