ESSAY·

MCP:模型上下文协议

从工具、资源与提示模板到握手、传输、任务与授权,解析 MCP 的消息结构、恢复边界和 Harness 接入设计。

最近修订:2026.09.07

阅读导航 · 第 1 / 6 篇

MCP(Model Context Protocol)统一 Agent 应用与外部能力服务之间的消息契约。它让同一个工具服务可以被不同宿主发现和调用,也让宿主以相近的方式接入不同服务。它不负责替模型规划任务,也不替业务系统保证写入只发生一次。

本文以 2025-11-25 规范为固定学习基线,2026-09-07 复核;不是所有后续版本的功能总表。原有配套代码仍是本地状态模型,没有启动真实 MCP 服务。新增报文与图用于解释规范和设计,不扩大实验结论。

架构:Host 管理信任,Client 管理连接,Server 提供能力

flowchart TB
%% title: 系统架构图
    U[用户任务] --> H[Host:上下文与权限策略]
    H --> M[模型适配器]
    H --> C1[MCP Client A]
    H --> C2[MCP Client B]
    C1 <-->|JSON-RPC| S1[本地 Server]
    C2 <-->|JSON-RPC| S2[远程 Server]
    S1 --> F[文件或 CLI]
    S2 --> API[业务 API 或数据库]

Host 是拥有用户会话的应用;Client 是宿主内负责某条服务连接的协议角色;Server 暴露能力。图中的两个 Client 表达连接隔离,不要求两个独立进程。模型厂商 API 属于另一条调用链,不因使用 MCP 而消失。

一个合理的 Host 要维护“服务身份+工具名”的命名空间。例如两个服务都提供 search,模型可见名称需要无歧义,调用后仍要映射回原服务。工具描述、资源内容和服务端 instructions 都是外来输入,不能覆盖宿主自身的授权规则。

三种服务端原语解决不同问题

原语典型交互交给宿主什么不应误解为
Toolstools/list、tools/call可执行操作及输入输出契约已授予执行权限
Resourcesresources/list、resources/readURI 标识的上下文数据自动进入模型全部上下文
Promptsprompts/list、prompts/get可参数化的消息模板高于宿主规则的系统指令

三者的区别在控制意图:工具支持模型选择操作,资源供应用组织上下文,提示模板供用户或应用显式选用。实际界面如何呈现由 Host 决定。服务端原语

反向能力也值得关注:Server 可在客户端支持时请求 sampling 或 elicitation,分别请求模型采样或向用户收集信息;roots 提供宿主愿意暴露的根目录信息。它们均受能力协商约束。roots 是边界描述,不能代替操作系统文件权限或沙箱。 一个服务器请求模型采样,也不意味着获得了无限 Token 预算。Sampling、Roots、Elicitation

报文分为信封、工具契约和业务语义

以下是作者构造的合法形状示例,假定 search_docs 已被发现并获准使用:

{"jsonrpc":"2.0","id":"req-7","method":"tools/call","params":{"name":"search_docs","arguments":{"query":"恢复策略"}}}

工具定义中的 inputSchema 描述参数形状;结果可以包含文本、资源链接或结构化内容。声明 outputSchema 时,结构化结果还需满足输出契约。工具 annotations 中的只读、破坏性等提示不能取代服务端权限判断。工具定义与结果

flowchart TB
%% title: 数据流图
    D[目录与输入 Schema] --> P[模型提出名称与参数]
    P --> V[Host 校验参数和授权]
    V --> Q[Client 封装 tools/call]
    Q --> X[Server 执行业务逻辑]
    X --> O[结果或错误]
    O --> N[归一化并保留来源]
    N --> C[装配下一轮上下文]
    N --> E[独立业务验收]

参数校验、授权、结果归一化是本文建议的 Host 接入流程。Schema 只能检查形状;query 合法不代表有权查询任意租户。结果进入上下文与任务验收是两个不同出口。

初始化建立共同的运行前提

握手完成后再进入工具发现;传输、身份验证和错误处理还需要各自的生命周期管理。 客户端提出版本,服务端返回所采用的版本与能力;客户端若不支持返回版本,规范建议断开连接。收到初始化结果后,客户端必须发送 notifications/initialized。后续行为要遵守已协商的能力。MCP 生命周期规范

实验采用更容易验证的工具闸门:完成上述步骤且服务端声明 tools 后才允许工具请求。它只实现工具子集,不能用来验证规范中 ping、日志等初始化阶段例外。

