【大模型Wiki】17 · MCP 模型上下文协议
MCP:模型上下文协议
新增篇目。MCP 是 2024 年 11 月 Anthropic 开源、2025-2026 成为行业标准的"AI 应用 USB-C 接口"。
官网:https://modelcontextprotocol.io
一、MCP 解决什么问题
MCP 之前,让模型用上外部工具/数据只有两条路,都很难规模化:
- Function Calling:每个应用 × 每个工具都要写胶水代码(M×N 问题)
- 各家私有插件:ChatGPT Plugins、Claude Tools 互不兼容
MCP(Model Context Protocol) 是一个开放协议:工具/数据的提供方实现一次 MCP Server,任何支持 MCP 的 MCP Host(Claude Desktop、Cursor、Cline、各 IDE 与 Agent 框架)都能直接用——把 M×N 变成 M+N。

┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Claude Desktop │ │ Cursor │ │ 你的 Agent │ MCP Hosts
└──────┬────────┘ └──────┬────────┘ └──────┬────────┘
│ MCP Client(随 Host 内置) │
└────────────┬───────┴───────────────────┘
▼ JSON-RPC 2.0(stdio / Streamable HTTP)
┌────────────────────────────────┐
│ MCP Servers │
│ filesystem │ github │ postgres│
│ browser │ 搜索 │ 自研系统 │
└────────────────────────────────┘
1.1 为什么是"USB-C"比喻
USB-C 之前,每个设备有自己接口(充电、投屏、传输各一根线)。USB-C 统一了物理与协议,一根线通吃。MCP 同理:在它之前,每个 AI 应用想接数据库、文件系统、API,都要各自写一套适配;MCP 统一了"AI 应用 ↔ 工具/数据"的接口,一次实现、处处可用。
1.2 三角色模型(Host / Client / Server)
MCP 的架构是清晰的三层,理解这三个角色是理解整个协议的前提:
| 角色 | 是什么 | 职责 |
|---|---|---|
| MCP Host | 运行 AI 应用的宿主(Claude Desktop、Cursor、VS Code、你的 Agent 程序) | 承载 LLM、承载用户交互、内置一个或多个 MCP Client、决定何时调哪个工具 |
| MCP Client | 随 Host 内置的协议客户端(每个 Server 对应一个 Client 实例) | 与某个 Server 建立 1:1 连接、收发 JSON-RPC、做能力协商 |
| MCP Server | 提供能力的服务进程(filesystem、github、你自研的 xxx) | 实现 tools/resources/prompts 等原语,真实对接后端系统 |
关键点:一个 Host 可连多个 Server,但 Client 与 Server 是 1:1 一对一。Host 内部为每个 Server 起一个 Client,Client 之间互不干扰。
Host (Claude Desktop)
├── Client A ──► Server A (filesystem) 1:1
├── Client B ──► Server B (github) 1:1
└── Client C ──► Server C (postgres) 1:1
二、协议基础:JSON-RPC 2.0
MCP 的传输内容全部是 JSON-RPC 2.0 消息。它是一种极简的远程调用格式,每条消息是一个 JSON 对象。三种基本消息类型:
2.1 请求(Request)
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": { "city": "北京" }
}
}
jsonrpc: 固定 "2.0";id: 请求编号,用于匹配响应(通知没有 id);method: 方法名,MCP 用/分隔命名空间(如tools/list、resources/read);params: 参数对象(可省略)。
2.2 响应(Response)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "北京:晴,26°C" }]
}
}
成功时带 result,失败时带 error(不含 result):
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32602, "message": "参数 city 缺失" }
}
2.3 通知(Notification)
没有 id 的消息,单向、不需要回复。常用于进度通知、日志:
{ "jsonrpc": "2.0", "method": "notifications/message", "params": { "level": "info", "data": "开始查询" } }
2.4 MCP 的方法命名空间
| 命名空间 | 用途 |
|---|---|
initialize / ping / shutdown |
生命周期 |
tools/list tools/call |
列出/调用工具 |
resources/list resources/read resources/templates/list |
列出/读取资源 |
prompts/list prompts/get |
列出/获取提示模板 |
sampling/* |
Server 反向请求 LLM(客户端侧原语) |
logging/* notifications/* |
日志与通知 |
三、三大核心原语
MCP Server 可以向 Host 暴露三类能力:
| 原语 | 说明 | 例子 |
|---|---|---|
| Tools(工具) | 模型主动调用的函数,用于产生副作用或取数 | 查询数据库、发邮件、执行 SQL |
| Resources(资源) | 应用控制的只读上下文,用 URI 标识 | file:///logs/app.log、db://users/schema |
| Prompts(提示模板) | 用户主动选择的预置提示 | /review-pr、/summarize-table |
另有两个客户端侧原语:Sampling(Server 反过来请求 Host 的 LLM 生成)与 Roots(限定文件系统访问范围)。
3.1 Tools(工具)——模型主动调
- 由模型决策调用:Host 把 tool 的 schema 暴露给 LLM,模型认为需要时调用;
- 有副作用或取数:执行 SQL、调 API、写文件都算 tool;
- 结构:
name+description+inputSchema(JSON Schema)+ 返回content(文本/图片/资源等)。
3.2 Resources(资源)——应用控制的只读上下文
- 由应用/HOST 决定何时注入:不靠模型"调",而是 Host 把资源内容作为上下文喂给模型(例如用户打开一个文件,Host 自动把
file://资源读进来); - URI 标识:
file:///logs/app.log、db://users/schema、screen://1; - 只读:资源不应产生副作用,是"给模型看的资料";
- 支持模板(template):如
file:///{path}表示一类资源,由参数实例化。
Tools 与 Resources 的区别一句话:Tool 是"让模型动手",Resource 是"给模型资料"。
3.3 Prompts(提示模板)——用户主动选择
- 由用户触发:用户在 UI 里点一个
/review-pr按钮,Host 套用模板生成一段提示词给模型; - 是预置的工作流提示,不是模型自动决策;
- 可带参数(如 PR 编号),返回一段构造好的消息序列。
3.4 客户端侧原语:Sampling 与 Roots
- Sampling(采样):让 Server 反过来请求 Host 的 LLM 生成文本。例如一个"翻译 Server"自身不装模型,调用 Host 的 LLM 完成翻译。需要 Host 授权,体现"Host 掌握模型"的安全边界。
- Roots(根):客户端告诉 Server"你只允许访问这些文件系统根目录"(如
/home/user/project),Server 在限定范围内工作,是安全隔离机制。
四、传输层:stdio 与 Streamable HTTP
4.1 stdio(本地子进程)
- Server 作为 Host 启动的子进程 运行,通过 标准输入 stdin / 标准输出 stdout 收发 JSON-RPC 行;
- 适合本地、同机工具(文件系统、本地数据库、CLI 封装);
- 配置简单:Host 只需知道"用什么命令启动这个 Server";
- 无网络、无鉴权负担,进程随 Host 生命周期管理。
{ "mcpServers": { "demo": { "command": "python", "args": ["server.py"] } } }
4.2 Streamable HTTP(远程传输)
协议迭代说明:早期规范用 SSE(Server-Sent Events) 做服务端推送、用 HTTP POST 发请求(即 "SSE + HTTP" 双通道)。后续规范演进为 Streamable HTTP,把请求与流式响应统一到一个可断连重连的 HTTP 端点,更易穿透代理/负载均衡、更易扩展。本文按"stdio 与 HTTP/SSE 流式传输"两条主线讲解,具体端点形态以官方最新规范为准。
- Server 跑在远程,通过 HTTP 通信;
- 服务端→客户端流式推送用 SSE(或 Streamable HTTP 的流式响应),用于长任务进度、分段返回;
- 适合远程、需鉴权、可水平扩展的 Server(SaaS 工具、团队共享服务);
- 需处理鉴权(API Key / OAuth)、会话管理(session id)、断线重连。
| 维度 | stdio | HTTP / SSE(Streamable HTTP) |
|---|---|---|
| 部署位置 | 本地子进程 | 远程服务 |
| --- | --- | --- |
| 鉴权 | 不需要(同机) | 需要(API Key/OAuth) |
| 流式推送 | 逐行 stdout | SSE / 流式 HTTP 响应 |
| 可扩展性 | 单用户本地 | 可多用户、可负载均衡 |
| 典型场景 | 文件系统、本地 CLI | 远程 API、团队共享服务 |
4.3 capability 协商(能力协商)
连接建立后,双方通过 initialize 握手声明各自支持的能力,避免"调了对方不支持的方法"。
交互流程:
// 1) Client → Server:initialize 请求,声明自己支持什么
{
"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {
"protocolVersion": "2024-11-05", // 协议版本
"capabilities": { "tools": {}, "resources": {}, "prompts": {} }, // Client 侧能力
"clientInfo": { "name": "my-host", "version": "1.0" }
}
}
// 2) Server → Client:响应,声明自己能提供什么
{
"jsonrpc": "2.0", "id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": { "tools": {}, "resources": {}, "logging": {} }, // Server 支持 tools/logging,无 prompts
"serverInfo": { "name": "demo-server", "version": "0.1" }
}
}
// 3) Client → Server:initialized 通知,握手完成
{ "jsonrpc": "2.0", "method": "notifications/initialized" }
协商的意义:Server 若未声明 prompts,Host 就不会向模型展示 prompt 功能;版本不匹配时双方可降级到共同支持的协议版本。能力协商让生态向后兼容、可渐进演进。
五、写一个 MCP Server(Python)
官方 SDK:pip install "mcp[cli]"(Python)、@modelcontextprotocol/sdk(TS)。用 FastMCP 十几行就能发布一个工具:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("demo-server")
@mcp.tool()
def get_weather(city: str) -> str:
"""查询指定城市的当前天气"""
# 真实场景这里调天气 API
return f"{city}:晴,26°C"
@mcp.resource("file://notes/{name}")
def read_note(name: str) -> str:
"""暴露只读资源"""
return open(f"./notes/{name}.md", encoding="utf-8").read()
if __name__ == "__main__":
mcp.run() # 默认 stdio 传输
在 Claude Desktop / Cursor 的 MCP 配置里注册后重启 Host,模型即可自动发现并调用 get_weather——无需写任何 Host 侧代码。
{
"mcpServers": {
"demo": {
"command": "python",
"args": ["server.py"]
}
}
}
5.1 完整 Server 示例(带 tools / resources / prompts + 异步)
下面给一个更接近生产的完整例子,覆盖三类原语,并演示异步、结构化返回、错误处理。
import asyncio
import json
from mcp.server.fastmcp import FastMCP
# 1) 创建 Server 实例,命名会展示在 Host 里
mcp = FastMCP("knowledge-base")
# 2) 一个内存里的"数据库",模拟真实后端
NOTES = {
"todo": "# 待办\n- 写 MCP 文档\n- 修复天气工具",
"meeting": "# 会议纪要\n- 确定采用 MCP 接入内部系统",
}
# ── Tools:模型主动调用,可能产生副作用 ─────────────────────
@mcp.tool()
def search_notes(keyword: str) -> str:
"""按关键词检索笔记,返回匹配的笔记标题列表。"""
hits = [title for title, body in NOTES.items() if keyword in body]
return f"命中: {hits}" if hits else "无匹配"
@mcp.tool()
def add_note(title: str, content: str) -> str:
"""新增一条笔记。title 为标题,content 为正文。"""
if not title:
return "错误:title 不能为空" # 简单校验,错误信息会回传给模型
NOTES[title] = content
return f"已保存笔记:{title}"
# ── Resources:只读上下文,由 Host 决定何时注入 ──────────────
@mcp.resource("file://notes/{name}")
def read_note(name: str) -> str:
"""按名称读取一条笔记内容(只读)。"""
return NOTES.get(name, f"笔记不存在:{name}")
# ── Prompts:用户主动触发的预置提示模板 ─────────────────────
@mcp.prompt()
def summarize_note(name: str) -> str:
"""生成"总结某条笔记"的提示词,用户在 UI 选择后触发。"""
body = NOTES.get(name, "")
return f"请用不超过 3 句话总结下面这条笔记:\n\n{body}"
# 3) 入口:默认 stdio;可传 transport="sse" 走 HTTP/SSE
if __name__ == "__main__":
mcp.run(transport="stdio")
要点讲解:
@mcp.tool()装饰的函数,函数签名 + docstring 自动变成 tool schema(名称来自函数名,参数来自签名,描述来自 docstring),Host 拿去给模型看;- 返回值是字符串(或结构化
content),会被包成result.content回传; - Resource 用 URI 模板
file://notes/{name},{name}由调用方填; @mcp.prompt()返回构造好的提示文本;mcp.run(transport="stdio")启动本地子进程模式。
5.2 跑一个远程 Server(HTTP / SSE)
若要让团队共享一个 Server,用 Streamable HTTP(或早期 SSE)模式启动:
# server_http.py —— 通过 http 暴露(具体端点形态参考官方最新规范)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("remote-kb")
@mcp.tool()
def search_notes(keyword: str) -> str:
return f"远程命中: {keyword}"
if __name__ == "__main__":
mcp.run(transport="streamable-http") # 或早期 transport="sse"
Host 端配置改为指向 URL 并带鉴权:
{
"mcpServers": {
"remote-kb": {
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer <API_KEY>" }
}
}
}
5.3 调试与踩坑
- Server 启动就崩:stdio 模式下 Host 靠子进程 stdout 通信,绝不能在 Server 里往 stdout 打印调试信息(
print会污染协议流),日志必须走 stderr 或文件。 - 工具不出现:检查装饰器是否用到(
@mcp.tool()别忘了括号)、函数是否在mcp.run()前定义、Host 是否真的重连了该 Server。 - 模型不调用你的工具:多半是
description写得太含糊,模型不知道何时该用。把"何时调用、参数是什么"写清楚。 - 参数类型:schema 参数尽量用基础类型(str/int/bool),复杂嵌套结构模型填参易错。
- 长任务:耗时操作建议异步 + 进度通知(
notifications/*),否则 Host 可能以为 Server 卡死。 - 权限:访问文件系统/网络的 Server 务必在最小权限下运行,避免被模型误用造成破坏。
六、生态现状(2026)
- 官方与社区 Server:filesystem、git/github、postgres/sqlite、puppeteer/playwright(浏览器)、fetch、记忆(memory)、sequential-thinking 等数百个
- Host 全面支持:Claude 全家桶、Cursor、Windsurf、VS Code Copilot、Cline、DeepSeek Harness 等
- 国内模型:Qwen、GLM、DeepSeek 的官方 Agent 框架均已支持 MCP 工具接入
- 企业落地模式:把内部系统(工单、CRM、数据仓库)封装成 MCP Server,一次投入、所有 AI 客户端通用
6.1 为什么企业喜欢 MCP
- 一次封装,处处可用:把内部 ERP/CRM/数据仓库封装成一个 MCP Server,Claude Desktop、Cursor、自研 Agent 全部直接接入,无需每端重写胶水;
- 安全边界清晰:Roots 限定访问范围、Sampling 让模型留在 Host 侧、Server 可独立做权限审计;
- 可被治理:工具清单可集中登记、版本化、下线,比散落各处的私有插件好管理。
七、MCP 与 Function Calling 的关系
容易混淆的一点:MCP 不替代 Function Calling,而是建立在它之上。模型侧看到的仍然是工具 schema(Function Calling 机制);MCP 解决的是"工具从哪里来、如何标准化发现与调用":
模型 ──(Function Calling)──► Agent/Host ──(MCP 协议)──► MCP Server ──► 真实系统
即:Function Calling 是模型能力,MCP 是工具分发协议。
7.1 两者的分工(关键)
| 维度 | Function Calling | MCP |
|---|---|---|
| 层级 | 模型能力(LLM API 层面) | 传输/分发协议(应用架构层面) |
| 解决 | "模型如何结构化表达'我想调函数'" | "工具在哪、怎么发现、怎么标准化调用" |
| 范围 | 单次请求内的工具调用 | 跨进程、跨服务的工具/资源/提示治理 |
| 关系 | 是 MCP 的"最后一公里" | 建立在 Function Calling 之上 |
通俗讲:Function Calling 让模型"会说我要调工具",MCP 让"工具真正能被找到并调到"。Host 从 MCP Server 拉来 tool schema,把它转成模型能懂的 Function Calling schema 喂给 LLM;模型返回 tool_call 后,Host 通过 MCP 协议把调用送回对应 Server 执行,再把结果回填模型。
7.2 一个端到端串联示意
┌─────────────────────────────────────────────────────┐
│ Host (如 Claude Desktop) │
│ 1. 启动时连 MCP Server,list_tools 拿到 schema │
│ 2. 把 schema 转成 Function Calling 的 tools 列表 │
│ 3. 用户提问 → 模型根据 schema 返回 tool_call │
│ 4. Host 通过 MCP 把 tool_call 发回 Server 执行 │
│ 5. Server 返回 result → Host 回填模型 → 模型作答 │
└─────────────────────────────────────────────────────┘
│ MCP (JSON-RPC over stdio/HTTP)
▼
MCP Server (filesystem/github/postgres/自研)
八、协议消息全流程示例(tools/call)
把前面各层串起来,看一次完整的"查天气"调用在 MCP 里长什么样:
// [Host] Client 列出可用工具
{ "jsonrpc": "2.0", "id": 10, "method": "tools/list" }
// [Server] 返回工具清单(schema 即 Function Calling 所需)
{ "jsonrpc": "2.0", "id": 10,
"result": { "tools": [
{ "name": "get_weather", "description": "查询城市天气",
"inputSchema": { "type": "object",
"properties": { "city": { "type": "string" } }, "required": ["city"] } }
] } }
// [Host] 模型决定调用,Client 发出 tools/call
{ "jsonrpc": "2.0", "id": 11, "method": "tools/call",
"params": { "name": "get_weather", "arguments": { "city": "北京" } } }
// [Server] 执行后返回(content 数组支持 text/image/resource)
{ "jsonrpc": "2.0", "id": 11,
"result": { "content": [ { "type": "text", "text": "北京:晴,26°C" } ] } }
注意 result.content 是数组,元素可多种类型——文本、图片、甚至内嵌 resource 引用。这让 MCP 工具能返回富结果(如截图、文件内容),远比单一字符串灵活。
九、安全与最佳实践
- 最小权限:Server 只申请它真正需要的访问范围(配合 Roots);
- 危险操作审批:写文件、发请求、执行命令的 tool,Host 侧应有人工确认;
- 日志隔离:stdio Server 的日志走 stderr/文件,绝不污染 stdout;
- 输入校验:tool 内部对参数做校验,错误信息清晰回传模型以便自愈;
- 能力按需暴露:Server 只在
initialize声明自己真支持的原语,避免 Host 误用; - 版本兼容:initialize 时协商
protocolVersion,新旧 Host/Server 才能共存; - 远程 Server 鉴权:HTTP 模式必须 API Key/OAuth,不在公网裸奔。
十、参考
- 规范:https://spec.modelcontextprotocol.io
- Python SDK:https://github.com/modelcontextprotocol/python-sdk
- TypeScript SDK:https://github.com/modelcontextprotocol/typescript-sdk
- Servers 合集:https://github.com/modelcontextprotocol/servers
- 官网文档:https://modelcontextprotocol.io
十一、小结
MCP 给"AI 应用 ↔ 工具/数据"提供了一套开放、标准的接口,把过去每个应用各写一套胶水的 M×N 难题,降维成 M+N。理解它要抓住三条主线:
- 三角色架构:Host(承载 LLM 与用户)内置 Client,与每个 Server 1:1 连接,分层清晰、互不干扰;
- JSON-RPC 2.0 消息 + 能力协商:所有通信是结构化 JSON,
initialize握手声明彼此支持的原语与版本,保证向后兼容; - 三类原语分工:Tools(模型主动调、有副作用)、Resources(Host 注入的只读资料)、Prompts(用户触发的模板),再加 Sampling/Roots 两个客户端侧安全原语。
传输上,stdio 适合本地、HTTP/SSE 适合远程,按部署位置选择。工程落地时,MCP 不替代 Function Calling,而是站在它之上——Function Calling 是模型"表达要调工具"的能力,MCP 是"工具从哪来、怎么标准化发现与调用"的协议,二者一个在模型层、一个在架构层,互补而非互斥。
落地 MCP 的最大收益在企业内部系统接入:把工单、CRM、数据仓库封装成 Server,一次投入、所有 AI 客户端通用,且安全边界(Roots/Sampling/审批)清晰、可被集中治理。踩坑多集中在 stdio 日志污染、schema 描述不清、长任务无进度通知——按本文 5.3 与第九节的实践清单即可规避。

浙公网安备 33010602011771号