• 约 32 分钟阅读

更新于

理解 MCP:协议架构、交互语义与互操作边界

从参与者、消息与能力模型到传输、授权和规范性要求,解释 MCP 如何建立共同交互契约,以及这一契约的保证边界。

目录

理解 MCP:协议架构、交互语义与互操作边界

摘要

MCP(Model Context Protocol)不是模型推理算法,也不只是工具描述格式,而是模型应用与外部能力提供者之间的交互契约。本文从参与者和控制权出发,分析消息关联、逐请求声明、能力发现、多轮输入、传输取消及授权之间的关系。核心论点是:MCP 将异构系统的接入约定转化为可检查的协议行为,但结构合法、通信完成、业务成功和安全执行仍是不同层次的命题。理解这些层次,才能判断互操作究竟成立在哪里,又在哪些边界需要宿主和业务系统承担责任。124

本文以 MCP 2026-07-28 规范为依据;涉及旧版机制时单独标注。6

1. 设计问题与抽象位置

设想一个研究助手:用户要比较两份报告,应用需要取得正文,确定比较口径,调用统计程序,再让模型结合证据解释差异。困难不只在于“调用某个 API”。不同能力提供者可能各自规定如何列出可用操作、如何描述参数、怎样表达结果,以及执行途中怎样向用户索取缺失信息。若每个宿主都重新约定这些行为,接入逻辑便与具体业务缠在一起。MCP 的目标,是让发现和使用能力的交互具有共同结构,而不是替每个业务领域规定正确答案。12

这也解释了它与相邻技术的关系。OpenAPI 3.1.1 是面向 HTTP API 的、与编程语言无关的接口描述规范;它描述服务提供什么接口,不等同于模型应用的一整套交互行为。32 以 OpenAI 的 function calling 为例,模型产生调用意图和参数,应用执行代码,再把工具输出交回模型;这是应用与模型之间的调用循环。33 MCP 则约定客户端与能力服务端之间如何发现、请求和交换结果。一个宿主可以把 MCP 工具转换成模型接口中的函数描述,再把模型建议转换回 MCP 请求。这是分层组合,不是三者相互替代;也不能把某一家模型 API 的具体格式当成所有 function calling 的统一标准。21433

可以把研究助手拆成四层:任务层决定比较目标和评价标准;能力层把内容、动作和交互模板抽象成可发现对象;消息层规定请求、结果及补充输入如何关联;传输层规定消息怎样抵达对端。授权横跨这些层次,却不由任何一份工具参数 schema 自动完成。24 这一分层的价值在于允许替换模型或后端,而尽量不重写中间的交互契约;代价是业务语义仍需解释。“增长率”按何种口径计算,不会因为字段名统一就自动一致。

如果把“比较报告”整个过程只暴露为一个工具,宿主获得的控制粒度就较粗;如果分别暴露内容读取、口径选择和统计动作,宿主便能独立选择上下文、核对中间结果,但需要承担更多编排。MCP 的三类能力为这种拆分提供表达手段,并不规定唯一拆法。141516 因而协议统一的应是每一步怎样被描述与交换,而不是强迫所有服务端拥有相同内部结构。评价一种接入设计时,还要问它是否把有意义的决定留在适当一方,而不只是问它是否成功返回 JSON。

2. 参与者、控制权与信息流

Host(宿主)是用户使用的应用,协调模型、管理上下文并执行同意与安全策略。Client(客户端)是宿主内面向服务端的协议组件,负责编码请求、关联响应和处理通知。Server(服务端)提供工具、资源或提示模板,可以包装本地文档、数据库或远程业务系统。宿主管理多个客户端,每个客户端与一个服务端建立对应关系;这种逻辑关系不意味着必须存在协议会话。24

图1:参与者、信息流与控制边界

模型并不直接持有 MCP 连接。它可以建议“调用统计工具”,但请求是否发送、携带哪些报告片段、结果是否进入下一轮上下文,都要经过宿主及客户端。服务端负责执行已暴露的操作,而不因接入就获得整段对话、其他服务端数据或宿主全部权限。图1将模型交互与 MCP 链路分开,正是为了区分行动建议权、调用决定权和实际执行权。214

