系列里这一篇只讲协议本身,不讲 react-agent-mini 怎么接线。
下一篇:给最简 Agent 接上 MCP(Tools 接线) · Resources / Prompts 接线


0. 最基础:全称、角色、怎么传

MCP 全称是什么

MCP = Model Context Protocol(模型上下文协议)。

一句话:给 AI 应用(Claude、Cursor、你自己写的 Agent)接外部系统的开放标准——像 USB-C:设备各异,插头统一。
Server 暴露数据 / 工具 / 工作流;Host 按同一套约定去发现、调用,不必为每个插件写一套私有协议。

六大能力可以先记成:

Tools 做事 · Resources 给材料 · Prompts 给开场白;另外三个是 Server↔Host 反向协作(Sampling / Roots / Elicitation)。

先记住一句分界(也是最容易混的地方):

斜杠 / ≈ MCP PromptTools 不进斜杠(对话里由模型发起调用);Resources 是材料(Host/@/Agent 读进上下文)。

三角色(别和「模型」搅在一起)

规范里是三个角色;日常口语里的「Agent」通常 = Host(或其里的 Client):

角色 是谁 干什么
Host Cursor、Claude Desktop、react-agent-mini… 跑对话、调模型、弹确认框、把 MCP 结果塞回上下文
Client Host 内部的 MCP 连接器 跟某个 Server 维持会话、发 JSON-RPC
Server 你写的 mcp-tour-server 等进程/服务 暴露 Tools / Resources / Prompts…
你(终端用户)                Host(Agent 应用)
在 Cursor 点按钮 / 打字          调模型 + 调 MCP SDK
       │                              │
       │         JSON-RPC 2.0         │
       │    (stdio 或 Streamable HTTP)│
       └──────────► MCP Server ◄──────┘
                         ▲
                         │  不直接拿你的 API Key 调大模型
                         │  (除非走 Sampling,且须你批准)

要点:模型和 MCP Server 不直接对话。
模型只跟 Host 说话(普通 LLM API);Host 若看到 tool_use,再经 MCP 去 callTool,把结果写成 tool_result 回灌给模型。

和 Agent 之间用什么协议传

  • 报文格式:JSON-RPC 2.0(有 method / params / id 的请求与响应;也有 notification)。
  • 管道(transport)——报文不变,换承载方式:
    • stdio:Host 拉起子进程,stdin/stdout 传一行行(或带 Content-Length 帧)的 JSON;本地 Demo、Cursor 配 command+args 就是这种。
    • Streamable HTTP(及早期 SSE 等):远程 Server;常配合 OAuth。

启动后先 initialize 做能力协商,再 tools/listresources/readprompts/gettools/call 等。
示意(逻辑形状,不是完整线上帧):

{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"add","arguments":{"a":17,"b":25}}}
{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"17 + 25 = 42"}]}}

stdio 时:stdout 只能走协议;日志必须打 stderr(本 Demo 的 [mcp-tour] ready 就是如此),否则 Host 解析会坏掉。

手写 JSON-RPC 帧也可以,但本仓库 Demo 用官方 SDK 对接

import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'

smoke.mjs / how-to-host.mjs 都是:Client + StdioClientTransportconnectlistTools / readResource / getPrompt / callTool。依赖见仓库根 package.json@modelcontextprotocol/sdk

传输细节与配置文件见 §8。

和 Function Calling、OpenAPI 差在哪

三者常被混谈,其实处在不同层:

Function Calling(工具调用) OpenAPI(HTTP API 描述) MCP
是什么 LLM API 的一种能力:模型输出「要调哪个函数、参数是啥」 描述 REST 接口的文档/契约(路径、schema) Host↔外部能力的会话协议(发现 + 调用 + 材料 + 开场…)
对谁说话 模型 ↔ Host(在 chat completions 里) 调用方 ↔ HTTP 服务 Host(Client)↔ MCP Server
报文 各家模型自己的 tool_calls / tool_use HTTP + JSON(按 path/method) JSON-RPC 2.0(再经 stdio/HTTP 承载)
典型内容 函数名 + JSON 参数 GET /users/{id} Tools / Resources / Prompts / Sampling…
谁执行 Host 本地执行或再去调 HTTP 你自己写客户端发请求 Server 进程/服务执行,结果经 MCP 回 Host

