导航

MCP的通信机制

Posted on 2025-03-11 18:30  蝈蝈俊  阅读(2352)  评论(0)    收藏  举报

在MCP(Model Context Protocol)中,为客户端-服务器通信定义了两种默认标准传输机制:

  • stdio(标准输入输出) 适用于本地通信

  • HTTP with SSE(Server-Sent Events) 适用于远程通信

其差异主要体现在通信场景、传输方式、协议实现等方面。

https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/

一、通信场景与部署方式

1、stdio(标准输入输出)

适用于客户端与服务端在同一台机器上的场景。

客户端通过启动服务端子进程(如命令行工具),利用操作系统的管道机制(stdin/stdout)进行数据传输。

是个同步阻塞模型,通信基于顺序处理,需等待前一条消息完成传输后才能处理下一条,适合简单的本地批处理任务。

2、HTTP with SSE(Server-Sent Events)

客户端与服务端可部署在不同节点,通过HTTP协议实现跨网络通信。

是个异步事件驱动。

  • 服务端通过SSE长连接主动推送数据,

  • 客户端通过HTTP POST端点发送请求,

支持实时或准实时交互,适合分布式系统或需要高并发的场景。

二、传输机制与协议实现

特性 stdio HTTP with SSE
协议基础 操作系统管道 HTTP/1.1长连接
消息格式 JSON-RPC 2.0,以换行符分隔 JSON-RPC 2.0,通过SSE事件流传输
连接方向 双向(客户端↔服务端) 客户端通过POST发送请求,服务端通过SSE单向推送响应
错误处理 依赖管道机制 支持HTTP状态码和SSE自动重连机制
适用场景 本地工具链、CLI应用 分布式系统、实时监控、远程服务调用

三、技术细节对比

1、stdio传输的核心规则

  • 消息必须为UTF-8编码的JSON-RPC格式,以换行符\n分隔。

  • 服务端通过stderr输出日志,客户端可选择处理或忽略。

  • 严格禁止在stdout中写入非协议消息,避免解析错误。


服务端:

https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/server/stdio.py

async def run_server():
    async with stdio_server() as (read_stream, write_stream):
        # read_stream contains incoming JSONRPCMessages from stdin
        # write_stream allows sending JSONRPCMessages to stdout
        server = await create_my_server()
        await server.run(read_stream, write_stream, init_options)

anyio.run(run_server)
  • read_stream 来自 stdin 的传入 JSONRPCMessages
  • write_streamJSONRPCMessages 发送到 stdout

2、SSE传输的实现要求

  • 服务端需提供两个端点:

    • SSE端点:客户端通过此建立长连接,接收事件流(如/sse)。

    • HTTP POST端点:客户端发送请求至此(如/messages)。

  • 消息推送流程:客户端首次连接时,服务端返回SSE端点的URI;后续所有请求通过POST发送,响应通过SSE事件流返回。


以 Python 实现为例:

服务端:

https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/server/sse.py

# Create an SSE transport at an endpoint
sse = SseServerTransport("/messages/")

# Create Starlette routes for SSE and message handling
routes = [
    Route("/sse", endpoint=handle_sse),
    Mount("/messages/", app=sse.handle_post_message),
]

# Define handler functions
async def handle_sse(request):
    async with sse.connect_sse(
        request.scope, request.receive, request._send
    ) as streams:
        await app.run(
            streams[0], streams[1], app.create_initialization_options()
        )

# Create and run Starlette app
starlette_app = Starlette(routes=routes)
uvicorn.run(starlette_app, host="0.0.0.0", port=port)

在 routes 中,只有 Route("/sse", endpoint=handle_sse) 是用于长连接的。这个路由设置了一个 SSE (Server-Sent Events) 的长连接终端点,用于持续向客户端推送消息。

Mount("/messages/", app=sse.handle_post_message) 则是用于处理客户端发送的 POST 请求,这通常是短连接。

四、协议扩展性

1、自定义传输机制:

MCP允许开发者实现其他传输方式(如WebSocket、gRPC),但需满足JSON-RPC 2.0的消息格式和生命周期管理。

如需实现双向实时通信(如游戏或双向聊天),就需要基于 MCP 自定义其他传输(如 WebSocket)。

2、远程连接支持:

当前MCP官方仅支持本地通信,远程连接功能(如OAuth认证、服务发现)计划在2025年路线图中实现。

五、总结与选择建议

1、选择stdio的场景:

本地调试、资源受限环境、简单流水线任务(如grep | awk式处理)。

2、选择SSE的场景:

需要低延迟、跨网络通信、多客户端订阅(如实时日志推送、分布式微服务)