在讨论大型语言模型和 AI 应用时,大家关注的往往是模型自身的参数和表现。但真要搭建一个功能丰富的 AI 应用,模型与外部工具、服务之间的高效协作,才是决定体验的隐形支柱。模型上下文协议(MCP)正是为此设计的标准,而 MCP 客户端,则是实现这一协作的关键角色。
什么是 MCP 客户端?
简单来说,一个 MCP 客户端是由宿主应用程序(host application)实例化的,专门用于与某个特定的 MCP 服务器进行通信的组件。这里的宿主应用程序,就是我们日常使用的 Claude.ai 或某种集成开发环境(IDE),它负责管理整体的用户体验,甚至可能同时协调多个不同的客户端。
每个客户端只处理与一个服务器之间的直接通信。理解宿主和客户端的区别非常重要:宿主是我们直接交互的应用程序,而客户端则是在协议层面,实现与服务器连接的那个“幕后组件”。
举个例子,在一个 IDE(宿主)中使用一个 AI 代码分析工具时,IDE 可能会启动一个 MCP 客户端,专门和这个工具的服务器对话。同时,如果还用了另一个 AI 辅助文档写作的工具,IDE 就会为它启动另一个独立的 MCP 客户端。IDE 作为宿主统一管理这些工具的界面和交互流程,而各个客户端则在后台默默处理着与各自服务器的数据交换。
客户端的核心特性
MCP 客户端不仅仅是单方面索取,它自己也能向服务器提供能力。这些客户端特性,能让服务器的开发者构建出交互性更强的应用。
| 特性 | 说明 | 示例 |
|---|---|---|
| Elicitation | 允许服务器在交互过程中向用户请求特定信息,提供了一种结构化的按需信息收集方式。 | 一个旅行预订服务器可能会询问用户的座位偏好、房型或联系电话,以完成预订。 |
| Roots | 允许客户端指定服务器应当关注的文件目录,通过协调机制来传达预期的工作范围。 | 一个旅行预订服务器可以被授予访问特定目录的权限,以便读取用户的日程安排。 |
| Sampling | 允许服务器通过客户端请求 LLM 进行文本生成,从而实现类似 Agent 的工作流。这种方式将用户权限和安全措施的控制权完全交给了客户端。 | 一个旅行预订服务器可以将航班列表发送给 LLM,并请求 LLM 为用户挑选最佳航班。 |
下面,我们逐个深入看看。
Elicitation:让服务器主动提问
Elicitation 机制,简单来说就是让服务器在交互过程中主动向用户“问问题”,从而创造出更动态、更灵活的工作流程。
它提供了一种结构化的按需信息收集方式。当服务器缺少必要数据时,它不必直接失败,或者要求用户一次性把所有信息都填好。它可以选择暂停当前操作,向用户发起一个具体的信息请求。这样交互就变得灵活多了,服务器能适应用户的需求,而不是走一个死板的流程。
流程大致是这样:服务器需要信息时,向客户端发起一个 elicitation/create 请求。客户端收到后,用合适的用户界面将问题呈现给用户。用户填写信息并提交,客户端再把响应返回给服务器。服务器拿到新信息后,继续执行后续操作。
一个 Elicitation 请求,通常包含一段说明和一个 schema,用来定义所需信息的结构。比如,一个旅行预订服务器在最后确认阶段,可能会发起类似这样的请求:
{"method": "elicitation/requestInput","params": {"message": "请确认您的巴塞罗那假期预订详情:","schema": {"type": "object","properties": {"confirmBooking": {"type": "boolean","description": "确认预订 (机票 + 酒店 = $3,000)"},"seatPreference": {"type": "string","enum": ["window", "aisle", "no preference"],"description": "航班座位偏好"},"roomType": {"type": "string","enum": ["sea view", "city view", "garden view"],"description": "酒店房间类型偏好"},"tra velInsurance": {"type": "boolean","default": false,"description": "添加旅行保险 ($150)"}},"required": ["confirmBooking"]}}}
这里,message 清楚地告诉用户请求的目的;schema 则详细定义了需要用户提供或确认的信息。客户端会根据这个 schema 生成对应的 UI 控件,比如复选框、下拉菜单,方便用户操作。同时,Elicitation 的设计也充分考虑了用户体验和隐私,客户端会清楚地显示是哪个服务器在请求信息、为什么需要这些信息,并允许用户选择提供、拒绝或取消。像密码或 API 密钥这类敏感信息,是不会通过这种方式请求的。
Roots:划定工作范围
Roots 的作用是给服务器划定一个“活动范围”,让客户端能明确告诉服务器应该把注意力集中在哪些文件目录上。
当一个服务器需要和本地文件系统交互时,它怎么知道哪些文件是相关的呢?Roots 就是为此设计的协调机制。它由一组文件 URI 组成,这些 URI 指向服务器能操作的目录。需要强调的是,Roots 传达的是“预期的工作范围”,它本身不强制执行安全限制。真正的文件系统安全,必须在操作系统层面通过文件权限或沙箱技术来保障。
一个 Root 的结构很简单,通常包含一个 URI 和一个名称:
{"uri": "file:///Users/agent/tra vel-planning","name": "Tra vel Planning Workspace"}
这里的 uri 总是用 file:// 协议,指向一个本地文件系统路径。name 是一个便于理解的别名。
以一位需要处理多个客户旅行计划的袋里人为例。其工作目录可能包含不同用途的文件夹。客户端可以向旅行规划服务器提供这样的 Roots:
file:///Users/agent/tra vel-planning- 主要工作区file:///Users/agent/tra vel-templates- 可复用的行程模板file:///Users/agent/client-documents- 客户文件
当袋里人为客户创建新行程时,一个行为良好的服务器就会尊重这些 Roots 边界:在指定目录内查找模板、保存新文件、引用客户文档,不会随意访问文件系统中的其他位置。
另外,Roots 列表是动态的。如果用户在 IDE 中打开了一个新项目文件夹,比如 file:///Users/agent/archive/2023-trips,客户端会自动更新 Roots 列表,并通过 roots/list_changed 通知服务器工作范围发生了变化。
这种设计哲学,将 Roots 定位为客户端与服务器之间的“君子协定”,而非强制性的安全沙箱。协议规范要求服务器“应该”尊重 Roots 边界,而不是“必须”强制执行。因为客户端无法完全控制服务器上运行的代码。所以,Roots 在与受信任的服务器协作、防止误操作、以及组织工作流程方面,表现出色。
Sampling:在客户端的控制下调用 LLM
Sampling 让服务器能通过客户端请求语言模型生成内容。这为实现更智能的 Agent 行为提供了可能,同时又将安全和控制权牢牢掌握在客户端手中。
想象一个场景:一个服务器本身不具备调用 LLM 的能力,也没有相关的 API 密钥和计费账户,但在执行任务时,它需要 LLM 的分析能力。Sampling 就是来解决这个问题的。
服务器可以向客户端发起一个请求,让客户端代为调用 LLM。这种方式把客户端置于控制地位——因为它已经配置好了对各种 AI 模型的访问权限,可以全权负责处理权限、安全和成本控制。
Sampling 流程中设计了“人在环路”(human-in-the-loop)的关键节点。服务器发起请求后,客户端可以先把请求内容展示给用户审批。用户能审核甚至修改将要发送给 LLM 的提示词。LLM 返回结果后,客户端同样可以先让用户审核,再决定是否把结果返回给服务器。
一个 Sampling 请求的参数非常丰富,允许服务器精细地表达意图:
{"messages": [{"role": "user","content": "分析这些航班选项并推荐最佳选择:\n" + "[包含价格、时间、航司、中转信息的47个航班]\n" + "用户偏好:早班机,最多1次中转"}],"modelPreferences": {"hints": [{"name": "claude-sonnet-4-20250514"}],"costPriority": 0.3,"speedPriority": 0.2,"intelligencePriority": 0.9},"systemPrompt": "你是一位旅行专家,帮助用户根据偏好寻找最佳航班","maxTokens": 1500}
在这个例子里,服务器不仅提供了完整的对话历史,还通过 modelPreferences 表达了对模型的偏好:它建议使用某个特定模型,并且更看重智能,对成本和速度不太在意。
回到旅行预订的场景,一个名为 findBestFlight 的工具在收集了 47 个航班选项后,就可以利用 Sampling,把这些数据连同用户偏好一起打包,请求客户端的 LLM 进行分析。LLM 会权衡价格、时间和中转次数等复杂因素,给出一个综合性的建议。工具拿到结果后,再向用户展示最终推荐。
通过把 LLM 调用能力集中在客户端,服务器开发者可以专注于自身的核心业务逻辑,不必为集成和管理 LLM 费心。而用户这边,所有由服务器发起的 AI 调用都在掌控之中,透明度和安全性都有了保障。
