先读概念篇(推荐)MCP 到底是什么?六大能力,每个讲清「怎么用」

系列回顾:主循环 · 代码库工具 · REPL · 项目上下文 · Skills · 权限 + Write

概念篇把 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 同一条流水线:查找 → 校验 → 门卫calltool_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 接线实现

带走什么?

  1. mini 只接 Tools + Stdio,够理解「外挂设备」怎么进循环。
  2. 适配器 + 门卫复用 → 下游零感知。
  3. 想弄清 Resources / Prompts 怎么进 REPL → 续篇,跑 mcp-tour-server

仓库与链接

欢迎 Star、Issue 和 PR。


本文基于 react-agent-mini 变更 v3-mcp;工具名 normalize 已按 v4-claude-align 校正。Resources/Prompts 见续篇。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