news 2026/8/14 15:18:22

Completions与通知机制:自动补全、进度上报与状态通知

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Completions与通知机制:自动补全、进度上报与状态通知

摘要:MCP Completions原语提供自动补全能力,通知机制支持进度上报和状态推送。本文详解补全请求响应格式、进度通知协议和长任务实时状态更新实现。

Completions与通知机制 自动补全、进度上报与状态通知

我之前给团队写过一个批量数据导出工具,跑一次要好几分钟。用户每次都来问我,到底是卡死了还是在干活。后来我把 MCP 的进度通知接上,界面能实时显示处理到第几条,问题一下子就没了。这篇就来聊聊 MCP 里那些容易被忽略的交互细节,包括自动补全、进度上报、日志推送和资源变更通知,配套可运行的代码。


MCP 通知机制全景

MCP 底层是 JSON-RPC 2.0。请求有 id 要等响应,通知没有 id 也不需要响应,是单向的“即发即忘”消息。这套单向通道撑起了 MCP 里大部分实时交互。

服务端能主动发给客户端的常用通知有这么几类。

通知方法触发时机关键字段
notifications/progress长任务执行中progressToken、progress、total、message
notifications/message服务端打日志level、logger、data
notifications/resources/updated某个资源内容变了uri
notifications/resources/list_changed资源清单变了
notifications/tools/list_changed工具清单变了
notifications/prompts/list_changed提示清单变了
notifications/cancelled取消进行中的请求requestId、reason

记住一点,通知本身不保证送达,网络断了就丢了。所以协议规定进度通知的 progress 值必须单调递增,断了重连也不会回退,客户端按最新值渲染就行。

进度通知实战

进度通知的玩法是“客户端先给令牌,服务端再拿着令牌回传进度”。客户端在请求的_meta.progressToken里塞一个字符串或整数,服务端在处理过程中不断发notifications/progress,把同一个 token 带回来。

协议里 progress 必须递增,total 可以省略(不知道总量时就别填),message 给人看的。我用 FastMCP 写一个长任务工具,配合 Context 对象上报进度和日志。

日志通知与等级控制

日志通知用notifications/message发送,字段是 level、logger、data。level 走的是 syslog 那套,从 debug 到 emergency 共八级。客户端会先发logging/setLevel设一个最低等级,服务端只回传达到等级的日志。

在 FastMCP 里直接用 Context 的快捷方法就行,ctx.info()ctx.debug()ctx.warning()ctx.error()分别对应不同等级。这些日志会作为结构化通知发给客户端,方便在界面上分级展示。

Completions 自动补全

Completions 原语让服务端给 prompt 参数和 resource URI 模板提供补全建议,做出类似 IDE 输入提示的效果。客户端发completion/complete,带一个引用类型和当前输入值,服务端返回最多 100 条建议。

引用类型只有两种,ref/prompt按 prompt 名字引用,ref/resource按 URI 引用。这里有个很多人搞错的地方,Completions 只支持 prompt 参数和 resource URI 模板,不支持 tool 参数补全。我最早以为 tool 参数也能补全,调了半天没反应,翻规范才发现压根没这个能力。

下面用低级 SDK 写一个带补全的服务端,给 code_review 这个 prompt 的 language 参数做补全。

资源与列表变更通知

当服务端的资源内容、资源清单、工具清单或提示清单发生变化,要主动通知客户端刷新缓存。比如后台跑了个任务改了某个配置文件,就发notifications/resources/updated带上 uri,客户端收到后重新读取该资源。新增或删除了工具,就发notifications/tools/list_changed,客户端重新拉取工具列表。

这类通知没有参数体(resources/updated 除外,它带 uri),语义就是“你缓存的清单过期了,重新拉一次”。客户端收到后调对应的 list 方法即可。

完整代码

先装依赖。

pipinstall"mcp[cli]"fastmcp

服务端progress_server.py,用 in-SDK 的 FastMCP 实现长任务进度和日志上报。

