MCP 订阅占住云函数
静态 MCP 服务的 subscriptions/listen 长流占住云函数。核对 Python SDK 2.3.0 的 subscriptions=False,让公布能力与处理入口一致,并验收真实客户端。
静态目录的 MCP 服务部署在云函数上,普通工具调用很快结束,subscriptions/listen 却一直挂着,直到函数超时。服务没有要发布的变更,也可能因为 SDK 默认公布订阅能力,让客户端主动打开这些长流。
已有 Python SDK 2.0.0、2.1.1、2.2.0 的 serverless 部署反馈。已核对的正式版 2.3.0 提供 MCPServer(..., subscriptions=False),用于确实不需要变更通知的服务。
无状态与 JSON 响应不等于禁用订阅
subscriptions/listen 的响应本来就是持续开放的事件流:先确认订阅,再等待符合过滤条件的通知。没有变化可发时,它可以一直保持安静,而不是立即返回空结果。
因此,stateless_http=True 或普通请求的 JSON 响应设置,不能证明服务不会打开订阅流。应按真实请求方法查看长期未结束的调用,再核对 server/discover 公布的 listChanged 和 subscribe 能力。
如果工具、资源或 prompt 的目录确实会变化,客户端依赖通知刷新,就不能只为缩短云函数时间关闭它。需要先确定服务契约,再选择支持长连接的部署方式或所需的订阅设计。
在构造服务时明确关闭
对于静态目录且不需要这些通知的服务,2.3.0 的构造方式是:
from mcp.server.mcpserver import MCPServer
server = MCPServer("static-catalog", subscriptions=False)
把这个选项加入原有服务构造,保留实际认证、lifespan、工具注册与部署配置。示例只说明参数位置,不是完整云函数入口。旧版本应先核对接口与依赖兼容性,不能直接照抄后把未知关键字错误当成云平台故障。
subscriptions=None 仍是默认行为,会创建进程内的 subscription bus,不表示关闭。2.3.0 的默认值也没有自动变为 False,升级但不配置时仍会公布并提供订阅。
设置 False 后,2026-07-28 discovery 中的工具、prompt、资源变更标记及资源订阅标记为 false;未被邀请的 subscriptions/listen 请求得到 -32601 Method not found,Streamable HTTP 对应 404,不再为它打开长流。处理函数里的 ctx.notify_* 仍可调用,只是没有订阅者收到通知。
这一开关整体关闭现代订阅入口,没有提供“只留工具变化、关闭资源订阅”的逐项开关,旧协议连接的行为也没有因此统一改变。
拒绝请求与公布能力要一致
只在 ASGI 中间件里拦截 listen,可以结束请求,但如果 discovery 还宣称支持,客户端仍可能不断尝试。正式选项同时改变处理入口和公布能力,不需要删除 _lowlevel_server._request_handlers 里的私有项。
多实例服务还要检查另一种情况:客户端流连在实例 A,工具在实例 B 发布事件,默认进程内 bus 无法跨实例传递。需要通知时,应让实例使用可满足业务要求的共享 bus;不需要时明确关闭。不能把“长流存在但一直没收到事件”都当作云函数超时问题。
验收方法与真实客户端
在获准的测试环境中,先读取 discovery,确认所需能力确实为 false,再检查主动发起的 listen 不会留下开放流。使用真实客户端和云函数适配器检查普通工具调用仍能正常完成,取消、失败和正常请求结束仍能释放各自资源。
若旧订阅已经打开,需要按实际客户端与服务生命周期结束它;新增构造配置不是对现存连接的远程关闭命令。修复后的请求时长、调用数量与费用要由自己的平台数据确认,不能把其他部署中的超时占比当成本站或所有云函数的节省结果。


