Jean's Blog

一个专注软件测试开发技术的个人博客

0%

MCP 通信机制:JSON-RPC、stdio 与 Streamable HTTP

MCP 通信由两层组成

MCP 的通信机制可以分为两层:

  1. 数据层(Data Layer):基于 JSON-RPC 2.0 定义请求、响应、通知、生命周期和能力协商;
  2. 传输层(Transport Layer):负责在 Client 与 Server 之间搬运消息。

截至 MCP 2026-07-28 规范,标准传输方式有两种:

  • stdio:适合由 Host 启动的本地子进程;
  • Streamable HTTP:适合独立部署的远程服务。

旧版 HTTP+SSE 传输已经被 Streamable HTTP 替代。它可以作为兼容旧 Server 的历史知识保留,但不应再作为新项目的默认方案。


JSON-RPC 消息与连接生命周期

MCP 使用 JSON-RPC 2.0 消息:

消息 特征 例子
Request 包含 id,对端必须返回结果或错误 tools/listtools/call
Response 与 Request 使用相同 id 工具列表、工具执行结果
Notification 没有 id,无需响应 进度通知、能力变化通知

一段简化的工具调用消息如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "add",
"arguments": {
"a": 3,
"b": 5
}
}
}

连接建立后,Client 与 Server 需要先完成初始化和能力协商:

1
2
3
4
Client ── initialize ──> Server
Client <─ result + capabilities ── Server
Client ── notifications/initialized ──> Server
进入正常通信阶段

只有在双方协商声明支持某项能力后,才应调用对应方法。不能因为协议中定义了 Prompts、Resources 或 Sampling,就假设每个 Client 和 Server 都支持它们。


一、stdio:本地子进程通信

工作原理

Host / Client 启动 MCP Server 子进程,通过标准输入和标准输出交换逐行 JSON-RPC 消息:

1
2
3
4
Host / MCP Client
├── 启动 python math_server.py
├── stdin ──────────────> Server
└── stdout <────────────── Server

stdio 模式 MCP Server 架构

适用场景

  • 本地文件、终端、IDE 和桌面应用集成;
  • 开发调试或单用户本地工具;
  • 不希望额外暴露网络端口的场景。

优点与限制

优点 限制
无需监听网络端口,配置简单 通常只适用于同一台机器
Host 可控制 Server 进程生命周期 每个 Client 可能需要独立子进程
本地通信开销低 stdout 不能混入普通日志或调试输出

在 stdio 模式下,Server 的普通日志应该写入 stderr,因为 stdout 专用于协议消息。

创建 stdio Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from mcp.server import MCPServer

mcp = MCPServer("Math Server")


@mcp.tool()
def add(a: int, b: int) -> int:
"""计算两个整数之和。"""
return a + b


@mcp.tool()
def multiply(a: int, b: int) -> int:
"""计算两个整数之积。"""
return a * b


if __name__ == "__main__":
mcp.run("stdio")

LangChain Client 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import asyncio

from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

from src.core.llms import model_client


async def main() -> None:
client = MultiServerMCPClient(
{
"math": {
"transport": "stdio",
"command": "python",
"args": ["/absolute/path/to/math_server.py"],
}
}
)

tools = await client.get_tools()
agent = create_agent(model=model_client, tools=tools)
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "100 × 100 等于多少?"}]}
)
print(result["messages"][-1].content)


if __name__ == "__main__":
asyncio.run(main())

配置 commandargs 后,Client 会启动 Server 子进程,因此通常不需要手动先运行 math_server.py。路径应该使用绝对路径或可预测的项目路径,不要把个人电脑上的固定路径直接复制到公共项目中。


二、Streamable HTTP:远程服务通信

工作原理

Streamable HTTP 使用 HTTP POST 发送 Client 消息。Server 可以返回普通 JSON 响应,也可以按需返回 SSE 事件流。与旧 HTTP+SSE 方案不同,它使用统一的 MCP 端点,通常是 /mcp

Streamable HTTP 模式 MCP Server 架构

1
2
MCP Client ── POST /mcp ──> MCP Server
MCP Client <─ JSON 或 SSE ── MCP Server