沿着案例观察,有三条不同的信息流。用户意图进入宿主,形成待解决的任务;资源内容和工具结果返回宿主,成为候选上下文;能力描述则帮助宿主和模型判断有哪些行动可以选择。它们都可能表现为文本,却具有不同用途。报告正文中的“请上传全部文件”是待处理内容,不会因为来自资源接口就变成宿主策略。协议定义对象和传递方式,宿主仍须决定如何解释与隔离外部内容。12

这种架构把跨服务端组合留给宿主。例如,文档服务端只提供报告,统计服务端只接收必要数据;宿主控制二者之间的信息转移。由此产生的互操作不是“所有参与者互相信任”,而是在职责分离下允许有限通信。隔离、最小披露和用户确认是实现这些边界的责任,并非画出几个组件框后自然成立的性质。12

3. 消息模型与交互规则

MCP 使用 JSON-RPC 2.0 的请求、响应和通知。请求中的 method 指定操作,params 承载参数,id 关联响应;成功响应使用 result,协议失败使用 error,二者不能同时出现。通知没有 id,接收方不得发送 JSON-RPC 响应。HTTP 对通知 POST 的状态码确认属于传输层,并不改变通知在消息层无需应答的语义。3410

当前 MCP 进一步要求请求 ID 为字符串或整数,禁止 null,并禁止与本方尚未收到响应的请求 ID 重复。结果必须含 resultType:complete 表示返回最终结果,input_required 表示还需输入。客户端把缺失的 resultType 视作 complete,是接收旧版本结果的兼容规则,不是当前发送端可以省略该字段的理由。45

以下是调用只读分析工具的示意消息。analyze_report 是案例定义的工具名,协议方法是 tools/call;这一区别把业务操作与协议操作分开。14

{
  "jsonrpc": "2.0",
  "id": "analysis-1",
  "method": "tools/call",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {
        "elicitation": { "form": {} }
      },
      "io.modelcontextprotocol/clientInfo": {
        "name": "ResearchClient",
        "version": "1.0.0"
      }
    },
    "name": "analyze_report",
    "arguments": { "uri": "report://annual/2025" }
  }
}

当前模型没有必需的 initialize 握手。每个请求都必须在 params._meta 中声明协议版本和客户端能力,即使能力对象为空也不能省略;clientInfo 则是建议提供的可选、自报软件信息,不是认证凭据。逐请求声明使服务端不必从先前连接历史推断本次可以使用哪些客户端功能。456

服务端必须实现 server/discover,返回支持版本及服务端能力,客户端可以先调用,也可以直接发具体请求。不支持版本时,服务端必须返回 -32022 和支持版本列表。发现回答“对方提供什么”,逐请求声明回答“本次客户端能处理什么”,前者并非后者的替代品。67 即使两个请求走同一条连接,也不能把上一次声明的能力默认为下一次仍有效;否则请求转移到另一实例时就会改变含义。4

无状态的准确含义,是服务端独立处理请求,不依赖连接历史建立协议上下文;它不禁止业务数据库或任务记录。跨请求状态必须由客户端显式传入标识符。分析记录可以长期存在,但不能用“此连接上最后一次分析”隐式定位。4 历史对照:2025-11-25 使用 initialize、初始化响应和 notifications/initialized 建立交互前提;不能仅把其版本字符串换成新日期,就认为完成了迁移。8

合法 JSON 仍不等于合法 MCP。JSON-RPC 的顶层批数组不在当前 MCP 消息联合类型和单消息传输规则内,独立请求的并发不等于 batch。359 异常路径还存在细节差异:JSON-RPC 对无法识别 ID 的错误规定 id: null,当前 MCP 错误结构则允许省略 ID、排除 null。因此不能宣称 MCP 原封不动保留了底层标准的全部选择;互操作判断必须落到所选修订版的具体消息与绑定。3410

请求 ID 的作用也应与执行顺序分开理解。它让客户端在多个未完成请求之间辨认结果,却没有承诺服务端按发送先后完成,更没有把多个操作变成事务。34 如果比较必须等待两份报告都取得,等待关系应由宿主编排;如果第二步必须针对第一步生成的分析记录,就应传递业务标识。把关联关系误读成顺序保证,会让看似正常的并发请求产生难以定位的业务错误。

