TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

MCP 传输层详解

MCP 还能把消息映射到 Azure Event Grid、Event Hubs 等消息服务上。这里讲清何时值得自定义传输、无状态要求和互通性代价。

这篇适合谁、读完能做到什么

适合已经会用 MCP 写服务器或客户端,并且需要把 MCP 接进企业现有消息系统的开发者。读完你能说清自定义传输必须满足哪些条件,能按 MCP 2026-07-28 规范的无状态要求设计一条基于 Azure 消息服务的传输,并知道它在互通性上付出了什么代价。

传输层是什么,自定义传输意味着什么

MCP 的传输层决定 JSON-RPC 消息怎样在客户端和服务器之间传递。标准传输只有两种:stdio 和 Streamable HTTP[1]。自定义传输是可选的,它是实现者自己定义的一种映射方式,而且两端必须对这种映射达成一致[1]。

企业里常见的动机是复用。组织已经有一套消息基础设施,希望 MCP 直接跑在上面,不再另建一套通道[1]。代价同样清楚:只实现标准传输的 MCP 宿主和 SDK 可能无法和自定义映射对接,互通性会下降[1]。因此动手前先问一句:标准的 Streamable HTTP 能不能满足需求?能满足,就不必自定义。

Azure Event Grid 和 Azure Event Hubs 在这里是两种架构模式,不是标准 MCP 传输[1]。下面的内容都以它们为例,其中的思路也适用于其他消息系统。

2026-07-28 规范对传输提出的要求

MCP 2026-07-28 规范的核心是无状态。协议层没有会话,每个请求都必须自包含,也没有握手可以替它携带状态[1]。这对自定义传输影响很大。无论消息经过哪个函数实例、哪个分区,接收端都只能依靠消息本身理解它,所以不能依赖会话亲和(session affinity),也不能假设同一会话的消息按顺序到达[1]。

规范要求可以归纳为几条:

  • 消息格式是带 MCP 扩展的 JSON-RPC 2.0,响应通过 JSON-RPC 的 id 与请求对应[1]。
  • 传输必须优雅地处理连接故障,并且必须支持安全通信[1]。
  • 每个请求都要带上协议版本、能力声明和身份信息[1]。

标准的 Streamable HTTP 要求携带 Mcp-Method 头,需要时还要带 Mcp-Name 头。非 HTTP 的传输如果希望中间设备不解析 JSON-RPC 正文也能完成路由,就必须另外约定一套等价的映射,并且事先写明[1]。

准备:先定映射,再选 Azure 服务

Azure Event Grid 是无服务器的事件路由服务,适合事件驱动的 MCP 架构[1]。Azure Event Hubs 面向高吞吐的实时流式数据,适合低延迟、高消息量的场景[1]。两者的侧重点不同:Event Grid 偏向把事件分发给订阅者,Event Hubs 偏向按分区消费的连续数据流。

如果只是几个内部客户端,建议先用 Streamable HTTP 跑通业务,再决定要不要引入消息服务。一开始就套上 Event Grid,会把本来可以用标准传输解决的问题变成互通性负担。

选型之前,先回答两个问题。第一,每个 MCP 请求是一次性的事件,还是持续的数据流?第二,响应要不要回到发出请求的那个客户端?答案决定你该用哪一种服务,以及接收端要怎样关联请求和响应。

动手前还要准备 Azure 订阅、选定区域,并查清服务限额。建议按自己的需求和服务限额验证方案,这一步不能省[1]。

