MCP Context 丢失请求状态
MCP 工具正常,prompt 或动态资源模板却报 Context is not available outside of a request,核对旧版泛型 Context 重建与 2.3.0 修复。
MCP 工具里读取 lifespan 状态正常,把同样的 ctx: Context[AppState] 放到 prompt 或动态 resource template 中,却报:
ValueError: Context is not available outside of a request
处理函数明明正在回答请求,ctx 也不是 None,仍可能遇到 Python SDK 旧 2.x 的泛型 Context 重建问题。已有 2.0.0 环境反馈;已核对的正式版 2.3.0 包含相应修复。
比较工具、模板和类型标注
先看失败的处理函数是否使用了 Context[T],以及读取的是 ctx.request_context、ctx.session、ctx.request_id 或 lifespan 对象。如果相同应用中的工具处理函数可以读取状态,prompt 或动态资源模板却失败,去掉类型参数改为裸 Context 又能工作,就符合这一旧缺陷的表现。
仅判断 ctx is not None 会漏掉问题:丢失状态的是一个已经重新构造的对象。还应确认调用发生在实际请求内,而不是后台任务、模块导入或自行构造的 Context() 中;后一类场景原本就没有有效请求状态。
这也不同于 FastAPI 挂载后没有启动 session manager 的问题。若堆栈是 Task group is not initialized,应检查宿主 lifespan 的启动方式,不能靠改泛型注解补上生命周期。
请求 Context 为什么会被换掉
旧实现中,prompt 和资源模板使用 Pydantic 验证调用参数。SDK 注入的实时 Context 也经过这一步。运行时的普通 Context 与标注的 Context[AppState] 是不同类,验证会把原对象重建为泛型模型。
新的对象没有带走 _request_context 等私有请求状态,所以后续属性访问报“请求外不可用”。工具参数路径没有以同样的方式重建注入值,因此同一个类型标注在工具上可以正常工作。
除了立即抛错,涉及多轮交互的 prompt 或模板还可能读取不到 input_responses 和 request_state,反复要求输入,直到客户端报 InputRequiredRoundsExceededError。出现这个表现时,也需要核对 Context 是否保留了原请求状态,不能只不断提高交互轮数上限。
保留原对象,也保留参数校验
2.3.0 的 Context 验证器会把已有的 Context 实例原样返回,其他输入仍走正常验证。这样保留了 SDK 创建的请求对象,prompt 和模板的普通客户端参数仍会被校验与转换。
旧部署应选择包含此修复、且接口兼容的发行版,之后保留原有 Context[AppState] 注解。不需要关闭所有 Pydantic 参数验证,也不需要在处理函数里重新创建 Context 或把请求状态搬到全局变量。
暂时不能升级时,裸 Context 是这个旧问题中已知可工作的窄范围绕行方式,但会失去 lifespan 类型参数带来的静态检查与自动补全。应标记这项临时改动,核对实际运行行为,再在兼容升级后恢复类型标注;不能把它当作所有请求上下文错误的修复。
静态资源的接口边界也没有因此改变。正式修复针对可接收 Context 的 prompt 和动态资源模板,并没有让静态资源任意接收请求 Context。
验收要真正读取状态
在获准的测试环境中调用原先失败的 prompt 和动态资源模板,读取预期 lifespan 值,并确认收到的仍是 SDK 注入的原对象。需要多轮输入的处理函数还要检查下一轮能读到前一轮的答案和状态。
随后验证普通参数仍能正常转换,非法参数仍被拒绝,工具路径保持可用。不能以“Context 不为空”或“客户端不再出现内部错误”代替这些检查;重新构造一个看似含有相同字段的对象,也不等于保留了请求身份和完整状态。