# progress_server.py# 进度上报与日志通知的服务端示例importasynciofrommcp.server.fastmcpimportFastMCP,Context# 创建一个命名服务端,名字会显示在客户端里mcp=FastMCP("ProgressDemo")@mcp.tool()asyncdefbatch_export(total:int,ctx:Context)->str:"""模拟批量导出,逐条处理并上报进度和日志。 Args: total: 要处理的记录总数 ctx: MCP 注入的上下文,用来发进度和日志 """# 打一条 info 日志,会作为 notifications/message 发给客户端ctx.info(f"开始批量导出,共{total}条记录")# 逐条处理,模拟耗时操作foriinrange(1,total+1):# 上报进度,参数依次是当前进度、总量、可读消息# report_progress 是协程,需要 awaitawaitctx.report_progress(i,total,f"正在处理第{i}/{total}条")# 每处理 10 条打一条 debug 日志,便于排查ifi%10==0:ctx.debug(f"已处理{i}条,进度{i/total:.0%}")# 模拟单条耗时,真实场景换成实际处理逻辑awaitasyncio.sleep(0.2)# 收尾日志,标记任务结束ctx.info("批量导出完成")returnf"成功导出{total}条记录"if__name__=="__main__":# 默认走 stdio 传输,适合被客户端以子进程方式拉起mcp.run()

客户端progress_client.py,用 standalone FastMCP 的 Client 接收进度回调。

# progress_client.py# 进度通知的客户端示例,连接上面的服务端并接收进度importasynciofromfastmcpimportClientasyncdefon_progress(progress:float,total:float|None,message:str|None):"""进度回调,每收到一条 notifications/progress 就触发一次。 Args: progress: 当前进度值,单调递增 total: 总量,未知时为 None message: 服务端附带的可读消息 """# total 存在时算百分比,否则只显示当前值iftotal:percent=progress/total*100print(f"[进度]{percent:5.1f}%{message}")else:print(f"[进度]{progress}{message}")asyncdefmain():# 用脚本路径构造客户端,会自动以 stdio 方式拉起服务端子进程client=Client("progress_server.py")asyncwithclient:# 调用长任务工具,传入进度回调# progress_handler 会被绑定到客户端生成的 progressToken 上result=awaitclient.call_tool("batch_export",{"total":30},progress_handler=on_progress,)# 打印最终返回值print("最终结果:",result.data)if__name__=="__main__":asyncio.run(main())

补全服务端completion_server.py,用低级 SDK 给 prompt 参数做补全。

# completion_server.py# Completions 自动补全服务端示例,基于低级 SDKimportmcp.server.stdioimportmcp.typesastypesfrommcp.server.lowlevelimportNotificationOptions,Serverfrommcp.server.modelsimportInitializationOptions# 创建低级服务端实例,名字任意server=Server("CompletionDemo")# 预置的编程语言候选词,真实项目可换成数据库查询结果LANGUAGES=["python","pytorch","pyside","javascript","java","rust","ruby"]@server.list_prompts()asyncdefhandle_list_prompts()->list[types.Prompt]:"""声明服务端有哪些 prompt,客户端据此展示可选模板。"""return[types.Prompt(name="code_review",description="代码审查提示模板",arguments=[types.PromptArgument(name="language",description="要审查的编程语言",required=True,)],)]@server.complete()asyncdefhandle_complete(ref,argument,context):"""补全请求处理器,根据引用类型和当前输入返回建议。 Args: ref: 引用对象,PromptReference 或 ResourceReference argument: 当前正在输入的参数,含 name 和 value context: 已解析的其他参数,做多参数联动时有用 """# 只处理对 code_review 这个 prompt 的补全ifisinstance(ref,types.PromptReference)andref.name=="code_review":# 针对 language 参数做前缀匹配ifargument.name=="language":# 用当前输入值做前缀过滤values=[langforlanginLANGUAGESiflang.startswith(argument.value)]# 返回补全结果,values 最多 100 条returntypes.CompleteResult(completion=types.Completion(values=values,total=len(values),hasMore=False,))# 其他情况返回空结果returntypes.CompleteResult(completion=types.Completion(values=[],total=0,hasMore=False))asyncdefrun():"""启动 stdio 服务端,等待客户端连接。"""# stdio_server 提供标准输入输出读写流asyncwithmcp.server.stdio.stdio_server()as(read_stream,write_stream):awaitserver.run(read_stream,write_stream,InitializationOptions(server_name="completion-demo",server_version="0.1.0",capabilities=server.get_capabilities(notification_options=NotificationOptions(),experimental_capabilities={},),),)if__name__=="__main__":importasyncio asyncio.run(run())