上手步骤

  1. 先把两端的映射写成文档,包括消息放进事件的方式、用哪些属性标识方法和 id、事件类型叫什么。两端必须完全一致,否则无法互通[1]。
  2. 实现发送方。Event Grid 示例把每条消息封装成事件,subject 为 mcp/{clientId},事件类型为 MCP.MessageReceived,数据版本为 1.0[1]。TypeScript 版本的发送方法如下:
    async sendMessage(message: McpMessage): Promise<void> {
        const event = {
            id: crypto.randomUUID(),
            source: `mcp-client-${this.clientId}`,
            type: "MCP.MessageReceived",
            time: new Date(),
            data: message
        };
        
        await this.publisher.sendEvents([event]);
    }
  1. 配置接收方。Event Grid 是推送模式。示例中 C# 版本的 ReceiveMessageAsync 直接抛出 NotImplementedException,注释说明接收端要用 Azure Functions 的 Event Grid trigger 来实现[1]。
  2. 在接收函数里反序列化事件数据,交给 MCP 服务器处理,再通过同一个传输把响应发回去[1]。这一步要确认发送方能把响应事件关联回原请求。

Event Hubs 的几个细节

  • 每个事件都带有属性。C# 示例为每条消息设置 MessageType、MessageId 和 Timestamp,消费端靠它们识别消息[1]。
  • 投递语义。TypeScript 示例在处理完一条消息后调用 updateCheckpoint,目的是实现至少一次投递(at-least-once)[1]。同一条消息可能被处理两次,所以处理逻辑要做成幂等的,比如先按 MessageId 查重再执行。
  • 错误处理。同一个示例捕获了处理错误,并用 console.error 记录[1]。错误记下来之后没有再抛出,也没有进入重试或隔离流程。生产环境应当配合死信队列(dead letter queue)处理失败消息,这也是消息系统里的常规做法[1]。
  • 分区。有一个分区示例按客户端 ID 分区,注释写的是「for session affinity」[1]。这与上面的无状态要求直接冲突。照抄之前要先想清楚:分区是为了吞吐,还是为了会话,二者不能混为一谈。

密钥和安全

生产环境应当使用托管标识(managed identity),避免把连接字符串写进代码或配置[1]。其中一个示例从 Azure Key Vault 读取名为 EventGridAccessKey 和 EventGridTopic 的密钥,再据此创建 Event Grid 传输实例[1]。密钥放在 Key Vault 里,代码只拿到需要的值,不把它们写死在源码中。

测试

集成测试可以用 Testcontainers 的 EventHubsContainer 启动一个测试用的事件中心,然后在上面做一次消息往返,检查接收到的消息 id 与发送的一致[1]。这种测试能发现映射和序列化上的问题,但它不能代替对规范的逐条核对。建议先写好映射文档,再用这种测试去验证它。

测试可以分三层看。第一层是序列化:消息经过事件封装再反序列化后,内容要和原消息一致。第二层是映射:事件类型、subject 和属性要和映射文档逐项相符。第三层是端到端:发出请求后,响应能否按 id 找回原请求。三层都通过,才能说这套映射可以投入使用。

常见问题

  • 客户端连不上:多半是映射没有对齐。只实现标准传输的宿主不认识自定义映射,这是设计上的代价,不是配置错误[1]。
  • 消息重复:Event Hubs 的至少一次投递会带来重复消息,用消息 id 去重,或者让处理逻辑本身幂等[1]。
  • 示例能直接跑吗:文中的代码更像结构草图。要接入真实项目,还需要补全接口定义、重试和监控,并做健康检查[1]。
  • 发送偶尔失败:消息要经过网络才能到达消息服务,失败是常态。规范要求传输能优雅处理连接故障,所以发送端需要重试策略;重试会产生重复,前面提到的幂等处理正好与之配套[1]。
  • 请求和响应对不上:检查两端对 JSON-RPC id 的处理是否一致,响应必须能通过 id 找回原请求[1]。

下一步

协议行为以 MCP 2026-07-28 规范为准[2]。Azure 服务的具体配置,请对照 Azure Event Grid 和 Azure Event Hubs 的官方文档确认。

本文改编自 Microsoft 开源课程《MCP for Beginners》中关于自定义传输的高级主题[1],原文以 MIT 许可证发布。

来源

  1. 1
  2. 2
    MCP Specification 2026-07-28Model Context Protocol

引用本页: MCP 传输层详解. TellWorld, https://www.tellworld.net/zh/practice/practice-mcp-transport. 更新于 2026-11-14

相关动态