RH
RouterHub AI API 中转站导航与平台推荐
博客

Agent Framework 的 MCP 客户端生命周期

Agent Framework Python 1.23 把 MCP 连接分成框架所有与调用方所有两条路径,升级时必须同步调整依赖、构造方式和关闭边界。

muchacha 2026-10-28 02:25:59

正文

Agent Framework Python 1.23 的 MCP 集成已经从低层 ClientSession 迁到 MCP SDK 2.x 的高层 Client:框架创建连接时由工具管理 url 或 command,应用自己创建连接时则必须先进入 mcp.Client,再把它传给工具,并让它覆盖完整的 Agent 或 Workflow 执行期。对开发者来说,这不是一次只改依赖版本的升级,而是把连接的创建者、使用者和关闭者明确分开。

1.23 具体改变了什么

2026 年 10 月 22 日发布的 Agent Framework Python 1.23.0 将 MCP 依赖约束切到 mcp>=2.2.0,<3,并把标准 MCP 操作交给 SDK 的高层 Client。官方升级记录列出的行为包括协议协商、多轮往返请求、响应缓存和订阅管理;这些能力不再通过 Agent Framework 自己拼接低层会话来实现。对应的 GitHub 变更还迁移了 HTTP 传输类型、结果封装、工具与提示词适配以及服务端承载层,并移除了不再支持的旧 Tasks 接口。

这次变化的重点不是“增加一个新的工具类”,而是连接边界发生了变化。过去的代码可能把 ClientSession 当成所有 MCP 操作的通用入口,再由框架围绕它补齐连接和重试逻辑。1.23 的正式设计把高层 Client 作为现代路径:它负责 MCP SDK 自己的协议行为,Agent Framework 负责工具封装、结果转换、目录刷新、认证策略和框架侧的生命周期协调。官方 ADR 明确写出,框架不应该只用 Client 做协商、随后又从 client.session 取回低层会话执行操作,否则会绕过高层 SDK 的多轮往返和缓存行为。

这也解释了为什么版本升级后不能只运行一次 pip install -U agent-framework 就结束。应用需要先判断自己属于哪一种连接所有权:如果让框架创建传输,就继续把地址或命令交给 MCP 工具;如果应用需要自定义或复用高层客户端,就由应用负责进入和关闭 Client,工具只使用传入的客户端。两条路径都能工作,但不能把两种配置混在同一个构造调用里。

如果项目通过中转层或多个模型入口运行 Agent,建议先从模型与平台目录确认实际使用的框架版本与接入对象,再把 MCP 连接生命周期单独做成测试;不要因为普通模型请求成功,就推断 MCP 的协议协商、订阅或缓存路径也已经兼容。

框架所有的连接:让工具管理传输

最简单的路径是把 url= 或 command= 交给工具。以远程 Streamable HTTP 服务为例,核心结构如下:

from agent_framework import Agent, MCPStreamableHTTPTool

async with MCPStreamableHTTPTool(
    name="docs",
    url="https://example.com/mcp",
) as mcp_tool:
    agent = Agent(
        client=chat_client,
        tools=mcp_tool,
        instructions="只使用文档工具回答问题。",
    )
    result = await agent.run("查询项目的安装要求")

在这条路径中,MCPStreamableHTTPTool 负责创建并进入高层 MCP Client,框架也负责在工具退出时清理它。stdio 场景对应的是 MCPStdioTool 和 command=。应用不需要提前创建 Client,也不应该同时传入一个已经进入的 mcp_client 再保留 url 或 command 作为另一套配置。

这样做适合连接只属于一个 Agent 或一次工作流的场景。连接的配置集中在工具构造函数中,失败时由框架处理创建和清理,调用方不需要暴露 MCP SDK 的内部会话。升级时应重点检查三件事:依赖解析结果是否真的落在 mcp 2.x;是否还在直接访问工具的 .session 属性;是否有代码把工具退出和工作流结束拆成了两个不一致的清理路径。

官方升级指南还指出,ClientSession 和 MCPTool.session 属于兼容路径,并不获得高层 Client 的缓存和自动多轮往返能力。如果旧代码依赖 .session 来发起额外操作,不能把它机械替换成 mcp_client.session;应改为使用公开的工具操作边界,或者明确迁移到调用方拥有的高层 Client。

调用方所有的连接:Client 要覆盖完整运行期

需要复用客户端、注入自定义认证、控制传输配置,或者让同一个高层 Client 覆盖一整个工作流时,应采用调用方所有的路径。Agent Framework 1.23 的官方工作流示例采用了下面的结构:

from agent_framework import Agent, AgentResponse, MCPStreamableHTTPTool
from agent_framework_orchestrations import SequentialBuilder
from mcp import Client