关系可以记成一条链:

用户说话
  → Host 调 LLM(这里用到 Function Calling:模型说「请 call add」)
  → Host 把这次 call 转成 MCP tools/call(或调你手写的本地函数 / OpenAPI)
  → 结果变 tool_result,再喂回 LLM

所以:

  • Function Calling ≠ MCP:前者是「模型和 Host 之间怎么表达 tool_use」;后者是「Host 和外部 Server 之间怎么发现/调用能力」。Host 常常两边都用:对内 FC,对外 MCP。
  • OpenAPI ≠ MCP:OpenAPI 描述「这个 HTTP 服务有哪些接口」;MCP 还可以带 Resources、Prompts、Sampling 等,且本地 stdio Server 根本不必是 HTTP。有人会把 OpenAPI 包一层做成 MCP Tools,那是适配,不是同一件事。
  • react-agent-mini(见实现篇)正是:模型侧照旧 tool_use,适配层再 callTool 转到 MCP。

仓库可跑 Demo(Client 侧均经 @modelcontextprotocol/sdk):

文件 作用
examples/mcp-tour-server/server.js Server:暴露 Tools + Resources + Prompts
node examples/mcp-tour-server/smoke.mjs Client:分别打 list/call/read/get
node examples/mcp-tour-server/how-to-host.mjs Client:模拟 Host 真实用法(挂材料 → 取开场 → 调工具)
node examples/mcp-tour-server/how-to-host.mjs

仅前三项有可跑代码;Sampling / Roots / Elicitation 只讲 Host 怎么接。)

下文怎么读

每个能力都按这个结构写:

  1. 用户怎么用(协议期望的产品交互;Cursor / Claude 的 UI 完整度因版本而异)
  2. Host 怎么用(调哪个 API、结果往哪塞)
  3. 什么时候该用它(别和别的搞混)

关于 Cursor: 「用户怎么用」写的是理想 Host 形态。Cursor 对 Prompt / Resource 的面板、参数表单、@ 引用等不保证和 Claude Desktop 一样全;Tool 则一般能在对话里被 Agent 调用。配置路径见 §8。

本文重点仍是 怎么用:写 Host 或配置 Cursor 时,每一步该调什么、用户侧长什么样。


1. Tools — 做事

用户怎么用

  1. 在 Cursor / Claude Desktop 里配好 MCP Server(见 §8;设置页也可)。
  2. 正常聊天:「帮我算 17+25」「给这个 issue 贴个标签」。
  3. 模型提出 tool call → UI 弹出确认 → 你点允许 → 看到结果。

不用手动 call;Host 和模型替你完成。

不会出现在 / 斜杠菜单里。 斜杠是 Prompt 的事;Tool 是模型在回合里发起的调用。

Host 怎么用

// 启动时发现
const { tools } = await client.listTools()
// 把 tools 转成模型能看的 tool schema,塞进本次 API 请求

// 模型返回 tool_use 之后
const result = await client.callTool({
  name: 'add',
  arguments: { a: 17, b: 25 },
})
// result.content → 写成 tool_result,追加进 messages,再调模型

对应 Demo:smoke.mjs 第 1 段;完整 Agent 接线见实现篇

什么时候用

执行动作(算、写、发、订、查并可能改状态)→ Tools。
需要用户批准的,一般也是 Tools。


2. Resources — 给材料

用户怎么用

协议期望的产品交互(Claude Desktop / 部分 Host 的资源面板较完整):

