第3章 MCP Client¶
学习目标¶
- 掌握使用
mcpPython SDK 实现 Client 的完整流程 - 理解 Client 与 Server 的连接建立、能力协商、消息路由
- 能够使用 OpenAI Agent SDK 集成 MCP(HostedMCPTool / MCPServerStreamableHttp)
- 掌握连接管理、工具发现、工具调用的工程实践
- 构建一个能自动发现并调用 MCP 工具的 Agent
问题导入¶
第 2 章我们造好了"工具供给侧"——MCP Server。但工具只有被 Agent 调用才能产生价值,这就需要 MCP Client。一个直观的问题是:为什么不能让 Agent 直接读 Server 的代码来发现工具?因为 Server 可能跑在另一台机器上,可能是别人开源的,你根本看不到源码。Client 通过标准协议完成"发现—协商—调用",让 Agent 无需关心 Server 的实现细节。
更进一步,OpenAI Agent SDK 把 MCP Client 封装为 HostedMCPTool,让 Agent 把整个 MCP Server 当作"一个超级工具"来用——一次配置即可获得 Server 上所有工具的能力。本章我们从底层 Client API 讲到上层 Agent 集成,覆盖完整的 Client 侧工程。
核心概念¶
1. Client 的核心职责¶
MCP Client 在 Host 内部承担四件事:
- 连接管理:与 Server 建立 stdio 或 HTTP 连接,维持会话。
- 能力协商:握手阶段交换双方支持的协议版本与原语集合。
- 原语发现:调用
list_tools/list_resources/list_prompts获取 Server 暴露的能力。 - 调用转发:把 Agent 的调用请求通过
call_tool转发给 Server,并返回结果。
2. 底层 Client API:ClientSession¶
mcp.ClientSession 是协议层的客户端,直接对接 JSON-RPC 消息。它需要配合一个传输层(stdio 或 HTTP)使用。
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
params = StdioServerParameters(command="python", args=["server.py"])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize() # 握手
tools = await session.list_tools()
stdio_client 负责拉起子进程并管理 stdio 管道,ClientSession 在其上建立协议会话。HTTP 模式则用 streamablehttp_client。
3. OpenAI Agent SDK 集成:HostedMCPTool¶
OpenAI Agent SDK(agents 包)提供了更上层的封装。HostedMCPTool 把一个 MCP Server 包装成一个 Agent 可用的工具集,自动完成发现与转发。
from agents import Agent, Runner
from agents.mcp import HostedMCPTool, MCPServerStreamableHttp
# 连接一个 HTTP 模式的 MCP Server
mcp_server = MCPServerStreamableHttp(
params={"url": "http://localhost:8000/mcp"},
cache_tools_list=True,
)
await mcp_server.connect()
# 把整个 Server 转成 Agent 可用的工具集合
mcp_tool = HostedMCPTool(server=mcp_server)
agent = Agent(name="助手", model="gpt-4.1", tools=[mcp_tool])
HostedMCPTool 的优势是:Agent 不需要逐个声明工具,SDK 在运行时自动从 Server 拉取工具列表并暴露给模型。
4. MCPServerStdio 与 MCPServerStreamableHttp¶
Agent SDK 提供两个 Server 连接器:
| 连接器 | 适用 | 特点 |
|---|---|---|
MCPServerStdio |
本地 Server | 拉起子进程,低延迟 |
MCPServerStreamableHttp |
远程 Server | HTTP 连接,可鉴权 |
二者都继承自 MCPServer,对外接口一致,便于切换。
5. 连接生命周期管理¶
MCP Client 是有状态的(需维持会话),因此生命周期管理很关键:
- 连接:
connect(),建立底层传输并完成握手。 - 复用:一个 Server 连接可被多个 Agent 共享,避免重复握手。
- 断开:
cleanup()或退出async with上下文,释放资源。 - 重连:HTTP 模式下网络抖动需重连,建议用指数退避。
cache_tools_list=True 可缓存工具列表,避免每次对话都重新发现,但 Server 更新工具后需手动 invalidate。
原理图¶
sequenceDiagram
participant Agent as Agent(Host 内)
participant Tool as HostedMCPTool
participant Conn as MCPServerStreamableHttp
participant Server as MCP Server(远程)
Agent->>Tool: 配置工具
Tool->>Conn: connect()
Conn->>Server: initialize(握手/能力协商)
Server-->>Conn: 协议版本 + capabilities
Conn->>Server: list_tools()
Server-->>Conn: [toolA, toolB, toolC]
Tool-->>Agent: 暴露工具集
Note over Agent,Server: 用户提问,模型决定调用 toolA
Agent->>Tool: call_tool("toolA", args)
Tool->>Conn: call_tool
Conn->>Server: tools/call(JSON-RPC)
Server-->>Conn: 结果内容
Conn-->>Tool: CallToolResult
Tool-->>Agent: 工具结果
Agent->>Agent: 模型基于结果生成回复
图:Client-Server 交互全流程——握手、发现、调用、回填
示例代码¶
下面演示两种 Client 集成方式:底层 ClientSession 与上层 HostedMCPTool。
# mcp_client_demo.py
# pip install "mcp[cli]" openai-agents
import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp, MCPServerStdio
# ---------- 方式一:底层 ClientSession 直接调用 ----------
async def raw_client_demo():
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async with streamablehttp_client("http://localhost:8000/mcp") as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print("发现工具:", [t.name for t in tools.tools])
# 调用第一个工具
result = await session.call_tool(
tools.tools[0].name, {"city": "北京"}
)
print("调用结果:", result.content[0].text)
# ---------- 方式二:Agent SDK 集成 ----------
async def agent_demo():
# 连接远程 HTTP Server
http_server = MCPServerStreamableHttp(
params={"url": "http://localhost:8000/mcp"},
cache_tools_list=True,
)
await http_server.connect()
# 也可同时连接本地 stdio Server
stdio_server = MCPServerStdio(
params={"command": "python", "args": ["enterprise_server.py"]}
)
await stdio_server.connect()
agent = Agent(
name="企业助手",
instructions="你是企业内部助手,优先使用 MCP 工具回答问题。",
model="gpt-4.1",
mcp_servers=[http_server, stdio_server], # 直接挂载多个 Server
)
result = await Runner.run(
agent, "帮我查一下 cpu 指标,并创建一个标题为'CPU 告警'的工单"
)
print("Agent 回复:", result.final_output)
# 清理连接
await http_server.cleanup()
await stdio_server.cleanup()
if __name__ == "__main__":
# 选一种方式运行
asyncio.run(agent_demo())
关键点说明:
mcp_servers参数让 Agent 自动发现所有挂载 Server 的工具,无需手动转HostedMCPTool。- 多个 Server 可同时挂载,工具名冲突时 SDK 会加前缀。
Runner.run是异步的,内部会处理"模型决策→工具调用→结果回填→继续推理"的循环。
企业案例¶
案例 1:Agent 集成 MCP¶
某企业客服 Agent 需要查订单、查物流、创工单。过去这些能力散落在三个微服务,Agent 代码里硬编码了三套调用逻辑。改造后,三个微服务各自暴露 MCP Server,Agent 通过 mcp_servers 一次挂载,工具发现与调用全自动,新增"退款查询"能力时只需让财务团队上线一个 MCP Server,Agent 代码零改动。
案例 2:多工具聚合 Host¶
某研发效能平台构建了一个"超级 Host",同时连接 GitHub MCP Server、Jira MCP Server、内部监控 MCP Server。开发者用自然语言说"看看昨天的 PR 是否触发了告警",Host 自动从 GitHub 拉取 PR 列表,再交叉查询监控 Server 的告警记录,最后由模型汇总分析。
案例 3:连接池化¶
某高并发场景下,Agent 平台为每个用户会话创建一个 Agent,每个 Agent 都要连 MCP Server。为避免连接爆炸,平台实现了"连接池":多个 Agent 共享同一组 Client 连接,按 Server 实例复用,连接数从万级降到十级。
常见问题¶
Q1:HostedMCPTool 和直接用 mcp_servers 有什么区别?
mcp_servers 是更便捷的写法,SDK 内部会自动创建 HostedMCPTool。如果你需要对工具做过滤、重命名等定制,应手动用 HostedMCPTool 包装。
Q2:工具名冲突怎么办?
SDK 会用 Server 名作为前缀消歧,例如 github__search 与 jira__search。也可在 HostedMCPTool 中配置别名。
Q3:cache_tools_list 何时该开?
Server 工具列表稳定不变时开启,减少每次对话的发现开销。若 Server 会动态增减工具,应关闭缓存或手动调用 invalidate_tools_list()。
Q4:HTTP Server 重连怎么做? SDK 内部有简单重试,但生产环境建议在应用层捕获连接异常,用指数退避重连,并在重连失败时降级(移除该 Server 的工具)。
Q5:一个 Agent 挂太多 Server 会怎样? 工具过多会导致模型选择困难、上下文膨胀、首 token 延迟增加。建议按任务场景动态加载 Server,或用"路由 Agent"先决定需要哪些工具。
面试题¶
1. MCP Client 的四大职责? 答:连接管理、能力协商、原语发现、调用转发。
2. ClientSession 与 HostedMCPTool 的关系?
答:ClientSession 是协议层客户端,直接操作 JSON-RPC;HostedMCPTool 是 Agent SDK 的上层封装,内部使用 Client 连接,把 Server 的工具暴露给 Agent 模型。
3. MCPServerStdio 与 MCPServerStreamableHttp 如何选?
答:本地工具用 Stdio(拉子进程、低延迟);远程或需鉴权的 Server 用 StreamableHttp。
4. 工具发现结果可以缓存吗?有什么风险?
答:可以,用 cache_tools_list=True。风险是 Server 增删工具后缓存过期,需手动 invalidate_tools_list() 或关闭缓存。
5. 多个 Agent 共享一个 Server 连接有什么好处? 答:减少连接数、降低握手开销、便于集中鉴权与监控。生产环境建议做连接池。
练习题¶
- 用
ClientSession写一个脚本,连接第 2 章的enterprise_server.py,打印出所有工具、资源、提示的列表。 - 用 Agent SDK 构建一个 Agent,同时挂载本地 stdio Server 和远程 HTTP Server,让模型回答"查 cpu 指标并读取 onboarding 文档"。
- 实现一个简单的连接池类,封装
MCPServerStreamableHttp的connect/cleanup,支持多个 Agent 复用同一连接。
小结¶
本章从底层 ClientSession 讲到上层 Agent SDK 集成,覆盖了 MCP Client 侧的连接、发现、调用全流程。核心要点:ClientSession 是协议层基础;HostedMCPTool 与 mcp_servers 是 Agent 集成的便捷方式;HTTP 模式需关注重连与连接复用;工具过多应分组加载。下一章我们将进入实战,调用真实的向量数据库、浏览器、文件系统等 MCP Server。