首页 >  科技周边 >  人工智能

MCP notifications/progress 怎么接:progressToken、递增进度与超时收口

来源:17golang原创

时间:2026-08-16 22:12:22 241浏览 收藏

批量索引、长文档解析这类 MCP 工具如果只在最后返回一次结果,客户端很难判断它是在正常运行、卡住,还是已经失联。notifications/progress 提供了一条轻量的反馈通道:客户端在原始请求的 _meta 中放入 progressToken,服务端再用同一个 token 回传当前进度。

进度通知不是心跳,也不能替代最终结果。它必须和正在运行的活动请求绑定,进度值需要持续递增;工具执行完成、失败、超时或被取消后,客户端要停止等待后续通知,回收本次请求的所有状态数据。

要点速览
  • progressToken 要在原始请求生成,并且在对应活动请求结束前保持全局唯一。
  • notifications/progress 至少携带 token 和当前 progress 字段,总量未知时 total 可以省略。
  • progress 每次上报都必须向前推进,不能用同一个数值反复刷屏来假装状态更新。
  • 客户端必须兼容完全不发进度通知的服务端实现,用完成、错误、超时和取消四个场景统一做收尾处理。

先把用户看到的等待状态拆开

一个名为 build_search_index 的工具需要扫描 120 个文件。客户端发起 tools/call 后,服务端可能需要几十秒才能返回最终结果。此时界面至少要区分三种状态:请求已成功发出、任务仍在后台处理、任务已经走完全部流程。进度通知只负责中间那段“仍在处理”的可见反馈,不能用来证明网络连接一定健康。

这个区分非常实用。网络层还在传输数据,不代表业务逻辑一定在向前走;反过来,服务端也有可能因为任务执行太快,连一条进度通知都来不及发就直接返回结果。客户端如果把“没收到进度通知”直接判定成失败,会把正常的快速响应和本身不支持进度上报的服务都误判成故障。

MCP tools/call 携带 progressToken 后由服务端发送 notifications/progress 并回到最终结果的调用链工程插画

请求里的 token 决定通知归属

客户端需要获取进度时,把 token 放在请求参数的 _meta 里。token 可以是字符串或整数类型,但在所有正在运行的活动请求中必须唯一。服务端发通知时原样带回这个 token,客户端才能把收到的进度通知绑定到正确的进度条上。

{
  "jsonrpc": "2.0",
  "id": 17,
  "method": "tools/call",
  "params": {
    "name": "build_search_index",
    "arguments": {"root": "/workspace/docs"},
    "_meta": {"progressToken": "index-17"}
  }
}

服务端不要凭空生成一个没有对应来源请求的 token,也不要把上一次请求的 token 缓存在全局变量里。并发调用场景下最容易出现的问题,就是A请求的进度被错误推到B请求的界面上。工程上可以把 token、请求 id、任务句柄和过期时间存在同一条活动记录里,任务执行完之后立刻删掉这条记录。

通知字段怎么设计才不会误导

最小粒度的进度通知包含 progressTokenprogress;如果任务总量已知,再额外带上 total。下面的48表示已经完成的文件数,100表示总文件数:

{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "index-17",
    "progress": 48,
    "total": 100,
    "message": "正在处理第 48 个文件"
  }
}
字段用途客户端处理逻辑
progressToken关联原始请求找不到对应活动记录时直接丢弃通知,同时记录诊断日志
progress当前进度值只接受比上次记录更大的数值
total已知任务总量字段缺失时直接显示已处理数量,或者用不确定进度条展示
message人类可读提示文案展示短状态给用户看,不把它作为机器判断任务状态的依据

如果任务总量未知,不要为了显示百分比随便填一个猜测的数值。可以只显示“已处理 48 项”,或者用不确定进度条。message 适合给用户解释当前执行阶段,不能替代结构化的状态字段;客户端不要通过解析“正在处理”这类文案来判断任务是否完成。

递增、限频和停止是三个硬检查规则

绑定同一个活动 token 的 progress 应该持续增加,就算 total 数值不知道也要遵守这个规则。客户端可以记录上次收到的进度数值,遇到回退或者重复值时保留旧状态,同时记录异常日志。服务端则应该按处理批次或者时间窗口发送通知,不要每扫描一行文件就发一条进度,避免通知本身占用过多传输和渲染资源。

  • 递增:12 → 28 → 48 是符合预期的正常更新;48 → 48 不能被当作新的进展。
  • 限频:按批次、时间窗口或者百分比变化发送通知,具体频率由服务端自行决定。
  • 停止:最终结果、错误或取消确认发出之后,不能继续给同一个活动 token 发进度通知。

MCP progress 从批次递增到完成结果并在超时和取消分支停止通知的状态边界插画

完成、失败、超时和取消分别收口

客户端可以把一次调用建模为 activecompletedfailedtimed_outcancelled 五种状态。收到最终结果时进入 completed;收到协议错误或者服务端返回的错误时进入 failed。触发本地超时逻辑后进入 timed_out,此时要停止更新界面,不能因为后续迟到的通知又把进度条重新点亮。

如果用户主动终止请求,还要按照 MCP 的取消机制发送 notifications/cancelled,同时带上原始请求 id。服务端收到之后要尽快终止对应的后台任务;客户端不能只隐藏掉进度条,却放任后端任务继续无限制运行。取消通知和进度通知是两条完全独立的消息:前者是向服务端传递停止执行的意图,后者只负责上报当前处理进度。

{
  "jsonrpc": "2.0",
  "method": "notifications/cancelled",
  "params": {
    "requestId": 17,
    "reason": "用户停止索引"
  }
}

一个可直接落地的客户端检查顺序

  1. 创建本地请求记录,生成当前活动范围内唯一的 progressToken
  2. 发送 tools/call,把生成好的 token 放进 _meta
  3. 只接收仍处于 active 状态的 token 对应的通知,同时检查 progress 数值是否符合递增规则。
  4. total 字段存在时计算完成百分比;字段不存在时直接使用不确定进度条或者数量提示。
  5. 最终结果、错误、超时和取消任一事件触发后,标记当前请求为终态,删除本地存储的活动 token。
  6. 后续收到的迟到通知只记录诊断信息,不要恢复已经结束的界面状态。

这个执行顺序把“展示进度反馈”和“判定任务最终结果”完全分开了。就算某个服务端从来都不发进度通知,只要最终响应在超时窗口内正常返回,整个调用流程仍然可以正常走完。

常见问题

服务端必须发送 progress 通知吗?

不需要。规范允许服务端不发送通知、按自己的节奏发送通知,或者省略 total 字段。客户端必须把进度通知当作可选的体验增强项来处理。

progress 可以从 0 重新开始吗?

同一个活动 token 对应的进度不应该回退。如果实际工作需要拆成多个阶段执行,应该让 progress 继续向前累加,或者为新的独立请求生成全新的 token。

没有 total 时能不能显示百分比?

没法可靠显示。更合理的做法是展示已处理数量、不确定进度条或者当前阶段提示,避免用猜测的总量生成虚假的完成率。

最终结果到了以后还收到通知怎么办?

直接判定当前请求已经结束,忽略这条迟到的通知并记录调试日志。服务端要修正活动 token 的生命周期管理逻辑,保证任务完成后立刻停止发送进度。

把进度条当作反馈,而不是承诺

MCP 进度通知最实用的价值,是让长任务的等待过程变得可感知可理解;它并没有把普通网络连接变成高可靠的任务队列。只要坚持用 token 绑定请求、进度数值递增、通知限频和终态收口这几个原则,客户端就能在有通知时给用户提供细致的等待反馈,就算没有通知也能保证整体流程正常运行。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>