绿然智老照片修复器
58.03M · 2026-02-12
在第三篇文章中,我们讲了MCP服务端的三个核心原语。在上一篇我其实忘记解释原语到底是什么,这里补充一下吧。
原语指的是最基本的、不可再分的操作单元,是构建更复杂功能的基础构建块(building blocks)。我们讲的服务端的三个核心原语,其实就是MCP服务器能提供的三种基本交互模式。那么类比到其他方面,比如HTTP 的原语:GET、POST、PUT、DELETE(基本操作),数据库的基本原语是CRUD等等。
那么这一篇我们就开始讲客户端的核心原语
如果你对前面的内容感兴趣,可以点击这里跳转
MCP (Model Context Protocol) 技术理解 - 第一篇
MCP (Model Context Protocol) 技术理解 - 第二篇
MCP (Model Context Protocol) 技术理解 - 第三篇
其实我们在前面的架构讲解的时候有讲过MCP的客户端是什么,但是这里就顺带再讲一遍吧。
MCP 客户端由AI应用程序实例化,用于与特定的 MCP 服务器通信。AI应用(例如 Cursor或者Claude Code)负责管理整体用户体验并协调多个客户端。每个客户端处理与一个服务器的直接通信。理清这里非常重要,不要把AI应用(host宿主)和MCP客户端混为一谈噢
根本区别就是host是用户与之交互的应用程序,而客户端是实现服务器连接的协议级组件
上面讲了概念,我们现在再来讲讲客户端的核心原语有什么,一般有下面的三种:
| 特征 | 解释 | 例子 |
|---|---|---|
| Elicitation | 信息获取使服务器能够在交互过程中向用户请求特定信息,为服务器按需收集信息提供了一种结构化的方法。 | 服务器在预订旅行时可能会询问用户对飞机座位、房间类型或联系电话的偏好,以完成预订。 |
| Roots | 根目录允许客户端指定服务器应该关注哪些目录,并通过协调机制传达预期范围。 | 旅行预订服务器可以被授予对特定目录的访问权限,从而可以从中读取用户的日历。 |
| Sampling | 采样允许服务器通过客户端请求 LLM 完成,从而实现代理工作流。这种方法使客户端完全掌控用户权限和安全措施。 | 旅行预订服务器可以向 LLM 发送航班列表,并请求 LLM 为用户选择最佳航班。 |
Elicitation,翻译过来意思是引导。它的作用和它的翻译意思一样,为服务器提供了一种结构化的方法,使其能够按需收集必要信息。服务器无需预先获取所有信息,也无需在数据缺失时失败,而是可以暂停操作,向用户请求特定输入。这创造了更灵活的交互方式,服务器能够适应用户需求,而不是遵循僵化的模式
这是它的时序图
该流程支持动态信息收集。服务器可以根据需要请求特定数据,用户通过相应的用户界面提供信息,服务器则根据新获取的上下文继续处理数据。
举个例子来说明白,我们使用claude code的时候,如果你的prompts不够清晰明了,CC会问你是否要xx,或者问xx这样做您满意吗?逐渐引导到你想要的结果。
又说到最近很火的千问送奶茶活动,你说的prompt是“我要喝xx的xx款奶茶”,但是它不知道你要大杯还是中杯,几分糖,所以会返回不同规格的同款奶茶给你(同家奶茶店,但是不同地点,大杯或中杯),等你选定再返回给千问下单。
来个数据流示意?
{
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"]
}
}
}
Roots,直译过来是根的意思,也就是根目录。根目录是客户端向服务器传达文件系统访问边界的一种机制。它们由文件 URI 组成,指示服务器可以操作的目录,帮助服务器了解可用文件和文件夹的范围。虽然根目录传达了预期的边界,但它们并不强制执行安全限制。实际的安全措施必须在操作系统层面通过文件权限和/或沙箱机制来实施
那么Roots可以规定MCP服务端可以访问的文件,要求不能越过越过这条边界(但是不能强制要求,因为服务器运行的代码是客户端无法控制的)。
下面是Roots的结构:
{
"uri": "file:///Users/agent/travel-planning",
"name": "Travel Planning Workspace"
}
根目录是文件系统路径,始终使用file://URI 方案。它们帮助服务器理解项目边界、工作区组织结构和可访问目录。根目录列表会随着用户使用不同的项目或文件夹而动态更新,服务器会在roots/list_changed边界发生变化时收到通知。
不理解?我们来一个示例:
旅行社agent需要处理多个客户的行程,因此可以利用Roots来组织文件系统访问。可以考虑创建一个工作区,其中包含用于旅行计划各个方面的不同目录。客户端向旅行规划服务器提供文件系统根目录:
- 包含所有旅行文件的主工作区- 可重复使用的行程模板和资源- 客户护照和旅行证件当agent创建巴塞罗那行程时,运行良好的服务器会遵守这些边界——访问模板、保存新行程以及引用指定根目录下的客户文档。服务器通常通过使用根目录的相对路径或利用遵循根目录边界的文件搜索工具来访问根目录中的文件。如果代理打开一个类似这样的归档文件夹,客户端会通过更新根列表roots/list_changed
Sampling,直译过来是采样。采样允许服务器通过客户端请求语言模型补全,从而实现代理行为,同时保持安全性和用户控制。服务器可以请求已拥有 AI 模型访问权限的客户端代表其处理这些任务。这种方法使客户端完全掌控用户权限和安全措施。由于采样请求发生在其他操作(例如数据分析工具)的上下文中,并作为独立的模型调用进行处理,因此它们能够清晰地划分不同上下文,从而更有效地利用上下文窗口。
不理解?来个例子,平时使用Cursor的时候,如果要执行某个文件命令又或者执行maven命令,如果你没有设置Agent全自动运行,那么Agent在运行前的时候肯定会先来问你,是否执行这个命令或者skip(跳过)。
下面是采样的时序图,该流程通过多重人为干预检查点确保安全性。用户可以在请求返回服务器之前,查看并修改初始请求和生成的响应。
下面是请求参数示例:
{
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" // Suggested model
}],
costPriority: 0.3, // Less concerned about API cost
speedPriority: 0.2, // Can wait for thorough analysis
intelligencePriority: 0.9 // Need complex trade-off evaluation
},
systemPrompt: "You are a travel expert helping users find the best flights based on their preferences",
maxTokens: 1500
}
Sampling本质就是想实现人机协同。采样请求可能需要用户明确同意。客户端可以说明服务器想要分析的内容及其原因。用户可以批准、拒绝或修改请求。
这篇我们运用了很多例子来讲解MCP客户端三种原语,相信大家都有不错的体感。下一篇,我们会讲解MCP实战开发。
如果你觉得MCP技术讲解系列非常不错,可以点攒+收藏+关注,谢谢。