4. 能力模型:上下文、工具、提示与双向交互

三类服务端能力分别回答不同问题。Resources(资源)描述可以读取的内容,Tools(工具)描述可以执行的动作,Prompts(提示模板)描述可复用的交互组织方式。它们不是三种名称不同的函数,而是对标识、输出和使用方式作出不同承诺。141516

图2:核心能力、客户端输入与可选扩展

资源以 URI 标识,可通过 resources/list 发现、通过 resources/read 读取。返回内容可以是文本或编码后的二进制;URI 不必是客户端能够直接打开的磁盘路径,file:// 也不保证指向客户端文件系统。资源模板描述参数化的一族 URI,不必穷举全部对象。15 案例中的 report://annual/2025 是内容身份,宿主仍需决定读取多少、如何截取和是否送给模型。可寻址性解决定位,不解决上下文预算。

工具通过名称和输入 schema 描述参数契约,tools/list 用于发现,tools/call 用于执行。结果既可包含文本、图像等内容块,也可在 structuredContent 中返回结构化值;若工具声明了输出 schema,服务端必须使结构化结果符合它,客户端应验证。14 以下仍为示意结果,而非真实统计输出:

{
  "jsonrpc": "2.0",
  "id": "analysis-1",
  "result": {
    "resultType": "complete",
    "content": [{ "type": "text", "text": "报告包含收入与成本两个指标。" }],
    "structuredContent": { "metrics": ["revenue", "cost"] },
    "isError": false
  }
}

这里的 schema 约束结果形状,却不能证明指标提取无误。工具返回资源链接,可以把“执行分析”与“读取详细报告”衔接起来,但链接不会自动变成模型上下文;仍需宿主决定是否继续读取。1415 能力组合因此具有显式边界,而不是把一切输出无条件塞进对话。

提示模板通过 prompts/list 发现,经 prompts/get 接收参数并产生带角色的消息序列。例如“比较两份报告”模板可以组织比较维度,但返回的不是模型已经生成的答案,也不是服务端获得了修改宿主最高优先级策略的权利。16 规范将工具定位为模型控制、资源定位为应用控制、模板定位为用户控制;这是预期交互方式,不是强制界面形态,也不取消宿主的审批责任。模板由服务端编写,与用户决定何时采用它,是两个问题。141516

能力发现还依赖本次访问条件。工具、资源和模板的列表可以随时间或授权变化,却不能因连接不同、或连接上之前调用过什么而暗中变化。141516 因而缓存发现结果时不能把“某用户看见的工具”直接当成所有用户共享的事实。服务发现不是一次永久登记,而是访问能力的当前描述。

当分析缺少比较口径时,服务端需要反向索取信息,但不应直接占有宿主界面。当前规范通过多轮往返请求(MRTR)表达这种双向交互:服务端在响应中返回 input_required,用 inputRequests 描述需要的输入;客户端处理后,以新 ID 重试原操作,附上对应的 inputResponses。允许这种结果的核心方法只有 tools/call、resources/read 和 prompts/get。服务端不得索取客户端在本次能力中未声明支持的输入类型,也不能假定客户端必然继续。17

其中 elicitation(信息征询)面向用户。表单模式给出问题及受限 schema,由客户端呈现,并返回接受、拒绝或取消;它不支持任意复杂嵌套表单。URL 模式则把用户引向带外页面,适合秘密或凭据等不能经客户端表单传递的内容。客户端不得未经同意打开或预取 URL,必须先展示完整 URL;用户同意导航也不表示带外操作已经完成。18

Sampling 让服务端请求客户端协调模型生成,Roots 让客户端声明相关目录或文件范围。两者在本修订版中均已弃用但尚未删除;如仍实现,应遵循当前 MRTR,而不是独立反向 RPC。Roots 是信息性范围提示,不是操作系统沙箱;Logging 也处于弃用状态。192021 与之不同,Tasks、Apps 是可选扩展:前者引入长期任务句柄,后者把工具与交互界面资源联系起来。弃用核心能力与可选扩展不能混为一类,也不能据此推断所有宿主支持后台恢复或界面渲染。222324

5. 传输、执行、失败与取消

