MCP 到底是什么?六大能力,每个讲清「怎么用」
系列里这一篇只讲协议本身,不讲 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 Prompt;Tools 不进斜杠(对话里由模型发起调用);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。
- stdio:Host 拉起子进程,stdin/stdout 传一行行(或带 Content-Length 帧)的 JSON;本地 Demo、Cursor 配
启动后先 initialize 做能力协商,再 tools/list、resources/read、prompts/get、tools/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 + StdioClientTransport → connect → listTools / 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 怎么接。)
下文怎么读
每个能力都按这个结构写:
- 用户怎么用(协议期望的产品交互;Cursor / Claude 的 UI 完整度因版本而异)
- Host 怎么用(调哪个 API、结果往哪塞)
- 什么时候该用它(别和别的搞混)
关于 Cursor: 「用户怎么用」写的是理想 Host 形态。Cursor 对 Prompt / Resource 的面板、参数表单、
@引用等不保证和 Claude Desktop 一样全;Tool 则一般能在对话里被 Agent 调用。配置路径见 §8。
本文重点仍是 怎么用:写 Host 或配置 Cursor 时,每一步该调什么、用户侧长什么样。
1. Tools — 做事
用户怎么用
- 在 Cursor / Claude Desktop 里配好 MCP Server(见 §8;设置页也可)。
- 正常聊天:「帮我算 17+25」「给这个 issue 贴个标签」。
- 模型提出 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,管道是 stdio 或 Streamable 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() 为何不用改。
参考
更多推荐


所有评论(0)