Roots 与 Elicitation:作用域与执行中的用户输入


文档摘要

Roots 与 Elicitation:作用域与执行中的用户输入 本节摘要:硬编码路径在用户打开另一个项目的那一刻就崩了;预填的工具参数在用户没说全的那一刻就崩了。Roots 把服务端的作用域限定在用户控制的 URI 集合;Elicitation 在工具调用中途暂停,通过表单或 URL 向用户征询结构化输入。两个客户端原语,修掉 MCP 的两类常见失败模式。SEP-1036(URL 模式 elicitation,2025-11-25)在 2026 上半年仍是实验性的——依赖前先查 SDK 版本。 学习目标 阅读完本节,你应当能够: 声明 并响应 。 把服务端的文件操作限制在声明的 root 集合内。 用 在工具调用中途向用户征询确认或结构化输入。

Roots 与 Elicitation:作用域与执行中的用户输入

本节摘要:硬编码路径在用户打开另一个项目的那一刻就崩了;预填的工具参数在用户没说全的那一刻就崩了。Roots 把服务端的作用域限定在用户控制的 URI 集合;Elicitation 在工具调用中途暂停,通过表单或 URL 向用户征询结构化输入。两个客户端原语,修掉 MCP 的两类常见失败模式。SEP-1036(URL 模式 elicitation,2025-11-25)在 2026 上半年仍是实验性的——依赖前先查 SDK 版本。

学习目标

阅读完本节,你应当能够:

  1. 声明 roots 并响应 notifications/roots/list_changed
  2. 把服务端的文件操作限制在声明的 root 集合内。
  3. elicitation/create 在工具调用中途向用户征询确认或结构化输入。
  4. 表单模式URL 模式 elicitation 之间做选择(后者实验性,漂移风险已注)。

一、问题与直觉

一个笔记 MCP 服务端在生产里撞上两类具体失败:

  • 路径假设崩坏:服务端写死 ~/notes。另一台机器笔记在 ~/Documents/Notes 的用户,得到一个静默失败(找不到文件)或更糟(写错地方)的工具调用。
  • 用户知道却缺失的参数:用户说「删掉旧 TPS 报告笔记」。模型调 notes_delete(title:"TPS report"),但有 2023、2024、2025 三条匹配。工具不能猜——报「歧义」很烦,三条都删是灾难。

Roots 修第一个:客户端在 initialize 声明服务端可触碰的 URI 集合。Elicitation 修第二个:服务端暂停工具调用,发 elicitation/create 请用户挑一个。

二、从零实现

Roots

客户端在 initialize 声明 root 列表:

{"capabilities": {"roots": {"listChanged": true}}}

服务端随后可调 roots/list:

{"roots": [{"uri": "file:///Users/alice/Documents/Notes", "name": "Notes"}]}

服务端必须把 roots 当作边界:任何在 root 集合外的文件读写都被拒绝。这不是客户端强制的(服务端仍是用户信任的代码),但合规服务端都会遵守。

用户增删 root 时,客户端发 notifications/roots/list_changed;服务端重调 roots/list 更新边界。

为什么 roots 是客户端原语

Roots 由客户端声明,因为它们代表用户的同意模型。用户告诉 Claude Desktop「给这个笔记服务端访问这两个目录的权限」。服务端无法自行扩大这个范围。

Elicitation:表单模式(默认)

elicitation/create 取一个表单 schema 加自然语言提示:

{ "method": "elicitation/create", "params": { "message": "删除『TPS 报告』?有多条匹配,挑一个。", "requestedSchema": { "type": "object", "properties": { "note_id": {"type": "string", "enum": ["note-3", "note-7", "note-14"]}, "confirm": {"type": "boolean"} }, "required": ["note_id", "confirm"] } } }

客户端渲染表单、收集用户答案、返回:

{"action": "accept", "content": {"note_id": "note-14", "confirm": true}}

三种可能的 action:accept(用户填了)、decline(用户关了)、cancel(用户中止了整个工具调用)。

表单 schema 是扁平的——v1 不支持嵌套对象。SDK 通常会拒绝任何比单层更复杂的东西。

Elicitation:URL 模式(SEP-1036,实验性)

2025-11-25 新增。服务端不发 schema,而是发一个 URL:

{ "method": "elicitation/create", "params": { "message": "登录 GitHub", "url": "https://github.com/login/oauth/authorize?client_id=..." } }

客户端在浏览器开 URL,等完成,用户回来时返回。适合 OAuth 流、支付授权、文档签署等表单不够用的场景。

⚠️ 漂移提示:SEP-1036 的响应形状仍在收敛;有的 SDK 返回回调 URL,有的返回完成 token。生产用 URL 模式前读你的 SDK release notes。

何时该用 elicitation

  • 破坏性动作前的用户确认(destructive hint + elicitation)。
  • 消歧(N 选一)。
  • 首次运行设置(API key、目录、偏好)。
  • OAuth 式流程(URL 模式)。

何时该用 elicitation

  • 填模型本可用散文问的工具必填参数——用普通重提示,不要 elicitation 对话框。
  • 高频调用——elicitation 打断对话,别在循环里发。
  • 服务端事后能校验的任何东西——校验、返错、让模型用文本问用户。

人在回路的桥梁

Elicitation 与 sampling 一起,构成 MCP 的「人在回路」模型。服务端的 Agent 循环可暂停等待用户输入(elicitation)或模型推理(sampling)。第 11 节讲了 sampling,本节讲 elicitation。两者合用,实现完整的中途循环控制。

三、框架对比

维度 Roots Elicitation(表单) Elicitation(URL)
谁发起 客户端声明 服务端 mid-call 服务端 mid-call
解决 作用域/路径 消歧/确认 OAuth/支付/签署
状态(2026) 标准活跃 标准活跃 实验性(SEP-1036)
用户中断 不中断 中断对话 中断,跳浏览器

💡 心法:Roots 是「同意边界」,elicit 是「中途打断问用户」。两者都该克制使用——频繁打断对话的 elicitation 是糟糕 UX。

四、可复用产物

本节产出 outputs/skill-elicitation-form-designer.md——给定一个可能需要用户确认或消歧的工具,它设计 elicitation 表单 schema 与消息模板。

code/main.py 把笔记服务端扩展为:roots/list 响应(root-list-changed 后重查);一个 notes_delete 工具,多条匹配时用 elicitation/create 消歧;一个 notes_setup 工具,用 URL 模式 elicitation 打开首次配置页(模拟);一个边界检查,拒绝声明 roots 外的 URI 操作。demo 跑三个场景:happy path(一条匹配)、消歧(三条匹配,elicitation 触发)、root 外写入(被拒)。

五、练习

  1. 触发消歧:运行 code/main.py,触发消歧路径,确认模拟用户答案被路由回工具。

  2. 加强制确认:加一个 notes_archive 工具,每次都要求 elicitation 确认(destructive hint)。对比 UX:这比模型用文本再问一次如何?

  3. 实现 URL OAuth:为首次运行 OAuth 流实现 URL 模式 elicitation,加 SDK 版本守卫(漂移风险)。

  4. 原子重读:扩展 roots/list 处理——通知到达时,服务端应原子地重读并重扫可能已出作用域的打开文件句柄。

  5. 读 SEP 讨论:读 GitHub 上 SEP-1036 的 issue 讨论线,找出一个影响服务端如何处理 URL 模式回调的开放问题。

本节要点回顾

  1. Roots 是同意边界:客户端在 initialize 声明可触碰 URI,服务端必须把 root 外操作当越界拒绝。
  2. Roots 是客户端原语:代表用户同意模型,服务端无法自行扩范围;变动经 notifications/roots/list_changed
  3. Elicitation 表单模式:requestedSchema + 消息,客户端渲染表单;accept/decline/cancel 三分支。
  4. 表单扁平:v1 不支持嵌套对象。
  5. URL 模式(SEP-1036)实验性:服务端发 URL,客户端开浏览器等回调;适合 OAuth/支付/签署;响应形状仍在收敛。
  6. 该用 elicitation:破坏性确认、消歧、首次设置、OAuth 流。
  7. 不该用 elicitation:模型能用散文问的必填参数、高频调用、事后能校验的。
  8. 人在回路双桥:elicitation(用户输入)+ sampling(模型推理)= 完整中途循环控制。

下一节,我们看 MCP 的异步任务——让长跑工作即使跨越会话重连也能存活,以及 SEP-1686 带来的 task 生命周期。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U