看到"Pydantic AI 已支持 MCP SDK v2",最容易的动作是放宽依赖范围,然后看一次 Tool Call 能不能跑通。问题是,真正会静默漂移的常常不是连接,而是 Sampling、Elicitation、Logging 和 Tasks 语义。
核心判断:FastMCP 3 和 4 应当是两个独立测试宇宙。先锁依赖,再盘能力、分 Tasks,最后用回滚门禁决定是否切流。

一、"支持 MCP SDK v2"到底意味着什么
Pydantic AI v2.29.0 让同一套 MCPToolset API 同时面向 FastMCP 3 和 FastMCP 4 / MCP Python SDK v2。它还将 FastMCP 依赖范围放宽到 >=3.3.0,<5,用兼容层处理字段命名和 Server Metadata 差异。
这是重要的兼容基线,但不是无条件切换信号:
- FastMCP 4 仍是需要显式选入的预发布线;
- 现代 Session 不按旧方式处理 Server-Initiated Sampling、Elicitation 和
log_level; - FastMCP 4 Tasks 走 SEP-2663,显式
use_task=True需要fastmcp-tasks/mcp-tasksExtra。
所以,"能连上"、"能列工具"和"能调一次"只是三个弱信号。
二、先把能力分叉摆到台面上
| 检查项 | FastMCP 3 线 | FastMCP 4 线 | 迁移门禁 |
|---|---|---|---|
| 安装 | 稳定默认线 | 预发布,显式选入 | 分开约束和 Lockfile |
Sampling / Elicitation / log_level |
旧协议路径 | 现代 Session 不按旧方式处理 | 有真实依赖就先阻断 |
| Tasks | SEP-1686 | SEP-2663 | 分别回归全生命周期 |
| 显式 Task | FastMCP 3 Tasks Extra | fastmcp-tasks |
单独安装与审计 |
| 回退 | 保留已验证版本 | 新线不达标即停 | 切流前演练 |
如果项目只用基本 Tool Call,它的迁移面会小很多。如果它依赖采样、结构化补充信息或长任务,就必须用真实 Server 和 Transport 重跑回归。
yaml
include:
- lane: fastmcp3
fastmcp: ">=3.3,<4"
task_semantics: "SEP-1686"
- lane: fastmcp4
fastmcp: ">=4,<5"
prerelease: true
task_semantics: "SEP-2663"
三、四阶段,每一步都要有过关证据
阶段 1:锁依赖
最小动作:为 FastMCP 3 和 4 分开约束、Lockfile 或 CI Matrix,记录 Pydantic AI、FastMCP、MCP SDK 和 Tasks 扩展的实际解析版本。
过关证据:两条日志都能输出完整版本组合。
适用边界:不设上限不是兼容策略。
阶段 2:盘能力
最小动作:枚举真实 Server 使用的 Sampling、Elicitation、Logging、Auth、Transport 和 Metadata。
过关证据:正常样本、拒绝样本和降级样本全部存在,警告能阻断 CI。
适用边界:连接成功不代表能力一致。
阶段 3:分 Tasks
最小动作:把普通 Tool Call、Server 宣告的长任务和显式 use_task=True 拆开。
过关证据:Task 创建、结果、取消、超时、重连和终态都能回读;需要扩展时,环境中确实有 fastmcp-tasks。
适用边界:不用 FastMCP 3 的 prefer_tasks 心智模型套 FastMCP 4。
阶段 4:留回滚
最小动作:保留旧 Lockfile,将警告、错误率、Task 终态完整性和回退动作放进同一门禁。
过关证据:注入一个不兼容样本后,系统能停止扩量并切回旧线。
适用边界:回滚不能依赖临时重新解算依赖。
四、本地门禁应该"失败关闭"
下面是框架无关的本地发布策略,不是 Pydantic AI 源码。它只做一件事:已知不兼容能力或 Tasks Extra 缺失时直接阻断。
python
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.5、pytest 8.4.2 跑了 7 个样本:FastMCP 3 基线、FastMCP 4 基本 Tool Call、Sampling / Elicitation / log_level 阻断、Tasks Extra 缺失与存在。
text
....... [100%]
7 passed in 0.01s
Ruff 0.16.3、BasedPyright 1.39.10、py_compile 与代码规则审计均通过。这是本地策略证据,不是 FastMCP 4 互操证据。
六、一张可执行检查表
- 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.0、FastMCP 4 / MCP SDK v2 支持 PR、Pydantic AI MCP Client 文档。
验证边界:事实基于 2026-08-14 对官方 Release、PR 与文档的核验;2026-08-20 续跑时官方站点被网络层阻断,不声称
v2.29.0仍是当前最新版。未安装 FastMCP 4 / MCP SDK v2,未验证真实 Tasks、Sampling、Elicitation 或 Transport 互操。