效果验证

服务端写好后,先用 Inspector 快速验证进度通知。在项目目录执行下面命令会打开一个可视化调试界面。

mcp dev progress_server.py

在 Inspector 里调用batch_export工具,传入total=30,右侧的通知面板会实时滚动显示notifications/progressnotifications/message,进度条从 0 涨到 100%。

跑客户端脚本验证端到端流程。

python progress_client.py

终端会按 0.2 秒一条的频率打印进度百分比,最后输出“成功导出 30 条记录”。

补全服务端同样用 Inspector 验证。执行mcp dev completion_server.py,在 Prompts 面板选 code_review,在 language 参数框里输入py,会下拉出 python、pytorch、pyside 三个建议。

常见问题与避坑

1. 进度回调始终不触发。我最早用 ClientSession 直接调call_tool,怎么都没收到进度。原因是客户端没把 progressToken 放进请求的_meta,服务端的ctx.report_progress找不到对应 token 就静默丢弃了。用 FastMCP Client 的progress_handler参数会自动注入 token,别自己手搓请求又忘了带。

2. 并发任务进度串台。progressToken 必须在所有活跃请求里全局唯一。我有一次图省事给两个并发任务用了同一个固定 token,结果两条进度线交叉显示。token 用自增计数器或 UUID 生成,任务结束就释放。

3. progress 值回退被客户端忽略。协议规定 progress 必须单调递增。我在重试逻辑里把计数器重置成 0 重新发,客户端直接忽略后到的较小值。重试时要么继续递增,要么发一条全新的任务。

4. 日志发了客户端看不到。日志受logging/setLevel控制。客户端默认可能只收 warning 以上,你发的 info 日志就被过滤了。调试时让客户端把等级设成 debug,或者只用 warning 和 error 发关键信息。

5. 以为 tool 参数也能补全。Completions 只覆盖 prompt 参数和 resource URI 模板,tool 参数没有补全能力。想要 tool 参数提示,得在 tool 的 description 或参数 description 里写清楚枚举值,让模型自己选。

小结

MCP 的通知机制把“请求-响应”之外的实时交互补全了。进度通知靠 progressToken 关联请求,日志通知靠等级过滤,Completions 给 prompt 和 resource 做输入提示,列表变更通知让客户端缓存不过期。用好 Context 对象的report_progressinfo等方法,长任务体验能上一个台阶。下一篇我们换到传输层,看 stdio、SSE 和 Streamable HTTP 三种传输模式各自适合什么场景。


相关推荐

  • JSON-RPC 2.0:MCP通信的底层语言
    • 通知机制实战:长任务进度上报与实时状态推送
    • MCP协议全景:Host、Client、Server架构详解
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/14 15:17:00

JSON-RPC 2.0:MCP通信的底层语言

摘要:MCP协议基于JSON-RPC 2.0进行通信,本文详解JSON-RPC消息格式、请求响应模型、批量调用、错误码定义,以及MCP如何在此基础上扩展出初始化握手和能力协商机制。 JSON-RPC 2.0 MCP通信的底层语言 我第一次抓包看MCP的通信内容时有点懵&…

作者头像 李华
网站建设 2026/8/14 15:12:26

继承(全)

目录 一. 继承的概念及定义 1.1 继承的概念 1.2 继承的机制 1.3 继承的定义 1.3.1 定义格式​编辑 1.3.2 继承父类成员访问方式的变化 1.4 继承类模板 二. 父类和子类对象赋值兼容转换 2.2 类型转换的特殊处理规则 2.3 应用实例 三. 继承中的作用域 3.1 隐藏规则 …

作者头像 李华
网站建设 2026/8/14 15:10:38

王虹、邓煜、张益唐、韦东奕四位数学家的‌核心成果时间线对照表

以下是王虹、邓煜、张益唐、韦东奕四位数学家的核心成果时间线对照表,完整覆盖他们从学术起步到取得里程碑成果的关键节点,清晰呈现不同成长路径的节奏差异。 一、四位数学家核心成果时间线对照表 时间 节点 王虹(调和分析/几何测度论&…

作者头像 李华