MCP 入门(四):信息征询、根目录、采样——客户端的三板斧

上一篇我们聊了 MCP 服务器能做什么,这一篇换个视角——聊聊客户端。

MCP 客户端由宿主应用(Host)实例化,专门负责与某个 MCP 服务器通信。宿主应用(比如 Claude.ai 或各种 IDE)管理用户体验的全局,协调多个客户端;而每个客户端只与一个服务器一对一通信。

这里有一个关键区分:宿主是用户直接交互的应用程序,客户端是协议层的连接组件。 宿主面向人,客户端面向协议。


客户端给服务器带来了什么?

除了消费服务器提供的上下文信息,客户端自身也能向服务器输出能力。正是这些能力,让服务器开发者得以构建更丰富的交互体验。

核心能力一共三个:

功能 一句话解释 举个例子
信息征询(Elicitation) 服务器在交互过程中主动向用户要信息,按需收集,结构化传递 旅行预订服务器询问用户的座位偏好、房型选择或联系方式,以完成预订
根目录(Roots) 客户端告诉服务器"你只需要关注这些目录",划定工作范围 旅行预订服务器被授权访问特定目录,从中读取用户的日历信息
采样(Sampling) 服务器通过客户端请求 LLM 补全,实现智能体工作流,同时客户端保有完全的权限管控 旅行预订服务器把航班列表发给 LLM,让模型帮用户挑出最佳航班

说到"采样"这个名字,很多人第一反应是:为什么不直接叫"模型调用"?

这个命名其实大有深意。 LLM 生成文本的本质是基于概率分布的随机采样(Sampling)。而在 MCP 的语境下,“采样"还暗含了一层更重要的含义——服务器自己并不拥有模型,它只是向客户端"申请采集一次生成结果”。一个词,同时点明了技术原理和权限归属:模型在客户端手里,服务器只是借用。 这正是 Human-in-the-loop 设计的精髓。

理解了这三个能力,我们逐一展开。


信息征询(Elicitation):让服务器学会"开口问"

信息征询让服务器在交互过程中主动向用户索要特定信息,从而构建更动态、更灵活的工作流。

核心思路

传统做法要么预先收集所有信息,要么在信息缺失时直接报错。信息征询提供了第三种方式:服务器可以随时暂停,向用户发起提问,拿到答案后继续执行。 不再是死板的流程,而是按需适配。

信息征询的工作流程

整个流程很直观:服务器在需要时发起请求,客户端把请求转化为用户界面,用户填写信息,客户端再把结果返回给服务器。服务器拿到新的上下文,继续处理任务。

来看一个具体的请求长什么样
{
  method: "elicitation/requestInput",
  params: {
    message: "Please confirm your Barcelona vacation booking details:",
    schema: {
      type: "object",
      properties: {
        confirmBooking: {
          type: "boolean",
          description: "Confirm the booking (Flights + Hotel = $3,000)"
        },
        seatPreference: {
          type: "string",
          enum: ["window", "aisle", "no preference"],
          description: "Preferred seat type for flights"
        },
        roomType: {
          type: "string",
          enum: ["sea view", "city view", "garden view"],
          description: "Preferred room type at hotel"
        },
        travelInsurance: {
          type: "boolean",
          default: false,
          description: "Add travel insurance ($150)"
        }
      },
      required: ["confirmBooking"]
    }
  }
}
实战场景:假期预订确认

用一个旅游预订服务器来感受信息征询的威力。

用户选好了去 Barcelona 的度假套餐,接下来服务器要做两件事:拿到用户的最终确认,同时补齐遗漏的细节。

服务器发起一个结构化请求,包含行程摘要(Barcelona 往返机票、6月15-22日、海滨酒店、总计 $3,000),以及座位偏好、房型选择、旅行保险等附加字段。

随着预订流程推进,服务器还会继续征询:乘机人信息、酒店特殊需求、紧急联系人…每一步都是按需获取,而非一股脑全问。

用户交互设计

信息征询的交互设计遵循三个原则:清晰、贴合场景、尊重用户自主权。

请求呈现:客户端在展示请求时,会明确告知是哪个服务器在问、为什么需要这个信息、信息将如何被使用。请求消息(message)负责解释目的,模式(Schema)负责约束数据结构和校验规则,各司其职。

用户的选择权:用户可以通过文本框、下拉菜单、复选框等控件填写信息;也可以拒绝提供,附上理由;甚至可以直接取消整个操作。客户端会在返回前根据 Schema 校验响应的合法性。

隐私红线:信息征询绝不会请求密码或 API 密钥。遇到可疑请求,客户端会主动警告;用户在发送前可以审阅所有即将提交的数据。


根目录(Roots):给服务器画一个"工作围栏"

根目录为服务器的文件系统操作划定边界,让客户端明确告知服务器:“你的活动范围就在这里。”

核心思路

根目录本质上是客户端向服务器传达文件系统访问边界的机制。它由一组 file:// URI 构成,告诉服务器哪些目录是可以操作的。

但有一点必须强调:根目录传达的是"建议边界",而非"安全围墙"。 真正的安全防护,要靠操作系统层面的文件权限和沙箱机制来保障。

根目录的结构
{
  "uri": "file:///Users/agent/travel-planning",
  "name": "Travel Planning Workspace"
}

根目录只接受 file:// URI 格式,帮助服务器理解项目边界、工作区结构和可访问的目录。当用户切换项目或打开新的文件夹时,根目录列表会动态更新,服务器通过 roots/list_changed 通知实时感知边界变化。

实战场景:旅行规划工作空间

一位旅游智能体同时处理多个客户的行程安排,涉及大量文件管理。通过根目录,不同用途的文件被清晰地组织起来。

