Model Context Protocol(MCP)正在成为 AI 应用连接外部工具与数据的「事实标准」。本文从通信原理讲起,逐步讲清它在你项目里该怎么用、怎么通信、有哪些坑。
目录
[MCP 是什么,解决什么问题](#MCP 是什么,解决什么问题)
[核心架构:Host / Client / Server 三层](#核心架构:Host / Client / Server 三层)
[三大原语:Tools / Resources / Prompts](#三大原语:Tools / Resources / Prompts)
[通信协议:JSON-RPC 2.0](#通信协议:JSON-RPC 2.0)
[传输方式:stdio 与 Streamable HTTP](#传输方式:stdio 与 Streamable HTTP)
[如何在你的项目里使用 MCP](#如何在你的项目里使用 MCP)
1. MCP 是什么,解决什么问题
在 MCP 出现之前,一个 AI 应用想接入外部能力(数据库、文件系统、GitHub、企业内部 API......),基本是「一应用一适配」:每个数据源都要写一套专门的集成代码。N 个 AI 应用 × M 个数据源,就是 N×M 的适配工作量。
MCP 把这个局面改成了 N+M:它定义了一套统一的、开放的标准协议,让 AI 应用通过同一个接口就能连接任意实现了该协议的工具和数据源。
一个广为流传的类比:MCP 之于 AI 应用,就像 USB-C 之于电子设备。 过去每个设备要配一根专用线,现在一个口、一根线通吃。
具体来说,MCP 由 Anthropic 于 2024 年 11 月 开源,定位是「模型上下文协议」------标准化 AI 应用(宿主)与外部工具/数据源(服务端)之间的通信方式。它不绑定任何模型厂商,Claude、GPT、Gemini、各类 IDE 和 Agent 框架都能用。
协议演进(截至 2025-11-25 最新版):
|------------|---------|----------------------------------------------|
| 版本 | 日期 | 关键变化 |
| 2024-11-05 | 2024.11 | 首次发布:stdio + HTTP+SSE,核心原语 |
| 2025-03-26 | 2025.03 | Streamable HTTP 取代旧 SSE 设计,加入会话管理与 OAuth 2.1 |
| 2025-06-18 | 2025.06 | 结构化输出、Elicitation(人机协同)、改进授权 |
| 2025-11-25 | 2025.11 | Tasks(异步任务)、Extensions 框架、企业级认证、图标 |
注意:旧的 HTTP+SSE 双端点传输已在 2025-11-25 版中被 标记为废弃 ,新项目应使用 Streamable HTTP。
2. 核心架构:Host / Client / Server 三层
MCP 是一个「参与者模型」,有三类角色:

- Host(宿主):承载 AI 对话的应用,比如 Claude Desktop、Cursor、你自己写的 Agent 服务。它负责接收用户输入、调用大模型、并把结果呈现给用户。
- Client(客户端) :协议实现层,运行在 Host 内部。每个 Client 与一个 Server 保持 1:1 连接。它负责把 Host 的意图翻译成标准的 JSON-RPC 消息发给 Server。
- Server(服务端):暴露具体能力的一方,比如「查天气」「读数据库」「操作 GitHub」。一个 Host 可以同时连接多个 Server。
为什么要有 Client 这一层? 把「宿主」和「协议」解耦。Host 专注产品体验,Client 专注协议细节(连接管理、能力协商、消息路由),这样同一个 Client 实现可以被不同 Host 复用。
3. 三大原语:Tools / Resources / Prompts
MCP 规定 Server 可以向 AI 暴露三类「原语」(primitives),它们回答三个不同的问题:
|-------------------|--------------|----------------|--------------------|
| 原语 | 回答的问题 | 方向 | 例子 |
| Tools(工具) | 我能做什么 | 模型触发 → 执行动作 | 查询数据库、发邮件、创建工单 |
| Resources(资源) | 我有什么数据可读 | 宿主读取 → 获取上下文 | 配置文件、日志、数据库 schema |
| Prompts(提示词) | 有什么模板可复用 | 用户/宿主选择 → 套用模板 | 代码审查模板、SQL 生成模板 |
三者意图不同,这也是 MCP 和传统「函数调用(Function Calling)」最大的区别之一:Tools 由模型决定何时调用,Prompts 由用户决定用哪个,Resources 则是被读取的上下文。
除了 Server 侧的原语,MCP 还定义了客户端(Client/Host)侧的能力,让 Server 能「反客为主」:
- Sampling:Server 反过来请求 Host 帮它做一次 LLM 补全。这样 Server 不用自带模型 SDK,也能「借」宿主的大模型能力。
- Elicitation:Server 请求用户补充信息或确认操作(人机协同、二次确认)。
- Logging:Server 向 Client 推送结构化日志,方便调试和监控。
一个常见误区:很多人以为 MCP 只有「工具调用」。其实 Resources(喂上下文)和 Prompts(喂模板)同样重要,很多场景下它们比工具更好用。
4. 通信协议:JSON-RPC 2.0
MCP 的「信的内容」用的是 JSON-RPC 2.0 ,全部消息 UTF-8 编码。它只有四种消息形态:
- 请求(Request) :带
id,期待一个响应。 - 响应(Response / Result) :带
id,对应某个请求的结果。 - 错误(Error) :带
id,对应某个请求的失败。 - 通知(Notification) :不带
id,单向发送、不期待响应。
// 请求:调用一个工具
{ "jsonrpc": "2.0", "id": 7, "method": "tools/call",
"params": { "name": "search_issues", "arguments": { "query": "auth" } } }
// 响应:工具执行结果
{ "jsonrpc": "2.0", "id": 7,
"result": { "content": { "type": "text", "text": "找到 3 个 issue..." } } }
// 通知:工具列表变了(无 id,无响应)
{ "jsonrpc": "2.0", "method": "notifications/tools/list_changed" }
核心方法一览:
|------|--------------------------------------|--------------|
| 类别 | 方法 | 用途 |
| 生命周期 | initialize | 握手、协商协议版本与能力 |
| | notifications/initialized | 客户端确认就绪 |
| | ping | 心跳/保活 |
| 工具 | tools/list / tools/call | 发现工具 / 执行工具 |
| 资源 | resources/list / resources/read | 列出资源 / 读取资源 |
| 提示词 | prompts/list / prompts/get | 列出模板 / 获取模板 |
| 通知 | notifications/tools/list_changed 等 | 能力动态变化时推送 |
关键点:能力是「运行时发现」的,而不是「编译期写死」的 。Client 通过
tools/list等接口在连接后才知道 Server 有哪些能力,这是 MCP 能解耦 N×M 问题的根基。
5. 传输方式:stdio 与 Streamable HTTP
MCP 把「信的内容」(JSON-RPC)和「怎么送信」(传输层)分离。规范定义了两种传输:
5.1 stdio(标准输入输出)
Client 把 Server 作为子进程启动 ,通过它的 stdin/stdout 交换 JSON-RPC 消息。
- 每条消息一行,换行符分隔,消息内部不得包含换行。
- Server 的日志只能写到
stderr;stdout上只能出现合法的 MCP 消息。
# 典型启动方式:Client 拉起 Server 子进程 npx -y @modelcontextprotocol/server-filesystem /tmp/workspace
特点:零网络配置、最简单、生命周期绑定进程。适合本地工具、IDE 集成、单人开发调试。
5.2 Streamable HTTP(可流式 HTTP)
Server 作为独立进程 运行,监听单个 HTTP 端点(如 https://example.com/mcp),可同时服务多个客户端。
- 客户端 → 服务端 :每次发消息都是一个独立的
HTTP POST到该端点,Accept头需同时声明application/json和text/event-stream。
- 服务端 → 客户端 :服务端可返回普通 JSON,也可升级为 SSE(Server-Sent Events)流 持续推送消息;客户端也可用
HTTP GET打开一个 SSE 流来被动接收服务端发起的消息。
- 会话通过
MCP-Session-Id头显式管理,SSE 事件 ID 支持断线重连与消息回放。
特点:支持远程、多客户端、横向扩展,能挂到标准 HTTP 基础设施(负载均衡、API 网关、认证中间件)后面。生产环境、云部署首选。
5.3 对比
|------|--------------|---------------------------|
| 维度 | stdio | Streamable HTTP |
| 部署形态 | Client 拉起子进程 | 独立进程,可远程 |
| 网络 | 仅本机 | 可跨网络 |
| 多客户端 | 不支持(1:1 进程) | 支持 |
| 会话管理 | 隐式(随进程生命周期) | 显式(Session-Id 头) |
| 断线重连 | 不适用 | 支持(SSE event ID 回放) |
| 认证 | 无(本机信任) | OAuth 2.1 / API Key / JWT |
| 适用 | 本地工具、开发调试 | 生产、云部署、远程 |
经验法则:开发期用 stdio 起步,生产环境迁移到 Streamable HTTP。
6. 通信全流程:一次完整的生命周期
MCP 的每一次连接都严格遵循 初始化 → 能力协商 → 运行 → 关闭 四个阶段。

逐阶段拆解:
① 初始化(握手) :Client 主动发 initialize,带上自己支持的 protocolVersion(日历版本号,如 2025-06-18)和 capabilities。Server 返回自己的版本与能力。版本若无法达成一致,连接必须断开。
② 能力协商 :双方各自声明支持的特性(tools/resources/prompts/logging/sampling......),并只使用「双方都声明了」的能力。例如 Server 没声明 tools,Client 就不能 调 tools/list。
③ 就绪确认 :Client 发一个 notifications/initialized 通知(无 id),表示可以开始正常通信。在此之前,双方只允许发 ping 和日志。
④ 运行 :Client 通过 */list 发现能力、tools/call / resources/read / prompts/get 使用能力;Server 可随时用 notifications/* 推送变化。
⑤ 关闭 :MCP 没有专门的 shutdown 方法,关闭传输连接即结束会话。stdio 下 Client 先关子进程输入流、再发 SIGTERM、必要时 SIGKILL;HTTP 下直接关闭连接。双方都要能优雅处理「连接意外断开」。
7. 一个完整的工具调用报文长什么样
把上面的流程落到最真实的场景:模型决定要「查 GitHub 上 auth 模块的 open issue」,一次完整的 tools/call 长这样:
// 1. Client → Server:执行工具 { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "search_issues", "arguments": { "query": "auth", "state": "open" } } } // 2. Server → Client:返回结果(结构化 content 数组) { "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "找到 3 个 issue:#12 登录超时、#18 鉴权绕过、#25 token 刷新" } ], "isError": false } } // 3. 如果工具执行失败(业务错误,不是协议错误) { "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "查询失败" } ], "isError": true } }
几个值得注意的细节:
- 工具的输入参数结构由
tools/list返回的inputSchema(JSON Schema)声明,模型是读 schema 来决定怎么传参 的------所以description写得越清楚,模型用得越准。
- 返回值是
content数组(可含多个文本/图片/资源块),而不是裸字符串。
- 业务错误 用
isError: true表达(工具跑了但失败);协议错误 (请求格式错误、方法不存在)则走 JSON-RPC 的error字段,两者要分清。
8. 如何在你的项目里使用 MCP
你的角色决定了用法。分三种情况:
8.1 你是「使用方」:接入现成的 MCP Server
最常见。很多常用能力已有现成 MCP Server,直接配置接入即可,无需自己写代码。
以把「GitHub MCP Server」接进某个 Host 为例,配置形如:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "<你的 token>" }
}
}
}
不同 Host 的配置入口不同(Claude Desktop 是 claude_desktop_config.json,Cursor 在设置里,WorkBuddy 在连接器管理里),但核心字段几乎一致 :command + args(stdio 方式)或 url + 认证信息(Streamable HTTP 方式)。
结合你的运维场景:GitHub MCP(管仓库/PR/Issue)、腾讯云轻量服务器 MCP(管实例/防火墙/快照)都能直接接进工作流,让 Agent 直接操作这些资源,而不是人肉去点控制台。
8.2 你是「开发方」:用 SDK 写一个 MCP Server
如果内部有专属工具/数据要暴露给 AI,就用官方或社区 SDK 写一个 Server。下面以 Go 为例(社区流行的 mark3labs/mcp-go,官方亦有 modelcontextprotocol/go-sdk):
package main import ( "context" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func main() { s := server.NewMCPServer("my-server", "1.0.0") // 注册一个 echo 工具 s.AddTool( mcp.NewTool("echo", mcp.WithDescription("返回用户输入的内容,用于连通性测试"), mcp.WithString("message", mcp.Required(), mcp.Description("要回显的内容"), ), ), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { msg := req.Params.Arguments["message"].(string) return mcp.NewToolResultText("echo: " + msg), nil }, ) // 以 stdio 方式启动(生产可换 Streamable HTTP) server.ServeStdio(s) }
Python 官方 SDK 的写法更简洁(mcp.server.fastmcp 装饰器),适合快速验证;Go 适合写进你现有的服务里。核心思路一致:定义工具 → 注册实现 → 选一个传输方式跑起来。
8.3 你是「集成方」:在自有 Agent 里同时当 Client 和 Server
像需要维护的Go + Gin 的 Agent 服务,典型姿势是:
- 对外(上游 Agent / IDE)暴露能力 → 用 SDK 起一个 MCP Server;
- 对内(调用下游数据源 / 其他工具)→ 用 SDK 起一个 MCP Client,去连 GitHub MCP、腾讯云 MCP 等。
一个进程可以同时是 Server 又是 Client ,这正是 Agent 作为「中间编排层」的常见形态。选型建议:对外暴露用 Streamable HTTP(可远程、可认证),对内调用本地子进程工具用 stdio。
9. 注意事项与安全红线
这是最容易踩坑、也最该认真对待的部分。
9.1 传输层安全(Streamable HTTP 必须做)
官方规范明确要求:
- 必须校验
Origin头 ,防止 DNS rebinding 攻击(恶意网页借本地 MCP Server 发起操作)。
- 本地运行时默认只绑定
127.0.0.1,不要轻易绑0.0.0.0暴露到公网。
- 远程 Server 必须做认证(OAuth 2.1 / API Key / JWT)。
9.2 提示词注入(Prompt Injection)
MCP 标准化了通道,但没有标准化「信任」。 Server 返回的内容会被模型直接读取,恶意 Server(或 Server 拉到的外部数据)可能夹带指令诱导模型做出危险操作。务必:
- 把「工具输出」与「系统指令」在语义上隔离,不要无条件信任工具返回内容当指令执行。
- 对高危操作(删除、支付、发布)设置人工确认点(可用 Elicitation 实现)。
9.3 权限与治理
- MCP 不定义 工具级/资源级的细粒度授权,也没有速率限制、审计、成本追踪。这些必须自己建一层(API 网关、中间件、管理平台)。
- 记住原则:「它说 MCP」只代表它能对话,不代表它有权操作。 该收口的权限要自己收口。
9.4 日志与敏感信息
- 日志只写
stderr(stdio 下),绝不让日志污染stdout,否则协议解析会崩。
- 不打印 Token、密码、完整连接串等敏感信息。
9.5 错误处理与超时
- 所有请求都要设超时,防止连接挂死、资源耗尽。
- 区分两类错误:协议错误 (JSON-RPC
error)和业务错误 (isError: true),处理与重试策略不同。
- 服务端要能优雅处理「连接意外断开」,不要假设正常关闭。
10. 常见误区与最佳实践
误区一:MCP 就是 Function Calling。 不对。MCP 是传输 + 发现 + 生命周期的完整协议,Resources/Prompts/Sampling 等都是 Function Calling 不具备的。它解决的是「连接管理」,而非「让模型会调函数」这件事。
误区二:接一个 MCP Server 就万事大吉。 MCP 不提供能力发现注册中心、不提供编排(多 Agent 协作请用 A2A)、不提供治理。这些是你要在它之上补的。
误区三:工具描述随便写。 description 是模型和你的系统之间的唯一接口。一句模糊的「管理订单」远不如「根据订单号查询订单状态,参数 orderId 必填,返回含状态与时间的 JSON」可靠。
最佳实践小结:
- 开发用 stdio,生产用 Streamable HTTP。
- 工具
description写清「输入、输出、副作用、何时用/何时别用」。
- 给所有工具调用设超时;监控每次调用的延迟、错误率、token 消耗。
- 高危操作加人工确认;对外 Server 强制认证 + Origin 校验。
- 为工具定义做版本管理,利用
tools/list_changed通知客户端变化。
- 把 MCP Server 当生产 API 一样做可观测性------它就是你新的「外部依赖」。
11. 总结与扩展阅读
一句话总结 :MCP 是一套「参与者模型(Host/Client/Server)+ 三类意图原语(Tools/Resources/Prompts)+ 基于 JSON-RPC 2.0 的可协商生命周期 + 可插拔传输(stdio / Streamable HTTP)」的开放协议。它把 AI 应用与外部工具的连接从 N×M 降到 N+M,但只标准化了通道,不标准化信任与治理------安全与治理责任在你。
核心要点回顾:
- 通信内容:JSON-RPC 2.0(请求 / 响应 / 错误 / 通知)。
- 通信流程:
initialize握手 → 能力协商 →notifications/initialized→ 发现(*/list)→ 使用(tools/call等)→ 关闭传输。
- 能力是运行时发现的,
description和inputSchema是模型正确使用工具的钥匙。
- 安全红线:校验 Origin、绑 localhost、必须认证、防提示词注入、高危操作人工确认、日志脱敏。
扩展阅读:
- 官方架构与生命周期说明:Architecture overview - Model Context Protocol