SSE 在这里是一种可选的响应流格式,不等于旧版“HTTP+SSE 传输”。

适用场景

  • 云端或团队共享的 MCP Server;
  • 多 Client 访问同一服务;
  • 需要认证、负载均衡、网关和可观测性的生产环境。

创建 Streamable HTTP Server

1
2
3
4
5
6
7
8
9
10
11
12
13
from mcp.server import MCPServer

mcp = MCPServer("Math Server")


@mcp.tool()
def add(a: int, b: int) -> int:
"""计算两个整数之和。"""
return a + b


if __name__ == "__main__":
mcp.run("streamable-http")

启动后,默认地址通常类似:

1
http://127.0.0.1:8000/mcp

实际主机、端口和路径应以 Server 配置为准。

LangChain Client 配置

当前 langchain-mcp-adapters 使用 http 表示 Streamable HTTP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import asyncio

from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

from src.core.llms import model_client


async def main() -> None:
client = MultiServerMCPClient(
{
"math": {
"transport": "http",
"url": "http://127.0.0.1:8000/mcp",
}
}
)

tools = await client.get_tools()
agent = create_agent(model=model_client, tools=tools)
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "计算 100 + 100"}]}
)
print(result["messages"][-1].content)


if __name__ == "__main__":
asyncio.run(main())

旧版示例中常见的 transport="streamable_http" 属于早期适配器配置。运行具体项目时,应以安装版本的 langchain-mcp-adapters 文档为准。


三、HTTP+SSE:仅用于兼容旧 Server

旧版 HTTP+SSE 传输通常使用:

  • GET /sse 建立 Server 到 Client 的事件流;
  • 单独的 HTTP POST 端点发送 Client 消息。

它已经被 Streamable HTTP 取代。原博客中“当前 MCP 支持三种主要通信方式”的表述不再准确,更合适的说法是:

当前规范定义 stdio 和 Streamable HTTP 两种标准传输;HTTP+SSE 是已废弃的旧版兼容方式。

只有在连接尚未迁移的历史 Server 时,才考虑配置 transport: "sse"。新项目不应继续编写新的 SSE Server。


有状态与无状态 HTTP

Streamable HTTP 可以按实现需要采用有状态或无状态设计。

模式 特点 适用场景
有状态 Server 保存会话状态,可关联多个请求 需要会话、订阅或长期任务
无状态 每个请求独立,便于水平扩展 简单工具服务、Serverless、高并发

是否使用会话、恢复或流式响应,应由实际 SDK 和 Server 实现决定。不能把“Streamable HTTP”直接等同于“完全无状态”或“连接一定可以恢复”。


生产环境安全要求

远程 MCP Server 至少需要考虑:

  1. HTTPS:不要在公网使用明文 HTTP;
  2. 认证与授权:验证访问令牌,并检查用户是否有权调用具体能力;
  3. Origin 校验:防止 DNS rebinding 等攻击;
  4. 本地绑定:本地调试服务优先只监听 127.0.0.1
  5. 超时与限流:限制工具执行时间、请求体大小、调用频率和并发;
  6. 日志脱敏:不要记录令牌、密钥和敏感工具参数;
  7. 会话安全:会话标识不能代替认证,也不应包含敏感信息。

如何选择传输方式

场景 推荐方式
访问本机文件、终端或 IDE stdio
本地开发一个简单 Server stdio
部署团队共享的远程 Server Streamable HTTP
云端高并发或 Serverless 无状态 Streamable HTTP
接入尚未迁移的历史 Server 临时使用 HTTP+SSE,并安排迁移

小结

MCP 的传输层并不决定 Agent 的业务能力,它只决定消息如何在 Client 与 Server 之间流动:

  • stdio:本地、简单、由 Host 管理进程;
  • Streamable HTTP:远程、可部署、适合生产基础设施;
  • HTTP+SSE:旧版兼容方式,不再推荐新项目使用。

下一篇将介绍 MCP Server 的 Tools、Resources、Prompts、结构化输出、Context 与认证边界。