传输负责消息边界与交付方式,不替业务定义完成条件。stdio 以子进程标准输入、输出承载换行分隔的消息;一条线上消息必须是一行 JSON,内部换行需要转义,日志不能混进标准输出。Streamable HTTP 则让客户端以独立 POST 发送消息,请求接受 JSON 和 SSE 两类响应;服务端可以一次返回 JSON,也可以打开该请求作用域内的事件流。910

SSE 定义 UTF-8 事件格式,不是模型逐 token 输出的同义词,也不意味着采用浏览器 EventSource 的 GET 与自动恢复行为。当前 MCP 的 SSE 可以先发送相关通知,再发送最终响应;服务端不得在流内发独立 JSON-RPC 请求,反向索取输入必须通过 MRTR。长期变化通知由客户端发起 subscriptions/listen 承载,不能把某次工具调用的进度混进任意订阅流。1012

图3:MRTR 将补充输入分成独立请求

图3中的第一次请求已在返回输入需求后结束,用户思考的时间不再等于原请求持续执行的时间。第二次请求重新携带元数据、原操作参数和补充输入;若服务端返回 requestState,客户端必须原样回传,不得解析修改。这种状态传递允许不同服务实例接续处理,却把完整性验证和重放控制的问题显式带到请求边界。17

因此必须区分请求状态、连接状态和业务状态。请求可以等待,连接可以复用,业务任务可以跨连接存续;三者生命周期不重合。若确实需要持久任务及重连后取结果,Tasks 扩展提供相应任务抽象,但这不是核心 HTTP 连接本身赋予的保证。423

HTTP 还把正文的版本、方法和操作对象镜像到头字段:MCP-Protocol-Version、Mcp-Method,以及用于工具调用、资源读取和模板获取的 Mcp-Name。处理正文的服务端必须检查镜像一致性。10 这使中间设备能够不解析整个 JSON 就路由和观察请求,代价是产生两份必须同步的表示;若网关按头判断权限而服务端按不一致的正文执行,就会出现判断对象与执行对象分离的问题。故前面的 JSON 只是消息体,不是完整 HTTP 请求。

失败同样分层:连接中断表示交付不确定;JSON-RPC error 表示协议操作失败;工具 result 中的 isError: true 表示工具执行错误。业务校验或上游服务失败可以作为工具反馈交给模型修正,而未知方法、无效请求等不能一概冒充成功结果。314 resultType: complete 与 isError 不是同一维度:前者指本次操作已经给出最终结果,后者仍可能说明业务执行失败。

当前 HTTP 关闭 SSE 响应流就是取消该请求的信号;服务端应尽快停止工作,并不得继续发送该请求的消息。stdio 没有可单独关闭的请求流,客户端须用 notifications/cancelled 引用目标 ID。超时应触发相应取消,而非永远等待;取消与完成可能竞态,已发生的副作用不会因此自动回滚。91034 历史对照:2025-11-25 的 HTTP 断连不应自动当成取消,并且可使用会话 ID、独立 GET 流和事件恢复;这些机制不属于当前绑定。13

假设分析工具改为“生成并保存收费报告”,服务端可能已经写入,响应却丢失。再次 POST 并不能证明只执行一次;请求 ID 是响应关联标识,不是业务去重键。HTTP 对非幂等操作的自动重试本就有限制,MCP 无状态也不改变这一点。311 业务需要另行定义幂等键、完成状态查询或补偿策略。可重新连接只说明通信能够重建,不说明重复执行安全。

取消后的“不再等待”与业务上的“确认没发生”尤其不能混淆。前者是客户端结束等待和释放资源的决定,后者需要服务端可观察的业务证据。即使服务端及时收到取消,也可能正处在不能中断的外部提交阶段;规范允许某些无法取消或已完成的 stdio 请求忽略取消通知,并要求双方处理竞态。34 因此界面显示“已请求取消”比无依据地显示“操作已撤销”更准确。这个差别不是措辞保守,而是避免把传输控制信号解释成不存在的事务保证。

6. 信任与授权

能力、身份、权限和同意是不同问题。客户端能显示表单,不证明它是谁;自报的软件名称不构成认证;发现某工具不代表有权执行;持有有效令牌也不必然等于用户同意本次操作的全部细节。协议的职责是使这些关系能够被表达和检查,而不是把它们压成一个“连接成功”的布尔值。1525