async with (
    Client(server="https://learn.microsoft.com/api/mcp") as mcp_client,
    MCPStreamableHTTPTool(
        name="ms_learn",
        mcp_client=mcp_client,
    ) as mcp_tool,
):
    researcher = Agent(
        client=chat_client,
        name="researcher",
        tools=mcp_tool,
        instructions="使用文档工具完成资料检索。",
    )
    writer = Agent(
        client=chat_client,
        name="writer",
        instructions="把检索结果整理成技术说明。",
    )
    workflow = SequentialBuilder(
        participants=[researcher, writer],
    ).build()
    result = await workflow.run("解释项目的安装方式")

这里有一个容易被忽略的顺序:先进入 Client,再构造 MCPStreamableHTTPTool,最后才创建或运行使用该工具的 Agent 和 Workflow。Client 的上下文必须覆盖整个工作流,而不是只覆盖工具构造函数。工作流结束后,工具包装器和调用方创建的 Client 按嵌套顺序退出,分别完成自己的清理。

调用方拥有高层 Client 时,工具不再要求占位用的 url 或 command。官方 PR 9532 特别修正了此前“传入 Client 但仍要求框架传输参数”的边界,并规定混用所有权时应直接报错,而不是静默忽略某些配置。因此,下面这种写法不应保留:

# 不要把两套所有权配置混在一起
MCPStreamableHTTPTool(
    name="docs",
    url="https://example.com/mcp",
    mcp_client=mcp_client,
)

应用如果已经进入了 mcp.Client,就让它成为唯一的传输与认证配置来源。工具负责把 MCP 能力暴露给 Agent,但不接管这个 Client 的关闭责任。相反,如果使用 url=,就不要在外层再维护一个看似共享、实际没有被工具使用的 Client。

旧代码应该怎样迁移

第一步是锁定变更范围,而不是立即修改调用点。搜索依赖文件、构造函数、工具工厂和测试中的 ClientSession、.session、MCPStreamableHTTPTool、MCPStdioTool、url=、command= 与 mcp_client=。把每个连接标记为“框架创建”或“调用方创建”,同时记录它的认证方式、使用范围和关闭位置。仅看主业务函数容易漏掉后台任务、并行 Agent 和测试夹具中的连接。

第二步是把依赖和协议能力放在同一个验证环境里。确认安装结果满足 mcp>=2.2.0,<3,然后用一个只读工具分别验证:首次连接能否完成协商;一次工具调用能否返回结果;需要多轮交互的请求是否沿着高层 Client 路径执行;工作流结束后连接是否关闭。不要把低层 ClientSession 的测试结果当作高层 Client 迁移成功,因为两者的缓存、MRTR 和生命周期语义不同。

第三步是根据所有权写回滚方案。框架所有路径的回滚点是工具构造参数和框架版本;调用方所有路径的回滚点还包括 Client 的进入位置、身份与传输配置。若应用在一个进程里复用多个工作流,不要把一个已关闭的 Client 放回全局缓存,也不要让一个工作流提前关闭另一个工作流仍在使用的 Client。需要共享时,应把共享对象的存活范围提升到所有消费者都完成之后,并为取消、连接失败和异常退出写测试。

这次升级还带来几个明确的边界:WebSocket 传输已从该 Python 集成中移除,MCP Tasks 当前不在支持范围内,服务端发起的 sampling 被标记为弃用。它们不是通过重试或替换一个参数就能恢复的能力。如果应用依赖长时间任务或人工输入流程,应先区分它使用的是 MCP 基础多轮往返,还是 Tasks 扩展;不要看到 InputRequiredResult 就把它当成 Tasks 的 input_required 状态。与权限逐步升级、工具调用失败相关的排查,可以结合站内的MCP 工具权限升级说明一起检查,但连接生命周期仍应以 Agent Framework 1.23 的官方设计为准。

一份可执行的升级验收表

可以把下面的检查加入 CI 或发布前脚本:

  1. 依赖锁文件中明确记录 Agent Framework Python 1.23 及其 mcp 2.x 范围,构建环境不依赖开发机上的旧包。
  2. 每个 MCP 工具只有一种连接所有权,框架所有的调用只传 url 或 command,调用方所有的调用只传已进入的 mcp_client。
  3. 调用方所有的 Client 覆盖整个 Agent 或 Workflow 执行期,取消、异常和正常返回都会执行关闭。
  4. 新代码不把 ClientSession 或 .session 当作高层 MCP Client 的替代品;旧兼容路径要有明确的迁移记录。
  5. 测试至少覆盖连接协商、工具调用、工作流中的连续使用、连接失败和任务取消,不只测试一次成功的文本回答。
  6. 对 WebSocket、Tasks 和 sampling 相关代码做显式盘点,确认没有把不支持的扩展继续写进运行时路径。

Agent Framework 1.23 的实质变化,是把 MCP 的复杂度从“每个集成自己猜会话怎么用”收束到清晰的所有权协议:框架创建的连接由框架管理,调用方创建的高层 Client 由调用方管理,而低层 ClientSession 只作为有能力差异的兼容路径保留。按照这个边界迁移,升级工作就不再是盲目替换导入,而是可以逐项验证依赖、构造参数、运行范围和关闭责任。

参考来源