1. 打开 MCP 资源面板 / 用 @ 引用
2. 勾选「差旅手册」(docs://handbook)
3. 再提问:「按公司政策,巴黎 3 天差旅怎么订?」

要点:材料语义是「取参考资料」,不是 invent 一个 getHandbook 工具去「做事」。
有的 Host(含 Cursor Agent)也会在对话中主动 readResource,语义仍是 Resource,只是 UI 未必先让你勾选。

Host 怎么用

// 1) 展示列表给用户勾选
const { resources } = await client.listResources()
// → [{ uri: 'docs://handbook', name: '差旅手册', mimeType: 'text/markdown' }]

// 2) 用户勾选后拉取正文
const { contents } = await client.readResource({ uri: 'docs://handbook' })
const text = contents[0].text

// 3) 塞进本轮上下文(system 附加段 / 独立 user 消息都行)
messages.unshift({
  role: 'user', // 或拼进 system
  content: `【MCP 材料 ${uri}】\n${text}`,
})
// 然后再把用户真正的问题发出去

how-to-host.mjs 里的步骤 ① 就是这件事。

什么时候用

场景 用 Resource 用 Tool
把手册/README/issue 正文当参考 勉强可以,但 UI 难做「附件」
按关键字搜索数据库并可能写入
用户要反复挂同一份资料

写法提示(做 Server):静态/半静态、只读、值得出现在资源列表里的 → Resource。
「查一下再干一件事」→ Tool。


3. Prompts — 给开场白

用户怎么用

这才是斜杠 / 的来源(协议期望:像斜杠命令):

1. 输入 / 或打开 Prompt 菜单
2. 选 plan_trip(Cursor 里常显示为 /<server>/plan_trip)
3. (理想 UI)填参数:city=巴黎,days=3
4. Host 把 getPrompt 返回的 messages 当作本轮用户消息发出

你不用自己写「请帮我规划差旅,要求先读手册……」那段话——Server 模板已经写好。

现实差异: 有的 Host(含部分 Cursor 版本)点斜杠后不弹参数表单,直接按缺省参数注入;本 Demo 的 Server 缺参时会 fallback 成「未指定城市 / ?」。写 Host 时仍应尽量先收集参数再 getPrompt

Host 怎么用

// 1) 菜单里列出
const { prompts } = await client.listPrompts()
// → [{ name: 'plan_trip', arguments: [{ name: 'city', required: true }, …] }]

// 2) 用户填参后取消息
const { messages } = await client.getPrompt({
  name: 'plan_trip',
  arguments: { city: '巴黎', days: '3' },
})

// 3) 直接把 messages 并入对话(通常是 user 角色)
conversation.push(...messages)
// 再 callModel(conversation)

how-to-host.mjs 步骤 ②。

什么时候用

场景 用 Prompt 用本地 systemPrompt / Skill
跨 Host 复用同一套「一键开场」 ✅ MCP Prompt
仅本仓库约定 Skill / AGENTS.md
全局人设 Host 自己的 system

4. 串起来:一次「差旅规划」Host 伪流程

这就是 how-to-host.mjs 在做的事:

用户勾选 Resource「差旅手册」
  → readResource → 正文进上下文          【材料】

用户点 Prompt plan_trip(巴黎, 3)
  → getPrompt → 开场 user 消息发出         【开场白 · 进斜杠】

模型回答时决定算预算 → tool_use add
  → 你确认后 callTool → tool_result 回注   【做事 · 不进斜杠】

终端跑一遍:

node examples/mcp-tour-server/how-to-host.mjs

你会看到三块输出对应上面三步。
这就是 Resources / Prompts「怎么用」相对 Tools 的差别:前两个多半是 Host/UI 先调,Tools 多半是 模型回合里再调


5. Sampling — Server 借你的模型(Host 侧怎么接 · 无 Demo)

用户怎么用

你通常看不到「我在用 Sampling」这个词,只会看到:

「MCP 服务器想使用你的模型完成一次推理,是否允许?」
[允许] [拒绝](有的 Host 还可预览将发送的 prompt)

点允许后,Host 用你已登录的模型跑完,把结果还回 Server。

Host 怎么用

你必须在能力协商时声明支持 sampling,并实现回调:

// 伪代码:Server 发来 sampling/createMessage 时
async function onSamplingRequest(params) {
  // 1. 弹 UI 给用户看 params.messages,征求同意
  if (!(await askUser('允许该 MCP 借用模型?', params))) {
    throw new Error('user rejected sampling')
  }
  // 2. 用 Host 自己的 API Key 调模型(不要把 key 给 Server)
  const output = await yourLLM.complete(params.messages, {
    maxTokens: params.maxTokens,
  })
  // 3. 只返回这次结果;不要附带完整会话历史
  return { role: 'assistant', content: output, model: '…', stopReason: 'endTurn' }
}

什么时候用(做 Server 的人)

Server 内部需要「智慧判断」,但不想嵌入 API Key、不想成为模型代理商 → 发 Sampling。
终端用户 / 普通 Host 开发者:实现上面的批准 + 转发即可。


6. Roots — 划定目录(Host 侧怎么接 · 无 Demo)

用户怎么用

在支持 filesystem MCP 的产品里:打开的工作区 / 你授权的文件夹 = roots。
换项目、加文件夹,roots 会更新;Server 不应读到项目外的 ~/Documents

Host 怎么用

// 启动或工作区变化时,告诉 Server(或响应 roots/list)
const roots = [
  { uri: 'file:///D:/Learn/Github/react-agent-mini', name: 'project' },
]
// 通过 roots 能力 / notifications/roots/list_changed 同步

// 可选:本地再拦一道,不信任 Server 自觉
function assertUnderRoots(path) { /* … */ }

什么时候用

Server 会读本地文件时(官方 filesystem MCP 等)→ Host 必须给 roots。
纯远端 API 的 Server(只 Tools 调 HTTP)→ 往往用不到。


7. Elicitation — 中途追问(Host 侧怎么接 · 无 Demo)

用户怎么用

工具跑到一半弹出表单:「请选择餐型:buffet / plated」→ 你填完继续,不用重说整句需求。

Host 怎么用

// Server 在 tools/call 期间发来 elicitation/create
async function onElicitation(params) {
  // params.message + params.requestedSchema → 渲染表单
  const values = await showForm(params.message, params.requestedSchema)
  // 用户取消则返回 cancel;否则把 values 交回
  return { action: 'accept', content: values }
}

和「门卫 y/N」的区别:门卫管许不许做;Elicitation 管还缺哪个字段

什么时候用(做 Server)

参数很难一次齐、需要结构化补全 → Elicitation。
参数简单、缺了直接 isError 也行的小工具 → 不必上。


8. 传输:你实际怎么连上 Server

§0 说过:报文是 JSON-RPC 2.0,管道是 stdioStreamable HTTP。本节是落地配置。

本地脚本(本 Demo)——与 how-to-host.mjs 相同,用官方 SDK:

import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'

const transport = new StdioClientTransport({
  command: 'node',
  args: ['examples/mcp-tour-server/server.js'],
  cwd: process.cwd(),
})
const client = new Client({ name: 'how-to-host', version: '0.0.0' })
await client.connect(transport)

配置文件因 Host 而异,语义相同(stdio:command + args):

Host 常见配置位置
react-agent-mini 仓库根 .mcp.json(见实现篇
Claude Desktop 其应用配置里的 mcpServers
Cursor 用户级 ~/.cursor/mcp.json,或项目级 .cursor/mcp.json

仓库根跑的 Host,相对路径即可:

{
  "mcpServers": {
    "tour": {
      "command": "node",
      "args": ["examples/mcp-tour-server/server.js"]
    }
  }
}

Cursor 若一直 Loading / 找不到 node,改用绝对路径,并显式设 cwd(nvm 用户尤其如此):

{
  "mcpServers": {
    "mcp-tour": {
      "type": "stdio",
      "command": "C:\\path\\to\\node.exe",
      "args": ["D:\\path\\to\\react-agent-mini\\examples\\mcp-tour-server\\server.js"],
      "cwd": "D:\\path\\to\\react-agent-mini"
    }
  }
}

远程服务则用 Streamable HTTP +(常)OAuth;语义不变,换的是管道。


9. 一张「我该调哪个」速查

我想… Host 调 / 斜杠?
让模型能执行外部动作 Tools listTools + callTool
让用户先挂一份只读资料 Resources listResources + readResource → 塞进上下文
让用户一键套用标准开场 Prompts listPrompts + getPrompt → 当 messages
让 Server 借用我的模型 Sampling 实现 sampling/createMessage 处理
限制 Server 可碰的目录 Roots 提供 / 更新 roots
执行中补字段 Elicitation 实现表单回调

自己验证前三项:

node examples/mcp-tour-server/smoke.mjs      # 分别看三个 API
node examples/mcp-tour-server/how-to-host.mjs # 看 Host 怎么串

server.js 加一个 Resource 或 Prompt,再跑一遍,比只看文档记得牢。


下一篇

协议侧「怎么用」清楚了 → 实现篇:react-agent-mini 只接 Tools + Stdio,适配器、门卫、query() 为何不用改。


参考

Logo

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

更多推荐