sequenceDiagram
%% title: 初始化与调用时序图
    participant H as Host / Client
    participant S as Server
    H->>S: initialize(版本与能力)
    S-->>H: 协商版本与服务能力
    H->>S: notifications/initialized
    H->>S: tools/list
    S-->>H: 工具目录与 Schema
    H->>H: 选择工具并检查权限
    H->>S: tools/call(请求 ID)
    S-->>H: 相同 ID 的结果或错误

时序表达工具子集的正常路径;Server 只声明 resources 时,Host 不能假定 tools 也可用。分页目录需取全或建立明确的增量发现策略。

请求 ID 只负责关联这一次往返

JSON-RPC 请求带有 ID,响应用相同 ID 关联;通知没有 ID,也不要求对应响应。响应应包含 result 或 error,两者不能同时出现。JSON-RPC 2.0 规范

两个工具请求先后发出,结果可能倒序到达。Harness 应按 ID 查找等待者,不能把第一个收到的结果交给第一个发出的调用。

实验用“连接代次:序号”产生 ID,例如 1:3、2:3。这是教学实现的本地策略,不是 MCP 要求的格式。重新连接后旧响应无法命中新请求,有助于测试迟到消息的处理。

但请求 ID 不能代替业务操作键。一次工单创建经过断线重试,可能涉及多个请求 ID,业务上仍是同一次创建意图。

工具目录也有有效期

服务端在支持相应能力时,可以通知工具列表发生变化。工具发现、输入 Schema 和执行错误格式由工具规范定义。

Harness 还需要自己的缓存策略。假设目录请求已经发出,此时收到目录变化通知,随后旧请求的响应才到达;直接把这份响应标为最新目录,会覆盖刚刚获得的失效信息。

实验为目录维护本地修订号。发请求时记住修订号,变化通知到达时递增;返回结果若对应旧修订,就不允许它重新使缓存有效。接着重新获取目录,再开放工具调用。

这是这里选择的保守策略,规范没有要求所有客户端采用这一缓存算法。真实目录还可能需要分页、权限过滤与 Schema 校验,本实验未实现这些部分。

三层成功必须分别判断

观察可以说明下一步仍需确认
收到合法 JSON-RPC 响应本次响应能够关联与解析是否为协议错误
收到工具结果且没有执行错误标记工具返回了正常结果业务对象是否符合目标
权威读取和验收通过当前证据支持目标已完成是否还有任务级未完成条件

协议层错误,例如无法识别的方法,进入 error;工具执行中的业务错误可以在 result 内使用 isError: true。本地模型分别返回 protocol_error 与 tool_error,正常结果也只记作 tool_result_received。

这延续了工具契约篇的原则:接口完成与任务完成由不同证据支持。错误分类也不自动决定能否重试,写操作是否已经产生副作用仍需单独确认。

练习时,画出客户端当前允许发送的方法,并在每条边上注明前提。若图里只有“连接成功→调用工具”,就把初始化、能力缺失、目录变化和错误返回四条分支补上。

两种传输需要不同的运行管理

MCP 2025-11-25 定义 stdio 与 Streamable HTTP。stdio 常用于客户端启动本地子进程,协议消息走标准输入输出,标准输出不能混入普通日志。Streamable HTTP 使用 HTTP 端点,并可通过 SSE 传递消息。MCP 传输规范

运行问题stdio 接入时重点检查HTTP 接入时重点检查
服务生命周期子进程启动、退出和资源回收服务可达性、请求和会话生命周期
日志与消息标准输出被日志污染代理缓冲、连接中断与流解析
身份边界子进程继承的环境和本地权限身份验证、会话归属与来源校验
故障验证子进程异常退出、残缺消息响应丢失、流断开、会话失效

这些是后续真实接入应执行的检查,本文没有启动子进程协议服务器,也没有发送 HTTP 请求。

三种 ID 对应三种范围

标识作用范围不能据此推断
JSON-RPC 请求 ID请求与响应关联业务动作只执行一次
MCP 会话 ID服务端选择启用的 HTTP 会话调用者具有业务权限
SSE 事件 ID支持恢复时定位事件位置外部写入尚未发生

业务操作键是另一层应用契约,用来关联同一个写入意图。配套模型把它保存在客户端等待记录里,没有把它伪装成 MCP 标准字段。真实接入时,需要工具或背后业务服务明确支持它。

HTTP 服务端可以选择分配会话 ID;携带已失效会话 ID 收到 404 时,客户端必须重新初始化。SSE 恢复也取决于服务端支持,不能假设每次断线都可以完整重放。会话与恢复规则

