MCP协议设计与实现-第21章 设计模式与架构决策

《MCP 协议设计与实现》完整目录

第21章 设计模式与架构决策

"Patterns are not invented, they are discovered --- in the recurring decisions that successful systems make when facing the same fundamental tensions."

:::tip 本章要点

  • 从全书内容中提炼六大核心设计模式
  • 理解每个模式解决的根本性张力(tension)
  • 掌握模式间的协同关系与互相强化
  • 认识 MCP 设计决策对未来协议演进的约束与启示
  • 建立可迁移到其他系统设计的架构决策框架 :::

21.1 模式的发现

回顾全书 21 章的旅程------从 MCP 的诞生动机(第 1 章),到架构全景(第 2 章),到 JSON-RPC 基础(第 3 章),到生命周期管理(第 4 章),到三大原语的深入分析(第 5-7 章),到两大 SDK 的实现解读(第 8-11 章),到传输层的多重选择(第 12-14 章),到安全与授权(第 15 章),到发现与注册(第 16 章),到反向通道与配置管理(第 17-18 章),到真实产品中的落地(第 19 章),再到亲手构建 Server(第 20 章)------我们积累了足够的材料来做一件更深层的事:识别贯穿整个协议的设计模式

这些模式不是凭空创造的规则,而是从源码、规范和实践中反复出现的决策逻辑中提炼出来的。它们回答的不是"MCP 做了什么",而是"MCP 为什么这样做"。

mindmap root((MCP 设计模式)) 协议层 能力协商 版本演进策略 传输抽象 语义层 三原语模型 控制平面分离 安全层 安全即默认 工程层 渐进式特性暴露

21.2 模式一:能力协商(Capability Negotiation)

21.2.1 解决的张力

协议需要稳定 (Client 和 Server 可能由不同团队在不同时间开发),但功能需要演进(新特性不断被添加)。如何让新旧版本共存?

21.2.2 模式描述

MCP 的初始化握手不仅是"我是谁"的自我介绍,更是"我能做什么"的能力宣告。Client 和 Server 各自声明自己支持的特性集合,双方在交集内工作:

graph LR subgraph "Client 能力" CR["roots"] CE["elicitation
(form + url)"] CS["sampling"] end subgraph "Server 能力" ST["tools"] SR["resources
(subscribe)"] SP["prompts"] SL["logging"] SC["completions"] end subgraph "协商结果" N1["Client 提供 roots 信息 → Server 可查询"] N2["Client 支持 elicitation → Server 可提问"] N3["Server 提供 tools → Client 可调用"] N4["Server 支持 logging → Client 可设置级别"] end CR --> N1 CE --> N2 ST --> N3 SL --> N4 style CR fill:#3b82f6,color:#fff,stroke:none style CE fill:#3b82f6,color:#fff,stroke:none style CS fill:#3b82f6,color:#fff,stroke:none style ST fill:#10b981,color:#fff,stroke:none style SR fill:#10b981,color:#fff,stroke:none style SP fill:#10b981,color:#fff,stroke:none style SL fill:#10b981,color:#fff,stroke:none style SC fill:#10b981,color:#fff,stroke:none

21.2.3 在 MCP 中的体现

这个模式贯穿全书多个章节:

  • 第 4 章(生命周期)initialize 请求和响应中的 capabilities 字段是整个协商的起点
  • 第 5 章(Tools) :Server 声明 tools 能力后,Client 才能调用 tools/listtools/call
  • 第 6 章(Resources)resources.subscribe 是可选的子能力,Client 只在 Server 声明时才订阅
  • 第 17 章(Sampling) :Server 必须检查 Client 是否声明了 sampling 能力
  • 第 18 章(Elicitation) :精细到区分 formurl 两种子模式的声明

SDK 中 enforceStrictCapabilities 选项控制是否严格执行能力约束------开发阶段可以关闭以便调试,生产环境必须开启。

21.2.4 可迁移的洞察

能力协商模式适用于任何需要渐进式兼容 的系统。它的核心智慧是:不要假设对方的能力,问了再用。这比版本号更灵活------版本号是线性的,能力集合是多维的。一个 Server 可以支持 Tools 但不支持 Resources,这种组合用版本号无法表达。HTTP/2 的 SETTINGS 帧、TLS 的密码套件协商、WebSocket 的子协议选择,都是同一个模式的不同实例。

21.3 模式二:传输抽象(Transport Abstraction)

21.3.1 解决的张力

协议逻辑应该与通信方式解耦 (同样的 Tool 定义,不应因为从 stdio 切换到 HTTP 而改变),但不同部署场景需要不同的传输特性(本地进程用 stdio 更简单,远程服务需要 HTTP 的鉴权和重连)。

21.3.2 模式描述

MCP 定义了一个极简的 Transport 接口------只有发送消息、接收消息、关闭连接三个操作------然后在此之上构建了所有协议逻辑。具体的传输实现是可插拔的:

typescript 复制代码
// TypeScript SDK 的 Transport 接口(简化)
interface Transport {
  start(): Promise<void>;
  send(message: JSONRPCMessage): Promise<void>;
  close(): Promise<void>;
  onmessage?: (message: JSONRPCMessage) => void;
  onclose?: () => void;
  onerror?: (error: Error) => void;
}

任何实现了这个接口的对象都可以作为传输层------这意味着你可以在 Electron IPC、gRPC、甚至蓝牙上运行 MCP 协议,而无需修改任何上层代码。Protocol 类只调用 transport.send() 和监听 transport.onmessage,完全不知道消息是通过 stdin 还是 HTTP 传输的。

21.3.3 在 MCP 中的体现

  • 第 12 章(stdio):最简单的传输,一行命令启动 Server,通过标准输入输出通信
  • 第 13 章(Streamable HTTP):支持无状态部署、会话管理、SSE 推送的现代 HTTP 传输
  • 第 14 章(SSE + WebSocket):补充传输方式,各有适用场景
  • 第 20 章(构建 Server):同一套 Server 代码通过切换传输层支持本地和远程两种部署模式

21.3.4 可迁移的洞察

传输抽象的核心启示是分层的纪律:协议层只依赖抽象的消息收发接口,永远不引用具体的传输细节。这需要在设计之初就明确分层边界,之后严格执行。一旦协议层"泄露"了对特定传输的假设(比如依赖 HTTP 的 Header 机制),抽象就会被破坏。LSP(Language Server Protocol)也采用了同样的设计,这不是巧合------MCP 的设计者明确将 LSP 视为先驱。

21.4 模式三:三原语模型(Three-Primitive Model)

21.4.1 解决的张力

AI Agent 需要灵活地与外部世界交互 (读数据、执行操作、获取指导),但交互模式必须标准化(否则每个 Server 都自创接口,生态无法形成)。

21.4.2 模式描述

MCP 将 Server 能提供的一切抽象为三种原语,区分标准不是数据类型,而是谁控制何时使用

原语 隐喻 控制方 典型交互
Tools 手(执行能力) LLM 决定调用 tools/call
Resources 眼(感知能力) 应用程序控制 resources/read
Prompts 脑(知识模板) 用户选择 prompts/get

控制方的不同直接决定了安全模型:

  • Tools 需要权限检查------LLM 的决策是概率性的,可能误调用
  • Resources 不需要额外权限------应用程序自己选择加载什么
  • Prompts 需要用户确认------用户主动触发的操作

21.4.3 在 MCP 中的体现

  • 第 5 章(Tools):工具是 MCP 最活跃的原语,从简单的 API 调用到复杂的多步操作
  • 第 6 章(Resources):资源提供了声明式的数据访问,支持订阅和变更通知
  • 第 7 章(Prompts):提示模板定义了可复用的对话结构,引导 LLM 的行为
  • 第 20 章(构建 Server):实战中三者的协同------Prompt 引导 LLM,LLM 决定调用 Tool,Tool 执行中读取 Resource

21.4.4 可迁移的洞察

三原语模型的深层智慧是按控制权分类,而非按数据类型分类。在设计任何 Agent 交互框架时,区分"谁决定何时使用"比区分"这是什么类型的数据"更有架构价值。数据结构可以相同(三个原语都返回 content 数组),但控制语义的差异决定了完全不同的安全和交互模型。

21.5 模式四:渐进式特性暴露(Progressive Feature Disclosure)

21.5.1 解决的张力

协议功能丰富 (Elicitation、Sampling、Progress、Completion 等),但入门门槛应该低(一个最简单的 Server 应该几十行代码就能写出来)。

21.5.2 模式描述

MCP 的每个特性都是可选的。一个 Server 可以只暴露一个 Tool,不支持 Resources、不支持 Prompts、不支持 Completion、不支持 Logging------它仍然是一个完全合法的 MCP Server。随着需求增长,开发者可以逐步添加更多特性:

graph TB L1["Level 1: 最小可用
一个 Tool + stdio 传输"] --> L2["Level 2: 丰富交互
+ Resources + Prompts"] L2 --> L3["Level 3: 智能补全
+ Completion + Tool Annotations"] L3 --> L4["Level 4: 操作反馈
+ Progress + Logging"] L4 --> L5["Level 5: 反向通道
+ Elicitation + Sampling"] L5 --> L6["Level 6: 远程部署
+ HTTP 传输 + OAuth"] style L1 fill:#10b981,color:#fff,stroke:none style L2 fill:#3b82f6,color:#fff,stroke:none style L3 fill:#6366f1,color:#fff,stroke:none style L4 fill:#8b5cf6,color:#fff,stroke:none style L5 fill:#ec4899,color:#fff,stroke:none style L6 fill:#f59e0b,color:#fff,stroke:none

21.5.3 在 MCP 中的体现

  • 第 8 章(TypeScript Server SDK)McpServer 高级 API 让创建基本 Server 只需十几行代码
  • 第 10 章(Python Server SDK)@server.tool() 装饰器让工具定义简洁到极致
  • 第 18 章(Elicitation/Roots):这些高级特性只在 Server 需要时才涉及
  • 第 20 章(构建 Server):从最简开始,逐步添加 Completion、Progress 等特性

21.5.4 可迁移的洞察

渐进式暴露的关键是正交性------每个特性应该独立于其他特性工作。如果添加 Completion 支持要求同时实现 Resources,那就不是渐进式的。MCP 做到了几乎完全的正交性:任何特性组合都是合法的。这对协议设计的启示是:在添加新特性时,始终问"这个特性是否可以独立于其他所有特性使用?"

21.6 模式五:安全即默认(Security by Default)

21.6.1 解决的张力

开放性是生态繁荣的前提 (任何人都能写 Server),但安全是用户信任的基础(恶意或有缺陷的 Server 不应该损害用户)。

21.6.2 模式描述

MCP 在协议层面内建了多层安全机制,而不是把安全作为"可选的最佳实践"留给实现者:

  1. 人在回路(Human-in-the-loop):工具调用默认需要用户确认,Elicitation URL 需要用户同意
  2. 最小权限原则:能力协商确保只有声明支持的特性才会被使用
  3. 安全分级:Form Mode 禁止收集密码和 API 密钥,URL Mode 确保敏感数据不经过 Client
  4. 传输安全:远程部署强制 OAuth 2.1 + PKCE,Host Header 验证防止 DNS 重绑定
  5. 信息隔离:Server 默认看不到对话历史,不能访问其他 Server 的数据
  6. 环境隔离:stdio 传输默认只继承白名单环境变量,防止凭证泄露

21.6.3 在 MCP 中的体现

  • 第 5 章(Tools) :Tool Annotations(readOnlyHintdestructiveHint)帮助 Client 做权限决策
  • 第 15 章(OAuth):MCP 授权规范基于 OAuth 2.1,强制 PKCE,禁止隐式授权流
  • 第 18 章(Elicitation):Form Mode 禁止收集敏感信息,URL Mode 必须在安全浏览器中打开
  • 第 19 章(Claude Code):MCP 工具与内置工具冲突时内置优先,防止第三方覆盖核心功能

21.6.4 可迁移的洞察

安全即默认意味着不安全的行为应该比安全的行为更难实现 。在 MCP 中,发送敏感数据通过 Form Mode 是被规范禁止的,你必须使用更复杂但更安全的 URL Mode------增加的摩擦是有意为之的。这个理念可以这样概括:让做正确的事比做错误的事更容易

21.7 模式六:协议版本策略(Protocol Versioning Strategy)

21.7.1 解决的张力

协议需要演进 (新功能、修复设计缺陷),但已部署的实现不能被破坏(百万级 Client 和 Server 不可能同时升级)。

21.7.2 模式描述

MCP 使用日期格式的版本标识(如 2025-06-182025-11-25),而非语义化版本号。初始化时 Client 提出支持的版本,Server 响应实际使用的版本。如果不兼容,连接直接失败------这是有意的设计:宁可连接失败,也不要在不兼容的状态下运行

21.7.3 日期版本的哲学

日期版本比语义化版本更适合协议:

  • 协议没有"补丁"的概念------每个版本都是完整的规范
  • 日期天然有序,不需要比较主版本号和次版本号
  • 避免了"这个 breaking change 是 major 还是 minor"的无休止争论
  • 每个版本号就是一个明确的时间快照,对开发者更直观

21.7.4 与能力协商的配合

版本号 + 能力协商构成了双重兼容性策略:

  • 版本号处理宏观的协议演进(消息格式变化、新方法添加)
  • 能力协商处理微观的特性差异(同一版本内,不同实现支持不同特性)

这就像 HTTP 版本(1.1、2、3)与 HTTP Headers 的关系:版本决定了基本的通信规则,Headers 协商具体的行为。两个层次各司其职,互不干扰。

21.8 模式间的协同

这六个模式不是孤立的。它们形成了一个互相强化的系统:

graph TB CN["能力协商"] --> |"决定可用特性"| PFD["渐进式暴露"] CN --> |"确保安全特性生效"| SBD["安全即默认"] TA["传输抽象"] --> |"任何传输上工作"| TPM["三原语模型"] TA --> |"HTTP 传输启用"| SBD TPM --> |"按控制权分层"| PFD PFD --> |"每层独立演进"| PVS["版本策略"] SBD --> |"安全约束纳入规范"| PVS PVS --> |"新版本添加新能力"| CN style CN fill:#3b82f6,color:#fff,stroke:none style TA fill:#8b5cf6,color:#fff,stroke:none style TPM fill:#ec4899,color:#fff,stroke:none style PFD fill:#10b981,color:#fff,stroke:none style SBD fill:#f59e0b,color:#fff,stroke:none style PVS fill:#6366f1,color:#fff,stroke:none

考虑一个具体的例子:当 MCP 在 2025-11-25 版本中引入 URL Mode Elicitation 时------

  • 版本策略让旧 Client 可以安全地拒绝新版本的连接
  • 能力协商 让新 Client 可以声明是否支持 url 模式
  • 安全即默认要求敏感数据必须通过 URL Mode,不能走 Form Mode
  • 渐进式暴露确保不需要 URL Mode 的 Server 完全不受影响
  • 传输抽象保证这个特性在 stdio 和 HTTP 上都能工作
  • 三原语模型中,URL Elicitation 自然地嵌入到 Tool 执行流程中

六个模式像齿轮一样咬合,每个都让其他模式更有效。

21.9 MCP 的设计权衡

没有完美的设计。每个模式背后都有权衡,诚实地记录这些权衡是本章的责任。

21.9.1 能力协商的代价

初始化握手增加了首次连接的延迟。每个 Client 和 Server 都需要维护能力映射表。但这个代价换来了无需版本锁步升级的自由度------对于一个开放生态来说,这是值得的。

21.9.2 三原语的边界模糊

在实践中,Tool 和 Resource 的边界有时不清晰。"获取文件内容"是 Resource 还是 Tool?MCP 规范的回答是看控制方------如果是应用程序自动加载上下文,用 Resource;如果是 LLM 决定需要获取,用 Tool。这个区分在概念上清晰,但在实现中需要开发者做出判断。

21.9.3 安全的摩擦

安全机制增加了开发复杂度。一个简单的 Server 如果需要收集 API 密钥,必须实现 URL Mode Elicitation 的完整流程------包括 HTTPS 页面、用户身份验证、回调处理。对于小型项目来说,这可能显得过重。但协议设计者做出了明确的取舍:宁可让少数开发者多写代码,也不让多数用户面临安全风险

21.9.4 JSON-RPC 的局限

MCP 选择 JSON-RPC 2.0 作为基础,获得了简单性和广泛的语言支持,但也继承了它的限制:没有原生的流式传输(需要 SSE 补充)、没有二进制消息(大文件传输效率较低)、没有内建的请求多路复用。这些限制通过传输层的扩展来弥补,但增加了整体复杂度。

21.10 全书架构决策总结

决策 选择 放弃的方案 理由 相关章节
消息格式 JSON-RPC 2.0 REST, gRPC, 自定义 简单、双向、人类可读 第 3 章
版本号格式 日期 (2025-11-25) semver 避免兼容性歧义 第 4 章
交互原语 Tool/Resource/Prompt 统一 Action 模型 按控制权分类更精确 第 5-7 章
本地传输 stdio Unix Socket, Named Pipe 跨平台、零配置 第 12 章
远程传输 Streamable HTTP REST+WebSocket 单连接、渐进增强 第 13 章
认证 OAuth 2.1 + PKCE API Key, mTLS 无需预存关系、支持作用域 第 15 章
Schema 验证 JSON Schema 2020-12 TypeScript 类型、Protobuf 语言无关、工具丰富 第 5 章
Server 发现 配置文件 + OAuth 发现 DNS-SD, mDNS 简单、显式、安全 第 16 章

21.11 面向未来的思考

MCP 的设计模式揭示了几个面向未来的方向:

Agent-to-Agent 通信。当前 MCP 是 Client-Server 模型,但 AI Agent 生态正在走向 Agent 间直接协作。MCP 的传输抽象和能力协商机制可以自然地扩展到 Agent-to-Agent 场景。

多模态原语。当前的三原语模型主要处理文本和简单的二进制数据。随着多模态 AI 的发展,视频流、实时音频、3D 场景等数据类型可能需要新的原语或现有原语的扩展。

去中心化发现。当前的 Server 发现依赖配置文件或中央注册表。未来可能出现基于 DNS-SD、mDNS 或区块链的去中心化发现机制。

长生命周期任务。MCP 的 Progress 和 Cancellation 机制为长操作提供了基本支持,但随着 Agent 执行越来越复杂的任务(几小时甚至几天),可能需要更完善的任务管理原语------MCP 规范中已出现的实验性 Tasks 特性就是这个方向的探索。

21.12 全书总结

MCP 不只是一个协议规范------它是一套关于如何让 AI 系统安全、可靠、可扩展地与外部世界交互的设计思想。

从第 1 章到第 21 章,我们完成了一次完整的旅程:

  • 为什么:AI Agent 需要标准化的外部交互协议(第 1 章)
  • 是什么:基于 JSON-RPC 的 Client-Server 架构,三大原语,能力协商(第 2-7 章)
  • 怎么实现:TypeScript 和 Python SDK 的深度解读(第 8-11 章)
  • 怎么通信:从 stdio 到 Streamable HTTP,传输层的选择与权衡(第 12-14 章)
  • 怎么安全:OAuth 授权、Elicitation 安全隔离、工具注解(第 15、18 章)
  • 怎么发现:配置文件、注册表、动态发现(第 16 章)
  • 怎么协作:Sampling、Elicitation、Progress 等反向通道(第 17-18 章)
  • 怎么落地:Claude Code 的 12 万行实战经验(第 19 章)
  • 怎么构建:从零到生产级 Server 的完整开发流程(第 20 章)
  • 为什么这样:六大设计模式与架构决策(本章)

MCP 协议仍在快速演进。但本书覆盖的核心概念和设计模式是稳定的------它们不会随版本更新而失效,因为它们反映的是更深层的系统设计智慧。理解了这些模式,你不仅能用好 MCP,更能在面对下一个协议设计问题时,做出更好的架构决策。

模型是引擎,协议是道路。好的道路不限制车速,但确保每辆车都到达正确的目的地。


全书源码参考

  • MCP 规范:https://github.com/modelcontextprotocol/specification
  • TypeScript SDK:https://github.com/modelcontextprotocol/typescript-sdk
  • Python SDK:https://github.com/modelcontextprotocol/python-sdk
相关推荐
杨艺韬2 小时前
MCP协议设计与实现-第20章 从零构建一个生产级 MCP Server
agent
杨艺韬2 小时前
MCP协议设计与实现-第18章 Elicitation、Roots 与配置管理
agent
杨艺韬3 小时前
MCP协议设计与实现-第17章 sampling
agent
杨艺韬3 小时前
MCP协议设计与实现-第16章 服务发现与客户端注册
agent
杨艺韬3 小时前
MCP协议设计与实现-第8章 TypeScript Server 实现剖析
agent
杨艺韬3 小时前
MCP协议设计与实现-第04章 生命周期与能力协商
agent
杨艺韬3 小时前
MCP协议设计与实现-第14章 SSE 与 WebSocket
agent
杨艺韬3 小时前
MCP协议设计与实现-第6章 Resource:结构化的上下文注入
agent
杨艺韬3 小时前
MCP协议设计与实现-第10章 Python Server 实现剖析
agent