ValueError: Context is not available outside of a request

这个报错通常出现在 MCP Python SDK 2.x 中,为资源模板(resource template)或提示(prompt)处理器标注了带参数的 Context[LifespanContextT] 时——服务器注入的 Context 被 Pydantic 重新构造,请求级私有状态丢失。优先排

快速结论:这个报错通常出现在 MCP Python SDK 2.x 中,为资源模板(resource template)或提示(prompt)处理器标注了带参数的 Context[LifespanContextT] 时——服务器注入的 Context 被 Pydantic 重新构造,请求级私有状态丢失。优先排查处理器签名里 ctx 的类型注解是否为参数化泛型,以及是否读取了 ctx.request_context / ctx.session / ctx.request_id。

适用环境:MCP Python SDK 2.0.0 以及当时 main 分支(commit a4f4ccd091138771535e17191123f20b30fda68e);Python 3.12.13;Pydantic 2.12.5。Issue 未提供操作系统、CUDA 或显卡信息。

最快修复方案:Issue 讨论中没有给出可由用户自行执行的一步修复方案,最终由维护者在 PR #3624 中修复(ctx: Context[T] 在 prompt/resource template 中现在能拿到实时请求上下文)。建议升级到包含该修复的 SDK 版本。作为临时规避,可优先尝试把处理器签名中的 Context[LifespanContextT] 改为不带参数的 Context——Issue 明确指出未参数化的 Context 碰巧可以正常工作,但需注意这属于未在 Issue 中验证的规避手段。

注意事项:该行为在修复前被官方记录为已知限制(docs/handlers/lifespan.md、docs/migration.md),且 tests/docs_src/test_lifespan.py 当时断言参数化上下文在资源与提示中会失败——也就是说旧版本上的失败是“预期行为”而非偶发 bug。修复走的是“Context 将已有实例校验为自身”的路线,与社区此前提出的 SkipValidation PR 不同;使用方无需修改 prompt/template 代码。若仍复现,应开新 Issue。

问题场景

在 MCP Python SDK 中构建 MCPServer,并使用 lifespan 提供类型化的生命周期状态。处理器写法遵循文档中推荐的 Context[LifespanContextT] 泛型模式:

  • 资源模板处理器:@server.resource("probe://{name}"),签名为 async def resource(name: str, ctx: Context[str]);
  • 提示处理器:@server.prompt("probe"),签名为 async def prompt(name: str, ctx: Context[str])。

当处理器内访问 ctx.request_context(进而读取 lifespan_context)时立即抛出 ValueError。而完全等价的工具(tool)处理器却可以正常工作,因此该问题专门影响动态资源与提示。

报错原文

ValueError: Context is not available outside of a request

原因分析

根因在 SDK 内部的对象重建:

  • ResourceTemplate.from_function 与 Prompt.from_function 会正确地把检测到的 Context 参数从对外参数 schema 中排除,但随后用 pydantic.validate_call 包裹原始处理器。
  • 调用时 SDK 把实时的 Context 注入被包裹的处理器,于是 Pydantic 又对它做了一次校验。
  • 对于泛型注解,Pydantic 会把运行时未参数化的实例转换为 Context[str, Any],从而创建一个新的模型实例,且不会携带 Context 的私有属性 _request_context、_mcp_server 等。

Issue 中给出的对象身份对比很直观:

Context       -> same object, request state present
Context[str]  -> new object, request state absent

工具处理器不走这条路径,因为 FuncMetadata.call_fn_with_arg_validation 只校验客户端传入的参数,注入值直接交给原始处理器,所以不受影响。可优先尝试的方向是:让 SDK 注入的上下文绕过校验、保留对象身份,同时继续校验/强制转换客户端参数——这也是最终修复所坚持的边界。

环境排查

  • 确认 MCP Python SDK 版本:2.0.0 或对应 main commit 存在该问题;确认是否已升级到包含 PR #3624 的版本。
  • Python 版本:Issue 环境为 3.12.13。
  • Pydantic 版本:Issue 环境为 2.12.5,属于触发重建的关键依赖。
  • 检查资源/提示处理器的 ctx 注解:是 Context[LifespanContextT](参数化,触发问题)还是未参数化的 Context(在当时碰巧可用)。
  • 检查处理器是否在调用链中读取了 ctx.request_context、ctx.session、ctx.request_id 或 lifespan 状态;仅断言 ctx 非空的测试会掩盖该问题。
  • Issue 未涉及操作系统、CUDA、显卡或其它依赖,无需在此范围内排查。

解决步骤

  1. 确认复现路径:用带 Context[str] 注解的资源模板 + 提示处理器,并在其中读取 ctx.request_context.lifespan_context,确认抛出 ValueError: Context is not available outside of a request。
  2. 记录当前 SDK 版本与安装来源,确认其是否早于包含 PR #3624 的版本。
  3. 升级到包含修复的 SDK 版本(PR #3624 已合并,ctx: Context[T] 在 prompt 与 resource template 中会收到实时请求上下文,客户端参数的校验与强制转换行为保持不变)。升级后无需修改 prompt/template 代码。
  4. 若暂时无法升级,可优先尝试临时规避:将处理器签名中的参数化注解改为未参数化的 Context(Issue 记录该写法在当时碰巧可用)。这是规避手段而非官方修复,请在你的代码库中标注并在升级后还原为类型化写法。
  5. 如升级后仍在任何位置复现,按维护者要求另开新 Issue,而不是复用 #3235。

验证方法

  • 运行 Issue 中的最小复现脚本:资源与提示两次调用都应返回形如 value:live-state 的内容,而不是抛异常。
  • 在处理器内验证注入对象为服务器创建的同一个上下文实例,而不仅仅是“能读到今天的 lifespan 值”——即 ctx 保留请求级状态(request_context、session、request_id、lifespan 状态)。
  • 确认客户端传入参数的校验与强制转换仍然生效(原本能通过的合法/非法参数用例行为不变)。
  • 确认同步与异步处理器、以及使用 **kwargs 的资源处理器均回归通过。

参考来源

modelcontextprotocol/python-sdk #3235(修复 PR:#3624;相关 PR:#3236)

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 27228

发表回复

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