Pydantic AI v2.29.0 已经让同一套 MCPToolset API 同时面向 FastMCP 3 和 FastMCP 4 / MCP Python SDK v2。但“API 兼容”不等于“业务行为完全一致”。

核心判断:不要只改依赖上限。把 FastMCP 3 和 4 拆成两条 CI 测试线,再分别检查能力、Tasks 语义和回滚。

FastMCP 3/4 双测试线迁移图

一、Pydantic AI 2.29.0 改了什么

官方 Release 把 FastMCP 4 / MCP SDK v2 支持带进了 MCPToolset,同时保留 FastMCP 3 兼容。实现上将 FastMCP 依赖范围放宽到 >=3.3.0,<5,并用兼容层处理字段命名和 Server Metadata 差异。

但官方 PR 也明确了边界:

  • FastMCP 4 仍是需要显式选入的预发布线;
  • 现代 Session 不按旧方式处理 Server-Initiated Sampling、Elicitation 和 log_level
  • FastMCP 4 的 Tasks 走 SEP-2663,显式 use_task=True 需要 fastmcp-tasks / mcp-tasks Extra。

因此,只验证“能列出工具”或“能调一次 Tool”还不够。

二、先冻结两条测试线

检查项FastMCP 3FastMCP 4 / MCP SDK v2迁移动作
安装稳定默认线预发布,显式选入分开约束和 Lockfile
Sampling / Elicitation / log_level旧协议路径现代 Session 不按旧方式处理业务依赖存在就先阻断
TasksSEP-1686SEP-2663重测创建、取消、超时和终态
显式 Task 扩展FastMCP 3 Tasks Extrafastmcp-tasks单独安装与审计
回退保留已验证版本新线不达标即停切流前演练

两条线要记录 Pydantic AI、FastMCP、MCP SDK 和 Tasks 扩展的完整解析结果。不设上限不是兼容策略,只是把决定权交给下一次安装。

include:
  - lane: fastmcp3
    fastmcp: ">=3.3,<4"
    task_semantics: "SEP-1686"
  - lane: fastmcp4
    fastmcp: ">=4,<5"
    prerelease: true
    task_semantics: "SEP-2663"

三、四阶段迁移门禁

阶段 1:锁依赖

最小动作:分开两条约束、Lockfile 或 CI Matrix。

过关证据:每条线的日志都能输出完整版本组合。

适用边界:不用“在我电脑上解出了”代替锁定依赖。

阶段 2:盘能力

最小动作:枚举真实 Server 所用的 Sampling、Elicitation、Logging、Auth、Transport 和 Metadata。

过关证据:每项都有正常与拒绝 / 降级样本,警告能让 CI 失败。

适用边界:有业务依赖就先阻断切换,不强行绿灯。

阶段 3:分 Tasks

最小动作:把普通 Tool Call、Server 宣告的长任务和显式 use_task=True 拆成三组用例。

过关证据:任务创建、结果、取消、超时、重连和终态全部可观测。

适用边界:FastMCP 3 的 prefer_tasks 不能直接当成 FastMCP 4 的契约。

阶段 4:留回滚

最小动作:保留旧 Lockfile,将警告、错误率、Task 终态完整性和回退动作放进同一发布门禁。

过关证据:人为注入一个不兼容样本后,系统能停止扩量并切回旧线。

适用边界:回滚不能依赖临时重新解算依赖。

四、用失败关闭策略拦截误切换

下面不是 Pydantic AI 源码,而是一个本地发布门禁。它的任务是:FastMCP 4 新线仍要求旧能力,或显式 Task 调用缺少 Extra 时,直接阻断。

def evaluate(case: MigrationCase) -> GateResult:
    match case.line:
        case FastMCPLine.V3:
            return GateResult(GateStatus.PASS, (), None)
        case FastMCPLine.V4:
            blockers: list[str] = []
            if case.features.sampling:
                blockers.append("sampling needs a separate compatibility decision")
            if case.features.elicitation:
                blockers.append("elicitation needs a separate compatibility decision")
            if case.features.log_level:
                blockers.append("log_level is not honored by the modern session")
            if case.features.tasks and not case.tasks_extra_installed:
                blockers.append("explicit task calls need the mcp-tasks extra")
            status = GateStatus.BLOCK if blockers else GateStatus.PASS
            return GateResult(status, tuple(blockers), None)
        case unreachable:
            assert_never(unreachable)

真实系统还要把警告、依赖解析结果和 MCP 互操测试接进 CI。

五、7 个本地策略样本

本地用 Python 3.13.5pytest 8.4.2 验证了 FastMCP 3 基线、FastMCP 4 基本 Tool Call、三项旧能力阻断、Tasks Extra 缺失与存在共 7 个样本。

.......                                                                  [100%]
7 passed in 0.01s

Ruff 0.16.3、BasedPyright 1.39.10py_compile 和代码规则审计同时通过。这只能证明本地策略逻辑稳定,不能代替真实 MCP Server 互操。

六、发布前检查表

  • FastMCP 3 / 4 的 Lockfile 和 CI 已分开;
  • Sampling、Elicitation、Logging、Auth 和 Transport 已盘点;
  • 警告能阻断发布,不把连接成功当能力一致;
  • SEP-1686 与 SEP-2663 的 Tasks 分别回归;
  • 显式 FastMCP 4 Task 已安装并审计 fastmcp-tasks
  • 真实 Server / Transport 互操通过;
  • 旧 Lockfile 回退已演练。

官方来源:Pydantic AI v2.29.0FastMCP 4 / MCP SDK v2 支持 PRPydantic AI MCP Client 文档

验证边界:事实基于 2026-08-14 对官方 Release、PR 与文档的核验;2026-08-20 续跑时官方站点被网络层阻断,因此不声称 v2.29.0 仍是当前最新版。未安装 FastMCP 4 / MCP SDK v2,未执行真实 Tasks、Sampling、Elicitation 或 Transport 互操。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