即便消息重放成功,仍应按请求 ID 处理重复或迟到响应,并按业务操作判断副作用。

取消是一场可能发生竞态的协作

普通请求可以使用 notifications/cancelled 通知取消。初始化请求不能这样取消;任务增强请求采用单独的 tasks/cancel 机制,不能混用。MCP 取消规范

取消可能与完成同时发生。接收方可能已经提交了工单,无法通过停止计算撤销它。发送方停止等待,也不等于收到了一份“没有执行”的证明。

实验在取消后移除等待项,把写操作加入未知结果集合,忽略后续迟到响应。这只是在模型中确定了客户端如何处理消息。是否撤销工单,需要一个独立且获得授权的业务动作,不能偷偷塞进取消逻辑。

重连后恢复的是观察能力

建议将恢复拆成可独立验收的步骤:

  1. 建立新连接并完成协商,重新确认工具目录。
  2. 找到断线时尚未确定结果的业务操作。
  3. 使用权威查询检查实际资源及其参数。
  4. 根据查询一致性、去重契约和任务目标,决定完成、等待、重试或交接。

“查不到”只有在查询语义足以排除已提交结果时,才能支持后续判断。远端查询如果存在延迟,就仍要保留未知状态。

配套演示直接查询本地 SQLite 中的工单,能看到已提交的资源;但协议模型仍保留 needs_reconciliation,因为演示没有实现任务级验收和完成状态回写。查询证据与任务终态之间,仍有一道应用决策。

给连接恢复设置终点

重复重连也会消耗时间和资源。每次连接尝试要受单次超时约束,整个任务还需要总截止时间,不能让进度通知或重试不断延长用户等待。

同时保留观测篇中的任务关联:任务 ID、业务操作键、请求 ID 和连接代次分别记录。否则新连接里的成功日志会掩盖上一条连接留下的未知写入。

练习时,选择一个真实写工具,在“提交前断开”和“提交后响应丢失”两处分别注入故障。两种场景的界面可能一样,恢复决策应由实际证据区分。

长任务与授权需要单独协商

2025-11-25 引入的 Tasks 在该版本中标记为实验性。它给可增强的请求增加任务句柄和生命周期,可以查询状态、取得结果或取消。它不是 A2A Task 的同义词:MCP Task 服务于一次能力请求的延迟完成;A2A 还描述独立 Agent 的能力与交互。Tasks 规范

stateDiagram-v2
%% title: Host 调用状态机
    [*] --> 未就绪
    未就绪 --> 可调用: 握手与目录完成
    可调用 --> 等待结果: 发送调用
    等待结果 --> 收到结果: 正常响应
    等待结果 --> 结果未知: 超时或断线
    结果未知 --> 对账中: 恢复连接后查询业务
    对账中 --> 收到结果: 确认已经执行
    对账中 --> 人工处理: 证据不足
    收到结果 --> [*]
    人工处理 --> [*]

这是 Host 的设计状态机,图中的“结果未知/对账中”不是 MCP 标准枚举。它专门保留网络协议无法解释的业务副作用。

HTTP 授权规范将 MCP Server 放在资源服务器的位置:客户端发现受保护资源及授权服务器信息,取得适用于目标资源的访问令牌。请求的资源边界、实际受众、有效期和权限范围都需核验;会话 ID 不是访问令牌。stdio 则通常从受控运行环境获得凭据,不能照搬 HTTP OAuth 流程。MCP 授权

Host 不应把发给自己的 Token 原样交给另一个服务。需要访问下游系统时,使用明确的下游凭据或受支持的委派机制。用户批准了“读取文档”,也不意味着批准“把原文发送给外部模型”。授权的对象应包括动作、数据和目标。

接入取舍与验证范围

MCP 适合多个 Host 复用同一能力服务,尤其在工具目录、资源和客户端反向能力都需要标准化时。仅在单进程调用两个自有函数,直接函数接口通常更容易调试。已经有 HTTP API 时,可在 API 外加适配层,而不是把业务逻辑搬进协议处理器。

接入验证至少覆盖:不支持的版本被拒绝、分页目录没有遗漏、同名工具不会串服务、无权参数被服务端拒绝、执行错误不会记作成功、重连后的旧响应不会污染新调用,以及写入后响应丢失时不盲目重试。这里给出验证设计,真实跨 Host 互通和生产授权链尚未实测。

工具执行语义见Tool Calling,跨 Agent 委派见A2A,授权流程见OAuth。MCP 的价值是统一接口边界;可靠运行仍取决于 Host 和业务服务对这些边界的落实。