9.1 Client 的构造:按参数类型自动选传输


文档摘要

9.1 Client 的构造:按参数类型自动选传输 本节摘要:客户端主线开始。本节讲透 v2 第一类 最聪明的设计——按参数类型自动选传输。传服务端对象→内存传输;传 URL 字符串→流式 HTTP 传输;传传输对象→直用。这个「按类型分发」的设计,让你在不同部署形态间切换几乎零成本——业务代码(调用方法)不变,只改构造参数。读完本节,你能用三种方式构造 Client,并说清它如何自动选传输。 一、Client 的三种构造形态 v2 的 接受单个参数,根据参数类型自动决定用哪种传输: 这三种形态覆盖了所有常见场景,且业务代码完全一致——构造之后的调用方法( 、 等)对三种传输都一样。 二、形态一:服务端对象(内存传输) 最简单的形态——把 实例直接传给 : 这是第 1.3 节的骨架。

9.1 Client 的构造:按参数类型自动选传输

本节摘要:客户端主线开始。本节讲透 v2 第一类 Client 最聪明的设计——按参数类型自动选传输。传服务端对象→内存传输;传 URL 字符串→流式 HTTP 传输;传传输对象→直用。这个「按类型分发」的设计,让你在不同部署形态间切换几乎零成本——业务代码(调用方法)不变,只改构造参数。读完本节,你能用三种方式构造 Client,并说清它如何自动选传输。

一、Client 的三种构造形态

v2 的 Client 接受单个参数,根据参数类型自动决定用哪种传输:

from mcp import Client # 形态一:传服务端对象 → 内存传输 client = Client(my_mcp_server) # 形态二:传 URL 字符串 → 流式 HTTP 传输 client = Client("http://localhost:8000/mcp") # 形态三:传传输对象 → 直用该传输 client = Client(my_transport)

这三种形态覆盖了所有常见场景,且业务代码完全一致——构造之后的调用方法(call_toolread_resource 等)对三种传输都一样。

二、形态一:服务端对象(内存传输)

最简单的形态——把 MCPServer 实例直接传给 Client:

from mcp import Client from mcp.server import MCPServer mcp = MCPServer("Demo") @mcp.tool() def add(a: int, b: int) -> int: return a + b # 内存连接 async with Client(mcp) as client: result = await client.call_tool("add", {"a": 1, "b": 2})

这是第 1.3 节的骨架。Client 看到 mcp 是一个 MCPServer 实例,自动用内存传输——直接函数调用,无序列化、无网络。

适用场景:测试、进程内嵌入。客户端与服务端在同一进程,无需起子进程或开服务。

三、形态二:URL 字符串(流式 HTTP)

最常用的生产形态——传一个 URL 字符串:

from mcp import Client # 连接远程的流式 HTTP 服务 async with Client("http://localhost:8000/mcp") as client: result = await client.call_tool("add", {"a": 1, "b": 2})

Client 看到参数是字符串,自动用流式 HTTP 传输——发 HTTP 请求到指定 URL。这是连接远程 MCP 服务端的标准方式。

适用场景:连接远程服务、生产部署。客户端与服务端在不同进程/机器,通过网络通信。

四、形态三:传输对象(直用)

最灵活的形态——传一个预先配置好的传输对象:

from mcp import Client from mcp.client.stdio import stdio_client, StdioServerParameters # 配置 stdio 传输(连本地子进程服务) stdio_params = StdioServerParameters( command="python", args=["server.py"], env={"DATABASE_URL": "..."} ) # 用 stdio_client 上下文管理器拿到传输 async with stdio_client(stdio_params) as (read, write): async with Client(read, write) as client: # 把传输传给 Client result = await client.call_tool("add", {"a": 1, "b": 2})

这种形态用于需要精细控制传输的场景——如 stdio 要配命令/环境、SSE 要配特殊选项。你先用传输的上下文管理器拿到读写流,再传给 Client

适用场景:stdio 连本地子进程、特殊传输配置、需要传输级控制

五、三种形态的对照

把三种形态放一起对照,差异与适用场景清晰:

形态 参数 自动选的传输 适用场景
服务端对象 MCPServer 实例 内存 测试、进程内嵌入
URL 字符串 "http://..." 流式 HTTP 连远程服务、生产
传输对象 (read, write) 或传输 直用 stdio、特殊配置

关键共性:构造之后的业务代码完全一致。无论用哪种形态,call_toolread_resource 等方法的用法都一样。这是「按类型自动选传输」设计的核心价值——业务代码与部署形态解耦

六、为什么这个设计有价值

「按类型自动选传输」看似只是个语法糖,实际上带来重要的工程价值:

价值 说明
零成本切换部署 同一份业务代码,改构造参数就能切部署形态
降低心智负担 不用记住「连内存要用 X 类、连 HTTP 要用 Y 类」
统一 API 一种 Client,三种传输,调用方法一致
便于测试 测试用内存(快)、生产用 HTTP(远),代码不变

举个例子:你写一个客户端脚本,开发期用内存连测试,生产改成 URL 连远程服务——只改一行构造参数,其余代码不动

💡 技巧:这个设计让你能写「传输无关的客户端代码」。把构造参数提成变量/配置,业务代码只依赖 Client 接口,这样部署形态变化时只改配置,不动业务逻辑。这是良好的客户端架构实践。

七、构造期 vs 连接期

理解 Client 的一个关键:构造期与连接期分离

client = Client(url) # 构造期:只选传输,不开连接 # 此时 client 还没连上 async with client as c: # 连接期:进入上下文,真正打开连接 # 此时才连上,可以调用方法 await c.call_tool(...)

构造期(Client(...))只决定「用哪种传输」,不实际打开连接。连接期(async with ... as ...)才真正打开连接、协商协议、交换能力声明。

这个分离的设计带来好处:构造不失败(不涉及网络),连接失败在 async with 时才暴露,异常处理更清晰。第 9.2 节会详讲连接生命周期。

本节要点回顾

  1. Client 按参数类型自动选传输:服务端对象→内存、URL→流式 HTTP、传输对象→直用。
  2. 形态一(服务端对象):内存传输,适合测试、进程内嵌入。
  3. 形态二(URL 字符串):流式 HTTP,适合连远程服务、生产。
  4. 形态三(传输对象):直用,适合 stdio、特殊配置。
  5. 三种形态的业务代码完全一致,调用方法对传输无感——这是核心价值。
  6. 价值:零成本切换部署、降低心智负担、统一 API、便于测试。
  7. 构造期与连接期分离:构造只选传输不开连接,连接在 async with 时打开。

构造清楚了,下一节讲连接生命周期——async with 里发生了什么、协议版本怎么协商。


发布者: 作者: 灏天文库 转发
评论区 (0)
U