前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库MCP JSON-RPC 消息类型请求响应通知
AIAI AgentMCP 协议

MCP 的 JSON-RPC request、response、notification 怎样区分和关联?

先判断消息在协议里扮演什么角色,再按请求标识把结果交给正确的等待者。

前端进阶之旅 · 一题精讲更新于 2026.09.06
AI Agent#MCP 协议#MCP#并发编程
先看核心答案读代码示例
理解线索

先分类再分发到等待者

  1. 请求记录标识并等待结果或错误
  2. 响应按请求关联找到等待者并结束等待
  3. 通知按方法处理事件,不创建响应等待项

下面每一行是一条独立消息的摘要,整体不是可以直接发送的 JSON-RPC batch 数组。

核心回答

先记住这个答案

request 带 method 和请求 id,用来发起需要结果的操作;response 用同一个 id 返回 result 或 error,两者不能同时存在;notification 带 method 但没有 id,接收方不返回 JSON-RPC 响应。MCP 客户端和服务端都可能发起请求,消息方向不能单独决定类型。按 2025-11-25,正常请求 id 是字符串或整数且不能为 null,同一请求方在同一会话内不能复用。并发调用必须按连接、方向和 id 关联,不能靠响应到达顺序配对。

  • 带 method 的消息可能是请求也可能是通知
  • result 与 error 是响应的互斥分支
  • 响应到达次序不保证与请求发送次序相同

看字段角色而不是看消息来自哪一端

tools/list 通常由客户端向服务端请求,roots/list 则可以由服务端向客户端请求。两者都是 request;它们的结果都由接收请求的一端返回。若接收循环写成“服务器发来的全是响应”,反向请求就会被错误地丢进待完成请求表。

示意中两个列表请求分别使用独立 id,工具列表的响应先回来不影响资源列表仍在等待。通知不登记为待完成请求,也不需要为它创建一个迟早超时的 Promise;这能避免长连接运行一段时间后等待表不断增长。

并发响应可以按不同顺序返回Text
C -> S  {"jsonrpc":"2.0","id":"r-1","method":"resources/list"}
C -> S  {"jsonrpc":"2.0","id":"t-2","method":"tools/list"}
S -> C  {"jsonrpc":"2.0","id":"t-2","result":{"tools":[]}}
S -> C  {"jsonrpc":"2.0","method":"notifications/tools/list_changed"}
S -> C  {"jsonrpc":"2.0","id":"r-1","result":{"resources":[]}}

五行分别表示五条独立协议消息,假设初始化与相关能力声明已经完成。关联表应先完成 t-2,再处理无 id 的变化通知,最后完成 r-1,不能按发送顺序取出第一个等待者。

错误响应与工具失败不是同一个分支

JSON-RPC error 描述请求无法按协议正常完成,例如方法不存在或报文参数结构无效。工具执行结果也可能通过 result 正常返回,但内部含 isError: true;这时外层响应关联仍然成功,业务动作却没有按预期完成。

因此调用层应先验证外层结构并匹配 id,再交给相应方法的结果解析器。不能仅凭 HTTP 200 或存在 result 就显示“任务成功”,也不能把所有方法的结果都当成 tools/call 的结果去寻找 isError 字段。

断线和未知响应需要清楚的清理策略

连接关闭时应结束或转移它所属的等待项,晚到的响应不能完成新连接里另一个碰巧同名的请求。保留连接身份、发起方向和 id 的关联信息,有助于发现重复响应、未知 id 与跨连接投递问题。

id 的唯一性是会话内的协议关联规则,不是业务幂等键。网络超时之后换一个新 id 重发写操作,仍可能重复产生副作用;反过来重复使用旧 id 也不会自动获得服务端去重保证。业务重试必须另有状态查询或幂等契约。

回答前,多想一步

容易答错的地方

没有 id 的消息都是错误响应
正常通知就没有 id;应结合 method、result 和 error 的结构分类。损坏请求的错误关联还存在特殊情况,不能用单个字段包办全部解析。
每个响应都按发送顺序移出等待队列
并发请求的处理时间不同,响应可以乱序到达;必须按连接内正确的请求标识配对,否则用户会收到另一个工具的结果。
试着用自己的话回答

面试官还会怎么问?

客户端和服务端能否分别使用相同的请求 id?

唯一性要求针对请求方在会话内的使用记录,因此实现还要区分消息方向。不要把双方发出的请求塞进没有方向信息的同一张等待表。

请求超时后迟到的响应是否还能改变任务状态?

应根据明确的终态策略处理,通常不能让已经结束的等待重新变成成功。仍可记录迟到结果用于核对副作用,但不能偷偷覆盖用户已看到的结论。

通知处理失败要不要返回一个 error?

JSON-RPC 通知没有响应通道,不能为它新增错误响应。可以记录诊断并采取协议允许的恢复动作;需要调用方确认结果的操作应设计为请求。

从一道题,走向一组知识

把知识连起来

MCP 协议

MCP 为什么还要发送 initialized 通知,如何避免握手期间的请求竞态?

初始化就绪是一个没有响应的具体通知实例

MCP 协议

MCP tools/call 的请求响应怎样流转,协议错误和工具失败怎么区分?

继续区分外层响应成功与工具执行结果失败

参考资料

  • MCP 2025-11-25:Base protocol
  • JSON-RPC 2.0 specification

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 看字段角色而不是看消息来自哪一端
  3. 错误响应与工具失败不是同一个分支
  4. 断线和未知响应需要清楚的清理策略
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