客户端为旅行规划服务器提供了三个根目录:

  • file:///Users/agent/travel-planning —— 主工作空间,存放所有旅行文件
  • file:///Users/agent/travel-templates —— 可复用的行程模板和资源
  • file:///Users/agent/client-documents —— 客户护照和旅行证件

当智能体创建 Barcelona 行程时,规范运行的服务器会严格遵守边界——仅在指定根目录内访问模板、保存新行程、引用客户文档。服务器通常通过根目录的相对路径,或使用遵循边界约束的文件检索工具来访问文件。

如果智能体打开了一个归档文件夹(如 file:///Users/agent/archive/2023-trips),客户端会通过 roots/list_changed 更新根目录列表,服务器随即感知到新的工作范围。

官方仓库中有完整实现并遵循根目录规范的服务器示例,推荐参考。

设计哲学:协调而非管控

根目录是客户端和服务器之间的协调机制,而非安全边界。

协议规范里写的是服务器"应该(SHOULD)"遵守边界,而非"必须(MUST)"强制执行。原因很现实:服务器运行的代码不受客户端控制。

所以,根目录在以下场景效果最好:

  • 服务器是受信任或经过审查的
  • 用户理解其建议性质,而非强制约束
  • 目标是防止误操作,而非抵御恶意行为

它擅长三件事:划定上下文范围(引导服务器聚焦重点目录)、预防意外越界(让规范的服务器保持在边界内)、组织工作流(自动管理项目边界)。

用户交互设计

根目录通常由宿主应用根据用户操作自动管理,部分应用也提供手动配置:

自动检测:用户打开文件夹时,客户端自动将其暴露为根目录。比如打开旅行工作区,客户端就会将该目录设为根目录,让服务器知道当前工作涉及哪些行程和文档。

手动配置:高级用户可以通过配置文件指定根目录。比如添加 /travel-templates 用于复用资源,同时排除包含财务记录的目录。


采样(Sampling):让服务器"借用"客户端的大脑

采样让服务器能够通过客户端调用 LLM,在保持安全性和用户掌控权的前提下,实现智能体行为。

核心思路

采样解决了一个很实际的问题:服务器需要 AI 能力,但不想(或无法)自己接入和付费使用 LLM。

解法很优雅——服务器向已经接入 LLM 的客户端发起请求,由客户端代为处理。这样一来,权限和安全的控制权始终握在客户端手中。

而且,采样请求作为独立的模型调用来处理,与其他操作上下文保持清晰隔离,实现了对上下文窗口更高效的利用。

采样的工作流程

这个流程的安全性体现在多重人工审核节点上。在结果返回给服务器之前,用户有机会审查并修改初始请求和生成的响应——两道关卡,双重保障。

请求参数示例
{
  messages: [
    {
      role: "user",
      content: "Analyze these flight options and recommend the best choice:\n" +
               "[47 flights with prices, times, airlines, and layovers]\n" +
               "User preferences: morning departure, max 1 layover"
    }
  ],
  modelPreferences: {
    hints: [{
      name: "claude-sonnet-4-20250514"  // 建议使用的模型
    }],
    costPriority: 0.3,      // 对 API 成本不太敏感
    speedPriority: 0.2,     // 可以等待深入分析
    intelligencePriority: 0.9  // 需要高质量的权衡评估
  },
  systemPrompt: "You are a travel expert helping users find the best flights based on their preferences",
  maxTokens: 1500
}

注意 modelPreferences 字段——服务器可以表达对模型的偏好(而非指定),最终选择权仍在客户端。这个设计处处体现着"建议而非控制"的协议哲学。

实战场景:航班智能分析

假设有一个旅行预订服务器,内置了 findBestFlight 工具。当用户说"帮我预订下个月去 Barcelona 的最佳航班",这个工具需要 AI 来做复杂的权衡评估。

工作流是这样的:

  1. 工具查询航空公司 API,搜集到 47 个航班选项
  2. 通过采样请求将数据发给 LLM,附上用户偏好(早上出发、最多一次中转)
  3. LLM 评估各种权衡——便宜的红眼航班 vs. 舒适的早班机、直飞 vs. 一次中转
  4. 工具根据分析结果向用户呈现前三个推荐方案

整个过程中,用户可以审查发送给 LLM 的内容和 LLM 的回复,全程透明可控。

用户交互设计

虽然不是强制要求,但采样的设计充分支持人机协同控制。用户可以通过以下方式保持监督:

审批控制:采样请求可以要求用户明确同意。客户端展示服务器想分析什么、为什么要分析。用户可以批准、拒绝或修改请求。

透明度:客户端可以展示完整的提示词、模型选择和 token 限制。用户在 AI 响应返回服务器前可以进行审核。

灵活配置:用户可以设置模型偏好,为可信操作开启自动批准,也可以选择对所有操作都要求人工审批。客户端还可以提供敏感信息脱敏选项。

安全底线:客户端和服务器都必须妥善处理敏感数据。客户端应实施速率限制并校验所有消息内容。Human-in-the-loop 的设计确保了一条铁律——未经用户明确同意,服务器发起的 AI 交互绝不会危及安全或触碰敏感数据。


写在最后

MCP 客户端的三个核心能力——信息征询、根目录、采样——看似各自独立,实则共享同一个设计理念:客户端是用户的代理人,一切以用户的知情权和控制权为先。

信息征询让服务器"会问问题",根目录给服务器"划定边界",采样让服务器"借用大脑"。三者协同,构成了 MCP 协议中客户端侧的完整能力图谱。

理解了这些,你就能更好地判断:什么时候该用哪个能力,以及如何在自己的应用中正确集成 MCP 客户端。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