登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

MCP Roots 已弃用怎么办:roots/list 兼容核验与工作区边界迁移

来源:17golang原创

时间:2026-08-18 13:57:44 293浏览 收藏

旧版 MCP 文件工具常把“允许访问哪些目录”交给 Roots:客户端声明工作区,服务端通过 roots/list 获取目录,再决定能否读取文件。这个机制现在进入了迁移期。2026-07-28 规范已经把 Roots 标为弃用,但旧客户端不会立刻消失,生产系统仍需要把兼容核验和路径越界拦截做好。

就算碰到 Roots 已经标记弃用的提示,也不用急着全量替换现有逻辑,先把兼容校验层搭好,再逐步把工作区范围迁移到显式授权的新路径上,就能避免文件读写越界、切换工作区读错内容的问题。
要点速览
  • 初始化阶段先看客户端是否声明 capabilities.roots,再决定是否请求 roots/list
  • Root URI 只是工作区提示,不等于操作系统权限;每次读写仍要做规范化路径和边界校验。
  • listChanged 为 true 时,收到 notifications/roots/list_changed 后要重新拉取并替换缓存。
  • 新项目优先把目录、资源 URI 或授权范围放进工具参数、资源 URI 或服务端配置。

先用一次基线请求看清 Roots 到底提供了什么

一个文件分析工具收到“扫描当前项目”的请求时,不能直接把服务器所在机器的工作目录当成项目目录。旧协议的正确顺序是:客户端在初始化能力中声明 Roots,服务端在处理具体请求时发送 roots/list,客户端返回一组 file:// URI 和可选名称。

MCP roots/list 从客户端能力声明到工作区 URI 校验和文件读取的生命周期图

{
  "method": "roots/list",
  "params": {}
}

{
  "result": {
    "roots": [
      {"uri": "file:///workspace/payments", "name": "payments"}
    ]
  }
}

这里能得到的是“客户端允许服务端关注的根目录列表”,不是一张可以绕过文件系统权限的授权票据。服务端仍应把 URI 解析成绝对路径,拒绝不支持的 scheme,并对后续目标路径逐次验收。

能力协商和根目录缓存要分成两个检查点

服务端不要假定每个客户端都支持 Roots。初始化结果里没有 capabilities.roots 时,应返回明确的“不支持工作区选择”错误,或改走工具参数;不要盲发 roots/list 等待超时。

检查项通过条件失败处理
能力声明存在 roots,并确认 listChanged回退到工具参数或服务端配置
URI 格式使用 file://,解码后是绝对路径拒绝未知 scheme、相对路径和空 URI
缓存更新通知后重新请求并原子替换根列表暂停受影响的文件操作

listChanged 只说明客户端会在列表变更时通知服务端。它不代表服务端可以继续使用旧缓存,更不代表某个根目录永久有效。把根列表和连接、用户身份、请求关联起来,才能避免工作区切换后读错目录。

路径边界必须在每次文件操作前重新判断

最常见的漏洞不是 roots/list 返回了错误目录,而是服务端把用户传入的 ../secrets.env 直接拼在根目录后面。稳妥做法是先 URL 解码,再规范化路径,最后判断目标是否位于某个已验证根目录之下;判断时要处理符号链接和大小写敏感差异。

root := cleanAndResolve(rootURI)
target := cleanAndResolve(join(root, userPath))

if !isWithin(target, root) {
    return ErrOutsideWorkspace
}
return readFile(target)

这段伪代码表达的是校验顺序,不是把 Roots 当成沙箱。真正的隔离还需要操作系统权限、容器挂载、符号链接策略和审计日志共同完成。对写入、删除、批量扫描等高影响动作,建议额外要求工具参数里的明确范围和用户确认。

收到 roots/list_changed 后怎么避免读到旧工作区

用户在客户端切换项目时,客户端会发送 notifications/roots/list_changed。服务端收到后先把旧列表标记为过期,再请求新的 roots/list。在新列表返回前,不要让后台扫描任务继续扩展旧目录;已经打开的文件句柄也应绑定旧请求的生命周期。

MCP Roots 旧客户端兼容与新项目迁移的路径边界对比图

  1. 记录通知到达时的连接、用户和当前工作区版本号。
  2. 暂停依赖根目录缓存的新文件任务,避免通知和读取并发造成竞态。
  3. 重新拉取列表并生成新版本,完成路径校验后再恢复任务。
  4. 把旧版本任务收口为取消或完成,不要把新根目录套到旧任务上。

2026-07-28 之后,新项目应该把边界放在哪里

Roots 被弃用,不等于旧接口当天失效,而是新设计不应继续把它当成主要扩展点。根据 MCP 的弃用说明,常见替代方向有三种:

  • 工具参数:scan_project 接收明确的项目标识或相对路径,服务端按账号配置映射到实际目录。
  • 资源 URI:由客户端或服务端暴露已选择的资源,工具只处理传入的资源标识,不自行发现整棵文件树。
  • 服务端配置:在部署配置中固定允许的工作区,适合无人值守任务和严格的租户隔离。

迁移时可以保留 Roots 兼容层:旧客户端继续走 roots/list,新客户端走显式参数;两条路径最终都汇聚到同一个 isWithin 校验器和同一套审计事件。这样改动集中,退出 Roots 时也不必重新实现文件访问安全。

上线前用四个断言验收工作区边界

  • 没有 Roots 能力的客户端是否得到可解释的回退结果,而不是超时?
  • file:// URI 解码、路径规范化、符号链接和大小写处理是否有测试?
  • 根列表变更时,旧缓存、后台任务和打开的文件是否会被错误复用?
  • 新接口是否已经把范围放进工具参数、资源 URI 或服务端配置,并复用同一个边界校验器?

相关问题

Roots 已弃用后,旧客户端还能不能继续用?

可以继续做兼容,但要按目标协议版本和 SDK 的弃用提示安排迁移。兼容期内仍要保留能力核验、路径校验和更新通知处理。

拿到 Root URI 就能读取目录外的文件吗?

不能。Root 只是服务端可操作范围的协议输入,操作系统权限和每次目标路径的边界判断仍然有效。

客户端没有设置 listChanged 怎么办?

把根列表视为不会主动通知变化,按请求或连接生命周期重新获取,或者要求用户通过显式工具参数传入项目范围。

工具参数和 Roots 能否同时保留?

可以。兼容层可接受 Roots,新路径优先使用显式参数;两者都必须归一化到同一套授权和路径校验逻辑。

MCP Roots 的迁移重点不是把 roots/list 换成另一个 RPC,而是把“工作区边界”从隐含的客户端能力变成可验证、可审计的输入。旧协议先做好兼容,新项目再把范围收进工具参数、资源 URI 或服务端配置,文件访问才不会随着协议版本变化失去安全边界。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>