先记住这个答案
initialize 的响应让客户端拿到协议版本与服务端能力,notifications/initialized 则由客户端告知服务端自己已完成初始化并准备正常通信。它是一条没有 id 的通知,对端不返回 JSON-RPC 响应,客户端不能等待一个不存在的 initialized result。按 2025-11-25 生命周期要求,客户端在初始化响应前通常不发 ping 之外的请求,服务端在收到就绪通知前通常不主动发起常规业务请求,保留 ping 与日志等允许的交互;工程上应使用明确状态与有序发送队列落实这个阶段边界。
- 就绪通知由客户端发送给服务端
- 通知没有 id,也没有对应 JSON-RPC 响应
- 用状态检查和有序队列管理握手期间的请求
为什么不能在收到响应字节时立刻开放所有动作
客户端可能还需要校验响应版本、保存连接能力,以及注册服务端反向请求的处理器。如果界面上的工具按钮在这些步骤完成前就变成可点,会出现同一个服务端偶尔成功、偶尔报告能力尚未准备好的启动竞态。
可以让业务请求通过统一入口检查连接状态:初始化期间排队或返回明确的未就绪错误,初始化失败则拒绝队列。不能让每个按钮各自判断一个含义模糊的 connected 布尔值,把传输已连接与协议可操作混在一起。
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}接收方不会为这条通知返回 JSON-RPC result。HTTP 传输对发送动作的状态码与 JSON-RPC 请求响应是两个层次,不能把传输确认当成通知新增了一次协议响应。
发送顺序比增加一个任意延迟更可靠
假设客户端把就绪通知和 tools/list 分别交给两个互不协调的异步发送任务,即使调用代码看似前后相邻,底层提交顺序也可能失去保证。更可靠的做法是使用 SDK 的连接完成语义,或在自建传输层统一串行安排初始化阶段的消息。
加入固定几百毫秒等待只能暂时掩盖竞争,在慢机器或断线恢复后仍可能失败。测试应主动延迟初始化响应和处理器注册,确认业务请求没有越过就绪边界,并检查失败时队列是否被清理而不是永久悬挂。
提前收到业务请求时不能编造统一错误码
生命周期规范约束消息发送时机,但没有保证每个服务端都会对所有提前请求返回同一个专用错误码。实际实现可能拒绝、关闭连接或采用自己的处理策略,客户端不应依赖某一种偶然的容错行为才能正常启动。
记录初始化请求标识、响应接收时间、就绪通知提交时间与首条业务消息,可用于定位顺序问题。恢复时应分清新会话重新握手和同一会话的传输恢复,避免在普通重试路径中不加判断地重复发送初始化流程。
容易答错的地方
- 等待 initialized 的成功响应再放行队列
- 通知不对应 JSON-RPC 响应,这样设计会让队列永久等待;应按所用传输与 SDK 的发送完成和连接状态语义推进。
- 只要 TCP 或 HTTP 可通就说明协议已经就绪
- 网络通达只证明传输可用,协议版本、能力和反向处理器还可能没有准备好;界面和调用入口需要区分这些阶段。
面试官还会怎么问?
就绪通知可以带 id 方便排查吗?
不能用 id 把通知改成请求。追踪信息应放在允许的元数据或本地日志中,并保持接收方不需要为通知返回响应的语义。
握手阶段为什么允许 ping?
它用于检查连接的响应能力,不依赖普通工具或资源能力已经准备好。允许这种基础消息不等于其他业务请求也可以随时发送。
就绪通知发送失败后应该怎么办?
先将连接保持在未就绪或失败状态,再按传输恢复策略处理。不要直接放行业务调用,也不要把未知发送结果解释成服务端已经收到。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。