MCP 授权框架面向 HTTP,整体为可选;采用时才进入具体规范要求,HTTP 实现应遵循该框架,stdio 不应照搬 HTTP OAuth 流程。25 在此框架内,MCP Server 是资源服务器,授权服务器负责签发令牌。客户端通过受保护资源元数据发现授权服务器,而不是猜测授权端点;这与 server/discover 发现业务能力属于两种发现过程。2628

客户端必须实施 PKCE 并检查支持情况,具备技术条件时使用 S256;授权和令牌请求必须指定目标 resource,访问令牌通过请求头发送。服务端必须验证令牌的目标受众,不能把收到的令牌作为通用凭据透传给第三方。2527 这些规则与 OAuth 安全最佳实践的权限最小化、授权码保护及受众限制相衔接,但不能把 HTTP 授权要求不加条件地推广为所有本地 MCP 进程的认证规则。29

信息征询也不能替代服务端自己的授权。URL 模式可以让用户向第三方授权,但第三方凭据不得经过 MCP 客户端;MCP 服务端不能靠这种流程绕过对自身访问者的认证。18 同理,requestState 即使由服务端签发,回传时也必须视作攻击者可控输入;凡影响授权、资源访问或业务逻辑,必须保护完整性并拒绝验证失败的状态。绑定主体、有效期和原请求可限制重放窗口,却不能自行保证只消费一次;一次性消费仍需服务端强制执行。17

对本地服务而言,进程能够访问什么,最终取决于运行身份、凭据和系统隔离,而不是 stdio 这个通道名称。对远程服务而言,工具注解、资源文本及提示模板也不能因来自协议接口就自动可信。工具标注“只读”不是安全证明,内容声称“忽略此前规则”不是授权。协议可要求输入验证与访问控制,但宿主仍要防止外部内容被错误提升为控制指令。11421

7. 规范性要求与互操作

互操作需要分清五层依据:JSON-RPC、HTTP 等底层标准;MCP 的核心文字规范;消息 schema;可选扩展;具体 SDK 与宿主。TypeScript 定义是消息结构的权威来源,JSON Schema 从它生成,但状态转换、能力前提和取消竞态不能仅靠形状验证覆盖。SDK 暴露一个叫“session”的对象,也不能反推所选协议版本必有会话语义。456

大写规范性关键词具有明确强度:MUST 要求满足,SHOULD 允许经充分权衡的例外,MAY 表示可选;RFC 8174 指出这些特殊含义针对全大写形式。没有关键词的文字也不当然失去规范意义。3031 更重要的是识别条件:服务端是否支持工具是可选问题,但声明支持后,响应列表和验证输入就产生相应义务。不能用“能力可选”推导出“选了之后也随便实现”。414

扩展以带命名空间的标识符和设置声明支持。若只有一端支持,必须回退到核心行为或明确拒绝;未知 resultType 不能被随意当成成功结果。46 扩展使创新不必全部进入核心,却扩大了兼容性判断的维度:共同版本只是起点,还要确认所需能力、扩展和对应行为。一个只支持工具的宿主不能因为能够连接服务端,就被认为能呈现其所有资源和模板。

规范快照也可能存在不一致。当前总览仍有“初始化时协商扩展”的残留措辞,授权页也保留 GET /mcp 示例。125 前者与逐请求版本细则及扩展声明示例冲突,后者不能覆盖当前 POST 传输要求。61022 本文依据针对该机制的细则解释,并保留冲突记录,而不是把几处片段拼接成不存在的流程。对未知 ID 的错误值差异同样应单独测试,不能用“使用 JSON-RPC”一语略过。35

所以,一份 JSON 示例通过官方 schema,只能证明它符合所检查的结构定义,不能称作完整官方认证。还应检查响应 ID 对应关系、MRTR 新旧 ID 不同、能力是否声明、状态是否原样回传,以及 HTTP 头与正文是否一致。41017 真正的实现验证还包括时序、错误、权限和断连路径;静态示例不会证明网络行为,更不会证明业务正确性。

