9.4 客户端能力声明、第一类 Client 与 ClientSession


文档摘要

9.4 客户端能力声明、第一类 Client 与 ClientSession 本节摘要:第 9 章收尾节,讲三个收尾性主题。一是客户端能力声明——客户端也声明能力,告诉服务端「我支持采样、根、引导填写」等反向能力,这套双向声明影响服务端行为。二是第一类 与低层 的关系—— 是 之上的便捷封装,日常用它够,需要细粒度控制才下沉。读完本节,第 9 章的客户端入门完整收口,为第 10 章进阶做好准备。 一、客户端能力声明:双向的 第 3.3 节讲过服务端的能力声明——服务端告诉客户端「我支持 tools/resources/prompts」。

9.4 客户端能力声明、第一类 Client 与 ClientSession

本节摘要:第 9 章收尾节,讲三个收尾性主题。一是客户端能力声明——客户端也声明能力,告诉服务端「我支持采样、根、引导填写」等反向能力,这套双向声明影响服务端行为。二是第一类 Client 与低层 ClientSession 的关系——ClientClientSession 之上的便捷封装,日常用它够,需要细粒度控制才下沉。读完本节,第 9 章的客户端入门完整收口,为第 10 章进阶做好准备。

一、客户端能力声明:双向的

第 3.3 节讲过服务端的能力声明——服务端告诉客户端「我支持 tools/resources/prompts」。客户端也声明能力,告诉服务端「我支持什么反向能力」:

服务端声明(正向能力): "我支持 tools、resources、prompts" → 客户端据此知道该问什么 客户端声明(反向能力): "我支持 sampling、roots、elicitation" → 服务端据此知道可以反向请求什么

「正向」指客户端→服务端的请求(tools/call 等);「反向」指服务端→客户端的请求(第 7 章的采样、根、引导填写)。客户端声明反向能力,让服务端知道「这些反向操作可以做」。

二、客户端能力的内容

客户端能声明的反向能力(回顾第 7 章):

能力 服务端据此能做什么 现状
sampling 反向请客户端做 LLM 补全 弃用趋势(第 7.4 节)
roots 反向问客户端工作区文件夹 弃用趋势
elicitation 反向问客户端用户输入 主流(第 7.1-7.3 节)

关键规则:如果客户端没声明某能力,服务端就不该尝试用。例如客户端没声明 elicitation,服务端的引导填写就不会工作(因为没有反向通道)。

# 客户端构造时声明能力(概念性) async with Client( url, capabilities={ "elicitation": {}, # 声明支持引导填写 # "sampling": {}, # 不声明,服务端不会请求采样 } ) as client: ...

三、能力声明如何影响服务端行为

这套双向声明影响服务端的实际行为。考虑一个例子:

场景:服务端有个 transfer 工具,需要引导填写确认 客户端 A(声明支持 elicitation): 服务端知道:可以反向问客户端 → transfer 执行中,触发引导填写 → 用户确认,转账完成 客户端 B(不声明 elicitation): 服务端知道:不能反向问 → transfer 执行中,引导填写无反方式 → 要么直接失败,要么用其他方式确认

服务端会根据客户端声明调整行为——这是双向声明的实际价值。它让服务端不必假设「客户端一定支持某反向能力」,而是动态适应。

⚠️ 注意:声明能力等于承诺实现。如果你声明支持 elicitation,就要在客户端实现「收到引导填写请求时,把问题转给用户、拿答案回传」的逻辑。声明了不实现,服务端的反向请求会挂起或失败。第 7 章的反向能力,客户端侧的实现由宿主(或你用 SDK 自研 Host)负责。

四、第一类 Client 与低层 ClientSession

第 9.1-9.3 节用的都是第一类 Client。它是 v2 新增的高层封装,内部基于低层的 ClientSession:

高层(日常用) ┌─────────────────────────────────────────┐ │ Client │ │ - 按参数类型自动选传输 │ │ - async with 管理连接生命周期 │ │ - call_tool 等便捷方法 │ │ - 内部维持一个 ClientSession │ └─────────────────────────────────────────┘ 构建于 ┌─────────────────────────────────────────┐ │ ClientSession(低层) │ │ - 直接操作传输的读写流 │ │ - 需手动管理连接 │ │ - 更细粒度的协议控制 │ └─────────────────────────────────────────┘

类比服务端的两层模型(第 2.3 节):Client 之于 ClientSession,就像 MCPServer 之于低层 Server——高层做便捷,低层做兜底。

五、何时下沉到 ClientSession

绝大多数场景,第一类 Client 够用。只有这些情况才下沉到 ClientSession:

场景 为什么下沉
复用一个传输跑多个会话 Client 一对一,ClientSession 可复用
自定义握手逻辑 Client 自动协商,ClientSession 可控
直接操作协议对象 调试、特殊协议操作
极致性能控制 减少高层封装的开销
# ClientSession 的概念性用法(下沉) from mcp import ClientSession async with stdio_client(params) as (read, write): # 手动创建会话 async with ClientSession(read, write) as session: await session.initialize() # 手动握手 result = await session.call_tool("add", {...})

下沉的代价是要手动管理——手动 initialize、手动处理传输、手动清理。除非必要,别下沉。

💡 技巧:默认用第一类 Client,把它当成「客户端的 FastAPI」——绝大多数需求它都能满足,代码也更简洁。只有当你遇到 Client 的能力边界(如要复用传输、要自定义握手),才考虑下沉到 ClientSession。这个判断与第 2.3 节「日常用 MCPServer,特殊才下沉到低层 Server」完全一致。

六、Client 与 ClientSession 的选择决策

用一个决策树帮你选:

你要写客户端? │ ├─ 是 → 默认用第一类 Client │ │ │ ├─ 需要复用传输跑多会话? → 下沉 ClientSession │ ├─ 需要自定义握手? → 下沉 ClientSession │ └─ 都不需要 → 用 Client(推荐) │ └─ 你要写 Host(编排多服务) → 用 Client + 第 10 章的会话组

绝大多数读者用 Client 就够。第 10 章会讲会话组(ClientSessionGroup),它是基于 Client/ClientSession 的更高级抽象,用于编排多个服务端。

七、本节的心智收获

读完本节,你应该带走这两点:

  1. 能力声明是双向的——服务端声明正向能力,客户端声明反向能力,双方据此调整行为。声明能力等于承诺实现。
  2. ClientClientSession 的便捷封装——日常用 Client(简单),需要细粒度控制才下沉到 ClientSession(灵活但繁琐)。

这两点与第 2 章的架构思想一脉相承:第 2.3 节的「两层服务端模型」对应这里的「两层客户端模型」,第 3.3 节的「能力声明」在这里扩展成「双向」。理解了这些,你就理解了客户端设计的整体逻辑。

本节要点回顾

  1. 客户端也声明能力(反向能力):sampling、roots、elicitation,告诉服务端可以反向请求什么。
  2. 双向声明影响行为:客户端没声明的能力,服务端不会尝试用。
  3. 声明能力等于承诺实现——声明了就要在客户端实现反向逻辑。
  4. 第一类 ClientClientSession 的便捷封装,类比 MCPServer 之于低层 Server。
  5. Client 处理:自动选传输、连接生命周期、便捷方法;ClientSession 是底层会话。
  6. 下沉到 ClientSession 的场景:复用传输、自定义握手、直接操作协议、极致控制。
  7. 默认用 Client,除非遇到它的能力边界才下沉——与「日常用 MCPServer」同理。

第 9 章结束。你已经能连上 MCP 服务、消费它的全部能力。第 10 章讲客户端进阶——传输接入细节、会话组、缓存、订阅。


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