最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
MCP Server 的通知应如何处理,客户端有哪些最佳实践?
时间:2026-09-16 10:18:01 编辑:袖梨 来源:一聚教程网
MCP Server 的通知应按“协议事件”处理,而不是一律转成 LLM 输入。列表变化通知用于让客户端重新发现 Tools、Prompts 或 Resources;资源更新通知用于让已订阅客户端重新读取指定 URI;进度、日志和取消等通知则服务于任务状态与界面反馈。客户端首先验证通知方法和订阅关系,再执行确定性的缓存失效或状态更新,只有宿主应用明确授权的业务事件,才可能经过安全策略进入代理调度。
先区分协议通知与业务事件
MCP 通知是没有 JSON-RPC 请求 ID 的单向消息。发送方不期待对应响应,接收方也不应把它当成普通请求调用。标准通知有明确方法名和参数结构,例如工具列表变化、资源内容更新、进度以及日志。它们解决的是协议协作问题,不等于“服务端要求模型立刻做某件事”。
智能家居中窗户被第三方打开、新邮件到达、工单状态变化,这些属于业务事件。若直接发明 `tool/window_open` 一类方法,不同客户端并不知道 params 的含义,也无法通用处理。更合适的做法通常是把窗口、邮箱或工单建模为 Resource,由客户端订阅资源更新;若确实需要通用事件语义,则应由宿主应用定义独立扩展和权限策略,不能假装它已经是标准 MCP 行为。
列表变化通知只负责让缓存失效
`notifications/tools/list_changed`、`notifications/prompts/list_changed` 与 `notifications/resources/list_changed` 表示客户端保存的能力列表可能过期。通知不包含完整新增、删除或修改差异。客户端收到后,应重新调用对应 list 方法,处理全部分页并原子替换快照。
list_changed
→ 标记对应缓存 stale
→ 合并短时间重复事件
→ 重新调用 list
→ 校验完整结果
→ 原子替换客户端快照
通知次数不能用来推导变更次数。服务端可能把批量操作合并成一条,也可能因至少一次投递产生重复事件。客户端刷新逻辑必须幂等,并在刷新期间又收到通知时至少再拉取一轮。
资源状态变化优先使用 resources/updated
当同一个对象的内容发生变化,而资源 URI 仍然存在时,应使用 `notifications/resources/updated`。参数携带 URI,客户端据此知道哪份缓存过期,再调用 `resources/read` 获取新内容。通知仍不是内容本身,避免把大型数据或敏感信息直接推入事件流。
{
"jsonrpc": "2.0",
"method": "notifications/resources/updated",
"params": {
"uri": "home://windows/living-room-front"
}
}
智能家居窗口状态就是典型资源:客户端先订阅 URI,状态变化后收到 updated,再读取新的 open 或 closed 值。若新增了一扇窗户,导致资源集合变化,则发送 resources list changed;如果只是已有窗户状态变化,发送 resource updated。两者不要混用。
客户端不应自动把通知交给 LLM
LLM 对话通常有 user、assistant、system 和工具调用结果等严格角色与轮次约束。服务端主动通知并不天然对应其中任何一种角色。若客户端把任意通知伪装成 user 消息,服务端就可能绕过用户意图触发模型、消耗费用,甚至诱导模型调用高风险工具。
更安全的架构是在协议层先处理通知,再由宿主应用的事件策略决定下一步。缓存失效事件只更新缓存;进度事件只更新任务视图;业务资源变化可以进入待办队列、展示给用户、触发规则引擎,或在明确允许的自动化中唤醒代理。这个决定属于宿主,不属于 MCP Server。
若要实现自主邮件处理,应明确配置“哪些邮箱、哪些发送者、什么时间段、可执行哪些工具、是否需要确认”。代理被唤醒后重新读取资源,基于最新状态规划动作。不要让邮件 Server 直接通过自定义通知命令模型发送回复。
按能力协商和订阅结果接收通知
客户端应先检查服务端 capability。列表通知需要相应 `listChanged` 支持,资源内容更新需要 `resources.subscribe`。在使用订阅流的协议版本中,客户端还要打开 `subscriptions/listen` 并明确选择通知类型或资源 URI。没有能力声明或没有有效订阅,就不能假定事件会送达。
重连后应重新完成能力发现、恢复必要订阅并刷新关键快照。不要把旧连接的订阅状态复制到新连接而不确认服务端支持。协议版本也要记录,因为不同版本的投递与订阅细节可能变化。
通知处理器要快速、隔离且可恢复
接收回调不宜执行长时间业务逻辑。它应验证方法与参数,将事件写入有界队列或触发缓存失效,然后尽快返回。耗时的 list、read、数据库写入和代理运行交给后台工作器。否则一个慢处理器可能阻塞同一连接上的其他消息。
Tools、Prompts、Resources、Progress 和 Logging 使用独立处理通道。某类通知解析失败不能拖垮整个连接。未知方法按协议策略记录并忽略,不能反序列化到任意动态类型后执行。日志中保留服务端、连接、协议版本、方法、资源 URI 和时间,但应删除令牌与敏感内容。
刷新应有单飞、去抖和版本保护
多个变化可能在几十毫秒内连续到达。客户端可对同一缓存键做短暂去抖,并用 single-flight 保证只有一个刷新请求运行。刷新中再次收到事件时记录 dirty,当前请求结束后再刷新一次。这样不会并发覆盖,也不会漏掉新变化。
onNotification(key):
state[key].dirty = true
if state[key].refreshing: return
while state[key].dirty:
state[key].dirty = false
snapshot = fetchAndValidateAllPages(key)
replaceAtomically(key, snapshot)
若服务端提供版本、ETag、TTL 或缓存范围,客户端应一并使用。分页刷新要保持一致性;发现跨页版本变化就重新开始。刷新失败时保留旧快照并标记 stale,按指数退避重试,同时为用户保留手动刷新入口。
资源更新需要控制读取频率
传感器或日志资源可能高频变化。客户端不应每收到一次 updated 都立即 read,尤其当服务端仅表达“当前值已变化”。可以按 URI 合并通知,在短窗口后读取最新状态;对于只关心最终值的场景,中间变化无需逐条恢复。
如果业务要求不丢事件,例如财务流水或审计记录,就不应只依赖“资源已更新”信号。资源内容应设计为带游标或序列号的事件日志,客户端从最后确认位置继续读取。MCP 通知负责唤醒读取,可靠事件语义由资源数据模型提供。
安全策略必须位于客户端宿主
通知来自外部 Server,应按不可信输入处理。客户端验证方法白名单、参数 schema、URI scheme、大小限制和授权上下文。资源更新只能触发对原本允许 URI 的读取,不能因为通知中出现新 URI 就越权访问。
若事件可能唤醒 LLM,应设置频率、预算、并发和操作权限上限,避免恶意 Server 制造通知风暴。所有自动动作都要审计:哪个 Server 发出什么事件、宿主读取了什么资源、模型作出什么决定、调用了哪些工具。删除、支付、发布消息等高风险动作仍应要求用户确认。
多进程与多设备如何分发
服务端横向扩展后,本机内存事件总线只能触达连接在该进程的客户端。应用需要共享发布订阅后端,把列表或资源变化广播到所有承载活跃连接的实例。内部事件携带租户、资源或能力类别和版本,节点收到后再通过自己的 MCP 订阅流转发。
客户端也可能在多个设备连接同一账户。是否每个设备都响应业务事件由宿主策略决定。缓存失效可以各自处理,但自动代理任务必须去重,避免同一新邮件被多个设备同时回复。可以用服务端任务锁、幂等键或集中调度器保证单次执行。
进度和日志通知不要污染模型上下文
长任务的 progress notification 适合更新进度条、超时判断和取消按钮;logging notification 适合诊断面板与可观察性。默认不应把每条进度和日志加入模型上下文,这会浪费 token,并可能把调试文本变成提示注入渠道。
只有当代理需要依据结构化阶段结果继续规划时,宿主才提取必要摘要,而不是原样转发通知。模型需要的最终业务结果应通过标准工具结果、资源内容或任务完成状态提供。
错误处理与降级
通知没有响应,因此服务端无法从 JSON-RPC 回复得知客户端处理是否成功。关键业务不能只依赖一次通知。服务端保持可重新读取的权威状态,客户端通过 TTL、重连刷新或手动刷新修复漏失事件。资源若需要可靠消费,应暴露序列化日志和确认游标。
处理器遇到非法参数时记录并丢弃,不要关闭整个客户端;同一 Server 连续发送无效或过量通知时可以限流、隔离或断开。重新连接后执行全量对账,而不是试图重放未知缺口。
测试最佳实践
测试至少覆盖能力未声明、订阅未建立、正常通知、重复通知、通知风暴、刷新期间再次变化、分页、读取失败、重连和权限收回。断言不应只检查“收到了事件”,还要检查事件引发的正确后续请求与最终缓存状态。
对资源订阅,先读取基线内容,再改变状态并发送 updated,确认客户端重新 read 指定 URI。对业务自动化,使用假 Server 发送恶意 URI、超大参数和高频事件,确认宿主不会未经授权唤醒模型或执行工具。
结论
MCP 通知的最佳实践是让协议层保持确定性:list changed 触发重新 list,resource updated 触发重新 read,progress 与 logging 更新应用状态。通知本身只做失效或状态信号,权威内容仍通过标准请求读取。
业务事件是否进入 LLM,必须由客户端宿主根据用户授权、安全策略和成本预算决定。将资源订阅、缓存刷新、代理唤醒和工具执行分成清晰层次,既能利用服务端主动通知,又不会把任意外部事件变成不受控的模型行动。