JSON 消息类型
MCP 底层使用 JSON-RPC 2.0 作为消息格式。理解四种消息类型,是真正掌握 MCP 通信机制的基础。
四种消息类型
- Request(请求):带 id + method + params,期待对方响应
- Response(响应):带相同的 id + result 或 error,是对 Request 的回答
- Notification(通知):只有 method,没有 id,单向推送,不需要响应
- Error(错误响应):带 id + error 对象(含 code、message、data),请求处理失败时返回
示例 Prompt
// Request — 客户端调用工具
{
"jsonrpc": "2.0",
"id": "req-001",
"method": "tools/call",
"params": { "name": "add_numbers", "arguments": { "a": 3, "b": 5 } }
}
// Response — 服务器成功响应
{
"jsonrpc": "2.0",
"id": "req-001",
"result": { "content": [{ "type": "text", "text": "8" }] }
}
// Notification — 服务器推送日志(无 id)
{
"jsonrpc": "2.0",
"method": "notifications/message",
"params": { "level": "info", "data": "处理完成" }
}
// Error Response — 请求处理失败
{
"jsonrpc": "2.0",
"id": "req-001",
"error": { "code": -32602, "message": "参数类型错误" }
}
消息流向
消息并非只从客户端流向服务器。Sampling 请求是服务器向客户端发送的 Request;日志通知是服务器向客户端发送的 Notification;Roots 请求也是服务器向客户端发的 Request。
🧠 自测:服务器发送进度通知时,应该使用哪种 JSON-RPC 消息类型?
- Request(因为需要客户端确认收到)
- Response(作为工具调用的中间响应)
- Notification(单向推送,不需要响应) ✓
- Error(进度更新属于特殊错误类型)
进度通知是单向推送,服务器不需要等待客户端确认。Notification 类型没有 id,客户端收到后处理即可,无需回复。
STDIO 传输
STDIO 是最简单的 MCP 传输方式,也是本课程一直在用的方式。本节深入了解它的工作原理、适用场景和局限性。
STDIO 传输的工作原理
STDIO(Standard Input/Output)传输通过进程的标准输入(stdin)和标准输出(stdout)交换 JSON-RPC 消息。客户端启动服务器子进程,通过管道(pipe)通信。
- 客户端 fork 出服务器子进程
- 客户端向服务器的 stdin 写入 JSON 消息
- 服务器向 stdout 写入响应消息
- 每条消息以换行符分隔
- 服务器的 stderr 可用于调试输出(不影响协议)
STDIO 的适用场景和局限
- ✅ 适合:本地工具、命令行服务器、Claude Desktop 集成
- ✅ 适合:开发和测试阶段,简单直接
- ✅ 优点:无需网络配置,天然安全(进程间通信)
- ❌ 局限:只支持单个客户端连接
- ❌ 局限:不支持跨网络访问,不能部署为远程服务
🧠 自测:STDIO 传输最大的局限性是什么?
- 消息格式不是标准 JSON,难以调试
- 只支持单个客户端连接,无法跨网络部署 ✓
- 需要配置复杂的网络防火墙规则
- 只能在 Windows 系统上运行
STDIO 通过进程间通信工作,天然只支持一对一的客户端-服务器关系,且服务器进程必须在客户端机器上本地运行,无法作为远程服务部署。
StreamableHTTP 传输
StreamableHTTP 是 MCP 的网络传输方式,支持多客户端并发连接和跨网络部署。它结合了 HTTP 的通用性和 SSE 的实时推送能力。
StreamableHTTP 如何工作
- HTTP POST:客户端向固定端点发送 JSON-RPC 请求
- SSE(Server-Sent Events):服务器通过长连接持续推送消息给客户端
- 支持多个客户端同时连接(不同于 STDIO 的单客户端限制)
- 适合部署为云服务或远程 API,客户端通过 URL 连接
STDIO vs StreamableHTTP 对比
- STDIO:本地进程通信,单客户端,简单,适合 Claude Desktop
- StreamableHTTP:网络通信,多客户端,需要 HTTP 服务器,适合云部署
- StreamableHTTP 支持认证(通过 HTTP 头),STDIO 无需认证(进程隔离保证安全)
- StreamableHTTP 服务器可以水平扩展,STDIO 不行
🧠 自测:StreamableHTTP 传输使用什么机制来实现服务器向客户端的实时推送?
- WebSocket 双向连接
- HTTP 长轮询(Long Polling)
- SSE(Server-Sent Events)单向推送 ✓
- gRPC 流式传输
StreamableHTTP 使用 SSE 实现服务器到客户端的实时推送。SSE 是基于 HTTP 的单向流,服务器可以持续发送事件,无需客户端反复请求。
StreamableHTTP 深入
理论够了,来写代码。本节展示如何用 FastMCP 搭建 StreamableHTTP 服务器,以及客户端如何通过 URL 连接它。
搭建 StreamableHTTP 服务器
示例 Prompt
from mcp.server.fastmcp import FastMCP, Context
import asyncio
mcp = FastMCP('http-server')
@mcp.tool()
async def long_task(steps: int, ctx: Context) -> str:
'''模拟长时间运行的任务,通过 SSE 推送进度'''
for i in range(steps):
await asyncio.sleep(1)
await ctx.session.send_log_message(level='info', data=f'步骤 {i + 1}/{steps} 完成')
return f'任务完成,共执行 {steps} 步'
if __name__ == '__main__':
mcp.run(
transport='streamable-http',
host='0.0.0.0',
port=8000
)
# 启动后访问:http://localhost:8000/mcp
编写 StreamableHTTP 客户端
示例 Prompt
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
server_url = 'http://localhost:8000/mcp'
async with streamablehttp_client(server_url) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
session.on_log_message = lambda msg: print(f'[服务器日志] {msg.data}')
result = await session.call_tool('long_task', {'steps': 3})
print(f'结果: {result.content[0].text}')
if __name__ == '__main__':
asyncio.run(main())
🧠 自测:启动 StreamableHTTP 服务器后,客户端应该连接到哪个 URL 路径?
- /api/v1/mcp
- /tools
- /mcp(FastMCP 默认路径) ✓
- /rpc
FastMCP 的 StreamableHTTP 模式默认将 MCP 端点挂载在 /mcp 路径下。完整 URL 格式为 http://host:port/mcp。
状态与 StreamableHTTP
使用 StreamableHTTP 部署服务器时,你必须决定:有状态还是无状态?这个选择对系统的可扩展性和复杂度有深远影响。
有状态服务器(Stateful)
- 优点:实现简单,状态管理由框架处理,支持复杂的多轮交互
- 缺点:每个请求必须路由到同一台服务器实例(黏性会话)
- 缺点:难以水平扩展——增加服务器实例不会自动分担现有会话
- 适合场景:单机部署、开发环境、用户量小的服务
无状态服务器(Stateless)
- 优点:可以水平扩展,负载均衡器可以将请求分发到任意实例
- 优点:任意实例宕机不影响其他实例的正常服务
- 缺点:需要外部状态存储(Redis、PostgreSQL 等),架构复杂
- 适合场景:云端部署、高并发服务、需要高可用的生产环境
🧠 自测:在需要支持大量并发用户的生产环境中,StreamableHTTP 服务器推荐使用哪种模式?
- 有状态模式,更容易实现
- 无状态模式,便于水平扩展 ✓
- 混合模式,奇数请求有状态,偶数请求无状态
- STDIO 模式,性能更高
无状态模式下,负载均衡器可以将请求分发到任意服务器实例,只要外部存储(如 Redis)保存了会话状态。这使得服务可以根据负载水平扩展。