兼容性还需要区分接收宽容与发送合规。为旧服务端接受缺失的 resultType,不等于新服务端可以持续发送旧格式;为双版本实现保留初始化分支,也不等于当前请求可以省略元数据。46 同样,收到 HTTP 错误后应先识别正文:版本不支持、能力缺失和头字段不一致都可能产生当前协议的错误,不能见到 400 就退回旧握手。10 否则所谓自动兼容可能隐藏真正的请求错误,甚至改变随后交互的取消与状态语义。

8. 保证边界与取舍

MCP 标准化的是交互的可理解性:内容有可寻址对象,动作有参数契约,模板有取得方式,输入不足和执行失败有明确表达。它不直接标准化研究任务如何规划、模型怎样得出结论、统计口径是否合理,也不替业务系统提供事务回滚与恰好一次执行。21114

贯穿案例可以看到三组取舍。统一能力抽象降低接入差异,却保留业务语义差异;逐请求声明减少对连接历史的依赖,却增加重复元数据和显式状态管理;可选扩展允许更丰富的交互,却要求双方逐项确认共同能力。这些不是额外补丁,而是协议在可组合性、部署灵活性与共同保证范围之间的选择。4617

最终,“支持 MCP”应展开成一组具体判断:支持哪个修订版、哪些能力、何种传输和扩展,能否在错误与取消路径上遵守相同规则,又由谁承担授权和业务副作用。把这些条件说明白,比仅展示一次成功工具调用更能解释协议价值。MCP 的边界不是其无用之处,而是不同系统能够合作时,仍需明确分工之处。

参考资料

  1. MCP 2026-07-28:规范概览↩1↩2↩3↩4↩5↩6↩7
  2. MCP 2026-07-28:架构↩1↩2↩3↩4↩5↩6↩7↩8↩9
  3. JSON-RPC 2.0 规范↩1↩2↩3↩4↩5↩6↩7
  4. MCP 2026-07-28:基础消息与元数据↩1↩2↩3↩4↩5↩6↩7↩8↩9↩10↩11↩12↩13↩14↩15↩16↩17
  5. MCP 2026-07-28:TypeScript 消息定义↩1↩2↩3↩4↩5↩6
  6. MCP 2026-07-28:版本与兼容性↩1↩2↩3↩4↩5↩6↩7↩8
  7. MCP 2026-07-28:服务发现↩
  8. MCP 2025-11-25:生命周期↩
  9. MCP 2026-07-28:stdio 传输↩1↩2↩3
  10. MCP 2026-07-28:Streamable HTTP↩1↩2↩3↩4↩5↩6↩7↩8↩9
  11. RFC 9110:HTTP 语义↩1↩2
  12. WHATWG HTML:Server-sent events↩
  13. MCP 2025-11-25:传输↩
  14. MCP 2026-07-28:工具↩1↩2↩3↩4↩5↩6↩7↩8↩9↩10↩11↩12↩13
  15. MCP 2026-07-28:资源↩1↩2↩3↩4↩5↩6
  16. MCP 2026-07-28:提示模板↩1↩2↩3↩4↩5
  17. MCP 2026-07-28:多轮往返请求↩1↩2↩3↩4↩5
  18. MCP 2026-07-28:信息征询↩1↩2
  19. MCP 2026-07-28:弃用功能登记↩
  20. MCP 2026-07-28:Sampling↩
  21. MCP 2026-07-28:Roots↩1↩2
  22. MCP 官方扩展概览(固定仓库快照)↩1↩2
  23. MCP Tasks 概览(固定仓库快照)↩1↩2
  24. MCP Apps 概览(固定仓库快照)↩
  25. MCP 2026-07-28:HTTP 授权↩1↩2↩3↩4
  26. MCP 2026-07-28:授权服务器发现↩
  27. MCP 2026-07-28:授权安全要求↩
  28. RFC 9728:OAuth 受保护资源元数据↩
  29. RFC 9700:OAuth 2.0 安全最佳实践↩
  30. RFC 2119:规范性要求等级↩
  31. RFC 8174:规范性关键词大小写↩
  32. OpenAPI 3.1.1:接口描述↩
  33. OpenAI:Function calling 交互流程↩1↩2
  34. MCP 2026-07-28:取消与超时↩1↩2

💬 评论

主题
字体
密度
语言