摘要:随着 Anthropic 推出 MCP(Model Context Protocol,模型上下文协议),AI 社区迎来了大模型时代连接外部上下文与工具功能的“USB-C 标准”。然而,许多习惯了传统微服务架构的开发者不禁产生疑问:在 HTTP REST 和 gRPC 已经统治分布式系统的今天,MCP 为什么没有直接选择它们,而是采用了基于 JSON-RPC 2.0 的 stdio 与 SSE 传输方案?
本文将从本地 IPC 通信安全、双向交互能力、开发者体验(DX)、JSON Schema 天然兼容性以及架构演进哲学等多个维度,深度剖析 MCP 协议背后的设计决策与权衡,并带你领略 AI Agent 时代全新的系统设计范式。
前言:AI 接入层的新统一标准 —— MCP
在 MCP 出现之前,大语言模型(LLM)想要调用外部工具或读取私有数据,面临着极其繁琐的M×N 接入难题:
不同的 LLM 应用(如 Claude Desktop、Cursor、VS Code 插件、自定义 Agent)都有各自的 Tool Calling 定义和客户端插件格式。
不同的数据源和工具(如 GitHub、PostgreSQL、本地文件系统、Slack)需要为每一个 AI 客户端单独编写适配器。
为了打破这种碎片化局面,Anthropic 于 2024 年底开源了MCP(Model Context Protocol)。它定义了一套通用的客户端-服务器架构:
┌───────────────────────────────────────────────────────────┐ │ MCP Host (Client) │ │ (例如: Claude Desktop, Cursor, Custom Agent) │ └─────────────────────────────┬─────────────────────────────┘ │ MCP Protocol (JSON-RPC) │ ┌─────────────────────┼─────────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Git Server │ │ Postgres Server│ │ Slack Server │ └──────────────┘ └──────────────┘ └──────────────┘然而,打开 MCP 的技术规范,你会发现它的底层技术选型非常“特别”:
消息格式:采用JSON-RPC 2.0;
传输层(Transport):本地首选stdio(标准输入输出),远程支持SSE(Server-Sent Events)+ HTTP POST(以及 WebSocket)。
很多人第一反应是:为什么不直接用更加成熟、性能更强、生态更广的 HTTP REST API 或者 gRPC 呢?
这绝非 Anthropic 架构师的“凭空发明”,而是面对 AI 特有应用场景做出的极具远见的技术权衡(Trade-off)。
一、 核心解构:MCP 的真实协议栈
在回答“为什么不用”之前,我们需要先看清 MCP 的真实协议分层:
┌───────────────────────────────────────────────────────────┐ │ 应用层 (Application) │ │ Prompts (提示词) | Resources (资源) | Tools (工具) │ ├───────────────────────────────────────────────────────────┤ │ 消息层 (Message Layer) │ │ JSON-RPC 2.0 规范 │ ├───────────────────────────────────────────────────────────┤ │ 传输层 (Transport Layer) │ │ 本地进程: stdio | 远程网络: SSE / HTTP POST │ └───────────────────────────────────────────────────────────┘从分层设计可以看出,MCP 实现了消息格式与底层传输方式的解耦。
标准 JSON-RPC 2.0 报文示例
当 MCP Client 想要调用 Server 提供的工具时,发送的交互数据如下:
Client 发起工具调用请求 (Request):
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "query_database", "arguments": { "sql": "SELECT * FROM users WHERE active = true;" } } }Server 返回执行结果 (Response):
{ "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "[{\"id\": 101, \"name\": \"Alice\"}]" } ] } }了解了底层机制后,我们来逐一拆解:为什么传统的 HTTP 和 gRPC 不适合作为 MCP 的核心标准?
二、 深度对比一:为什么不使用传统的 HTTP (REST API)?
HTTP/REST 是目前互联网上最普及的通信范式,但将它直接作为 MCP 的通用底层传输协议,会遇到以下几个致命的架构痛点。
2.1 本地进程间通信(IPC)的零配置需求与端口冲突
在目前 MCP 的核心应用场景中,超过 80% 的 MCP Server 是以本地子进程(Subprocess)的形式运行在用户的桌面终端上(例如 Cursor 调用本地 Git 工具、Claude Desktop 读取本地 SQLite 数据库)。
如果采用 HTTP 架构:
端口冲突问题(Port Collisions):每一个本地运行的 MCP Server(可能同时跑着十几、上百个)都需要在本地监听一个 TCP 端口(如
http://localhost:8080)。当端口被占用时,需要复杂的自动重试与端口协商逻辑。本地网络安全威胁(DNS Rebinding & Local Authorization):在本地开辟 HTTP Server 会带来严峻的安全性隐患。恶意网页可以通过浏览器发起的 CSRF(跨站请求伪造)或 DNS 重定向攻击,向
http://localhost:8080发送请求,非法读取用户的私有数据。
MCP 选择stdio的降维打击:
零网络开销与零端口占用:MCP Client(主进程)直接通过 OS 级的
spawn创建 Server 子进程,通过标准管道(stdin/stdout)进行文本流传输。物理级安全隔离:子进程不监听任何网络端口,外部网络没有任何侵入路径,天然防范了网络攻击。
生命周期强绑定:父进程挂掉时,操作系统会自动清理子进程(管道断开收到 SIGPIPE),彻底避免了本地 HTTP 服务遗留僵尸进程的问题。
【HTTP 本地通信架构】 (脆弱且复杂) Browser / Attacker ──(CSRF/DNS Rebinding)──► Localhost:8080 ──► [HTTP Server] 【MCP stdio 通信架构】 (绝缘且优雅) [MCP Client 主进程] ◄─── OS Pipe (stdin / stdout) ───► [MCP Server 子进程] (完全不经过网络栈)2.2 双向交互与状态保持(Stateful & Bi-directional)
传统的 HTTP/1.1 REST 是标准的单向请求-响应模式:只能由 Client 发起 Request,Server 被动返回 Response。
但在 AI Agent 的应用场景中,通信模式绝不仅仅是“客户端调工具,服务端给结果”这么简单,它存在大量服务端主动回调客户端的高级模式:
采样(Sampling)/ 递归推理:MCP Server 在执行工具的过程中,可能需要借助大模型进行二次思考。这时 Server 会向 Client 发起
sampling/createMessage请求,反向调用 Client 绑定的 LLM 能力。上下文变更通知(Notifications):当本地文件被修改、数据库表结构变更时,MCP Server 需要主动推送通知给 Client,刷动上下文(Context)。
根目录与权限协商(Roots/Elicitation):Server 询问 Client:“我需要读取
/path/to/project的权限,请让用户进行授权。”
如果采用传统的 HTTP REST,要实现服务端主动向客户端发请求,就必须:
客户端自身也搭建一个 HTTP Server 供服务端回调(复杂度翻倍);
或者是采用低效的轮询(Polling)机制。
MCP 采用JSON-RPC 2.0配合双向管道(或 SSE + POST),原生支持了客户端与服务端的对等双向调用(Peer-to-Peer RPC)。
2.3 协议头开销与序列化开销
在本地通信场景下,HTTP 请求头(Headers)通常包含Host,User-Agent,Accept,Content-Type,Content-Length,Cookie等数百字节的元数据。
对于频率极高的本地工具调用(例如每秒读取几十个小文件片段),HTTP 协议头的传输开销甚至远超 Payload 本身。而基于stdio的换行符分隔(Newline-delimited)JSON-RPC 报文,没有任何多余的 HTTP 封装开销。
三、 深度对比二:为什么不使用高吞吐的 gRPC?
gRPC 拥有基于 HTTP/2 的多路复用、基于 Protocol Buffers 的极小二进制体积以及强类型定义,在微服务架构中是绝对的技术王者。
那么,MCP 为什么没有抱紧 gRPC 的大腿呢?
3.1 开发者体验(DX)与 AI 生态的严重冲突
AI 领域的开发者生态,与传统微服务/后端工程存在极大的性格差异。
AI 领域的工程师、数据科学家乃至自动化脚本编写者,绝大多数使用Python和TypeScript / JavaScript。他们追求的是快速原型验证(Rapid Prototyping)、开箱即用和极低的开发门槛。
gRPC 的开发流程是典型的“契约先行(Contract-First)”:
定义 .proto 文件 ➔ 使用 protoc 编译生成 Stub 代码 ➔ 编写业务逻辑 ➔ 处理复杂的依赖构建这套流程在企业级微服务中非常严谨,但在 AI 领域却构成了巨大的开发摩擦力(Developer Friction):
想写一个仅有 20 行 Python 代码的简单 Git 读取工具,却不得不配置
protoc工具链与编译步骤。动态脚本语言(如 Python / JS)无法充分享受编译型语言的静态桩代码红利,反而被类型编译打乱了工作流。
MCP 的极简 DX 范式:
在 MCP 中,创建一个完整的 Server 只需要写一个简单的 Python 函数,加上装饰器即可:
from mcp.server.fastmcp import FastMCP mcp = FastMCP("My Quick Server") @mcp.tool() def add(a: int, b: int) -> int: """Add two numbers together.""" return a + b if __name__ == "__main__": mcp.run(transport="stdio")无需任何.proto编译,没有任何前置构建步骤,直接运行!
3.2 JSON Schema 是大模型的“母语”
这是 MCP 拒绝 Protobuf / gRPC 最核心的底层原因:大语言模型(LLM)的 Function Calling 原生基于 JSON Schema。
无论是 OpenAI 的tools字段、Anthropic 的tools规范,还是 Gemini 的 Function Declaration,其入参和出参的定义格式全部是标准 JSON Schema。
{ "name": "get_weather", "description": "获取指定城市的天气", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "城市名称" } }, "required": ["location"] } }如果采用 gRPC:MCP Server 的开发者需要先写 Protobuf 描述,运行时再通过复杂的转换层,将 Protobuf Schema 翻译成 LLM 能看得懂的 JSON Schema。在这个过程中,许多 JSON Schema 独有的表达能力(如
oneOf,anyOf,pattern正则校验等)会在 Protobuf 的强类型限制下丢失。如果采用 JSON-RPC:MCP 中的工具定义直接透传 JSON Schema,不需要任何中转与损耗,直接 1:1 投递给 LLM。
3.3 可观测性、调试难度与纯文本红利
AI 工具调用(Tool Calling)本质上是一个高度非确定性(Non-deterministic)的探索过程。大模型生成的工具参数经常会出现幻觉或格式错误,因此可观测性(Observability)与可调试性至关重要。
gRPC:底层基于 HTTP/2 帧与 Protocol Buffers 二进制流。如果不借助 Wireshark、Postman 等特定反序列化工具,人类无法直接用肉眼阅读管道中传输的任何数据。
MCP (JSON-RPC over stdio):纯文本 UTF-8 编码!开发者可以极其方便地将交互日志重定向到文件,或者直接用
jq工具在命令行进行流式过滤与排查:
# 直接打印并查看 MCP 通信报文 python my_mcp_server.py | jq .3.4 传输层的通用性(Transport Agnosticism)
gRPC 强绑定于 HTTP/2 协议。要想在 OS 的标准输入输出管道(stdio)上运行 gRPC,就必须在管道上完整实现一套 HTTP/2 的帧解析与多路复用状态机。这无疑是极其重型且极不自然的架构“硬套”。
而JSON-RPC 2.0 只是纯粹的文本字符串规范,它对传输介质零依赖:
它可以运行在
stdio上(用于本地子进程);它可以运行在
SSE + HTTP POST上(用于远程轻量 Web 服务);它可以运行在
WebSocket上(用于长连接交互);它甚至可以运行在
PostMessage/WebWorker上(用于浏览器沙箱内部)。
四、 综合多维对比矩阵
为了更加清晰地展现各协议在 AI 场景下的优劣,我们整理了如下横向对比大表:
| 评估维度 | MCP (JSON-RPC over stdio/SSE) | HTTP (REST API) | gRPC (HTTP/2 + Protobuf) | WebSockets |
| 消息序列化 | JSON / JSON-RPC 2.0 | JSON / XML / Text | Protocol Buffers (二进制) | 自定义 / JSON |
| 本地 IPC 适宜度 | 极大 (极佳,无网络栈与端口开销) | 差 (需绑定 localhost,端口易冲突) | 差 (需在 Pipe 上封装 HTTP/2) | 中等 (仍需监听网络端口) |
| 双向通信能力 | 原生支持 (Client/Server 对等请求) | 极差 (仅单向 Request-Response) | 支持 (Streaming) | 支持 (纯双向流) |
| 开发者体验 (DX) | 极简 (零编译,开箱即用) | 简单 (熟悉度高) | 复杂 (必须定义与编译.proto) | 中等 (需自行设计消息路由) |
| LLM 兼容度 | 原生契合 (1:1 匹配 JSON Schema) | 良好 (需要手动解析 JSON) | 较差 (需 Protobuf ➔ JSON 转换) | 良好 |
| 调试与肉眼可读性 | 极佳 (纯文本,jq直接排查) | 极佳 (Postman / Curl) | 较差 (二进制编解码需专用工具) | 良好 |
| 本地安全性 | 绝对安全 (无网络端口暴露) | 有风险 (容易受 DNS Rebinding 攻击) | 有风险 (需要端口暴露) | 有风险 (需验证 Origin) |
| 网络吞吐性能 | 中等 (文本解析) | 中等 | 极高 (二进制压缩与多路复用) | 高 |
五、 MCP 远程传输的工程精妙:为什么选择 SSE?
在看完本地通信后,可能会有人问:如果 MCP 部署在远程服务器上,它又是怎么处理的呢?
MCP 规范定义了远程传输的标准模式:SSE(Server-Sent Events) + HTTP POST。
【MCP 远程传输流程】 MCP Client MCP Server │ │ │ ─── 1. HTTP GET (Header: Accept: text/event-stream) ───►│ │ ◄─── 2. 建立 SSE 长连接 (推送 endpoint URI) ───────────────│ │ │ │ ─── 3. HTTP POST (发送 JSON-RPC 请求到 endpoint) ─────────►│ │ ◄─── 4. 通过 SSE 通道流式异步返回 JSON-RPC 响应 ───────────│为什么远程不选普通的 REST,也不选复杂的 WebSocket?
为什么不用纯 REST?
前文提到,MCP 需要服务端具备主动推送通知(Notifications)和发起采样(Sampling)的能力。纯 REST 无法实现服务端主动推流。
为什么首选 SSE 而不是 WebSocket?
防火墙与 HTTP/1.1 友好性:SSE 本质上就是标准的 HTTP 响应,使用普通的长连接流(Chunked Transfer Encoding),极易穿越企业级防火墙、反向代理(如 Nginx、Envoy)以及各种 Cloud Gateway。而 WebSocket 升级协议(
Upgrade: websocket)在许多严格的企业网络环境下会被拦截。异步解耦:客户端通过普通的
HTTP POST发送请求,服务端通过建立好的SSE单向流异步回传结果。这种“单向下行流 + 短平快上行 POST”的组合,比维护一个状态复杂的双向 WebSocket 连接更加稳健,容错性更高。
六、 架构启示:AI Agent 时代的协议设计哲学
从 MCP 的协议选型中,我们可以总结出 AI Agent 时代软件架构的三大新趋势:
1. 实用主义胜过纯粹的“性能偏执”
在微服务时代,我们追求 1 毫秒还是 0.1 毫秒的 RPC 延迟,因此二进制序列化(Protobuf / FlatBuffers)是首选。
但在大模型时代,大模型自身的推理耗时通常在 500ms 到 5000ms 之间。此时,传输层节省的 2 毫秒相对于 LLM 的延迟几乎可以忽略不计。相反,文本的可读性、调试的便捷性以及开发者生态的扩展速度(DX)成为了最高优先级的指标。
2. 传输层与协议层的彻底解耦
MCP 的高明之处在于将 JSON-RPC 作为语义层,与具体的stdio/SSE传输层分离开来。这使得 MCP 可以以极轻量的方式嵌在本地命令行中,也可以无缝扩展到云端分布式服务中。
3. “Local-First(本地优先)”的 AI 隐私范式
未来的 AI Agent 不仅仅存在于云端,更多会深入到用户的本地操作系统中(读写本地文件、操作本地代码库、调用本地 CLI)。不依赖网络端口、天然隔离安全的stdio架构,为本地 AI 生态的爆发奠定了最坚实的安全基石。
总结
MCP 没有选择 HTTP REST 或 gRPC,绝不是对成熟技术的标新立异,而是在深入洞察 AI Tool Calling 范式后的必然选择。
它放弃了 HTTP 的单向无状态,换取了本地
stdio的零配置、极佳安全性与双向交互;它放弃了 gRPC 的二进制高性能与 Protobuf 约束,换取了对 JSON Schema 的原生契合、极致的开发者体验与极低的可观测性门槛。
理解了 MCP 的传输协议设计,就理解了 AI Agent 与外部世界交互的本质。希望本文能够帮助你在设计自己的 AI 插件、Agent 架构或上下文集成服务时,做出更加优雅的技术选型!