[v2] Expose request cancellation on MCPServer tool Context

在 mcp==2.1.0 中,通过高层 MCPServer API 注册的工具拿到的 Context 没有公开的 cancel_requested 事件,因此工具无法直接感知客户端取消;优先确认 SDK 版本,并改用 finally 或 CancelledError 做协作式清理。

快速结论:在 mcp==2.1.0 中,通过高层 MCPServer API 注册的工具拿到的 Context 没有公开的 cancel_requested 事件,因此工具无法直接感知客户端取消;优先确认 SDK 版本,并改用 finally 或 CancelledError 做协作式清理。

适用环境:Issue 已确认 mcp==2.1.0、Python 3.12、MCP Python SDK 的 Server / 高层 MCPServer 工具上下文;其他依赖版本未在 Issue 中给出。

最快修复方案:暂无确认的一步修复方案。cancel_requested 未进入公开 Context API;官方在 #3625 中改为补充取消处理文档,指导用 finally(async def 工具)和 anyio.from_thread.check_cancelled()(普通 def 工具)完成清理。

注意事项:取消通知不是响应,处理器无法“回复”取消;能做的只有协作式轮询或依赖任务中断。清理逻辑应保持幂等,因为通知可能在处理器即将结束时到达。文档还指出,部分 Streamable HTTP 场景下处理器不会被取消,需单独处理。

问题场景

使用 mcp==2.1.0,通过高层 mcp.server.mcpserver.MCPServer 注册带注入 Context 的异步工具(例如持有有界子进程、需要清理敏感资源的处理器)。处理器想在客户端取消请求时执行协作式清理,却发现高层 Context 上没有 cancel_requested;底层 mcp.shared.context.BaseContext 却有该事件。

from mcp.server.mcpserver.context import Context as MCPServerContext
from mcp.shared.context import BaseContext

assert not hasattr(MCPServerContext, "cancel_requested")
assert hasattr(BaseContext, "cancel_requested")

报错原文

[v2] Expose request cancellation on MCPServer tool Context
assert not hasattr(MCPServerContext, "cancel_requested")
assert hasattr(BaseContext, "cancel_requested")

原因分析

调度器已经解析 notifications/cancelled,并把取消事件保存在每个请求的 dispatch context 中;高层 MCPServerContext 只是围绕它的一层包装。问题在于 _make_context() 没有把该事件传给 ServerRequestContext,所以工具通过公开 API 观察不到取消信号。Issue 评论也指出这是真实的 API 缺口,但改动本质是一次透传,范围很小。

环境排查

  • 确认安装的 SDK 版本是否为 mcp==2.1.0,或是否已升级到包含取消处理文档/修复的后续版本。
  • 确认 Python 版本为 3.12(Issue 报告环境)。
  • 确认工具是通过高层 MCPServer 注册并注入 Context,而不是直接使用底层 BaseContext。
  • 确认是否依赖私有 session/dispatch 内部属性来读取取消状态;这类做法不受支持。

解决步骤

  1. 先判断工具是否必须依赖取消事件。若只在客户端取消时做资源清理,可优先尝试“可优先尝试”的替代写法:对子进程使用 asyncio.wait_for 加硬超时,并在 finally 中调用 proc.terminate(),保证客户端即使不发取消通知也能清理。
  2. 若需要响应取消,利用 SDK 在支持取消的传输上会取消处理器任务这一点,捕获 asyncio.CancelledError(或使用 anyio CancelScope)执行清理;该方式当前无需遍历私有属性。
  3. 对 async def 工具,在 finally 块中执行清理;对普通 def 工具,按官方 #3625 文档使用 anyio.from_thread.check_cancelled() 判断取消。
  4. 把清理逻辑写成幂等操作,避免取消通知在处理器收尾阶段到达时重复执行。
  5. 若使用 Streamable HTTP,注意文档列出的两种场景下处理器不会被取消,需按文档另行处理。
  6. 如需等待统一的事件式 API,关注 SDK 后续版本说明;Issue 关闭时未把 cancel_requested 暴露到 Context。

验证方法

用一次客户端主动取消(或在支持取消的传输上中断请求)触发工具执行,确认 finally / 取消异常分支中的清理代码确实执行,并且子进程或外部资源被回收;再验证客户端未发送取消即断开时,硬超时与 finally 仍能完成清理。若编写断言检查 hasattr(ctx, "cancel_requested"),在 Issue 关闭时的公开 API 上仍会为 False。

参考来源

modelcontextprotocol/python-sdk #3389

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 31873

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注