TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

MCP roots 是什么:文件范围提示与弃用说明

MCP 的 roots 让客户端告诉服务器哪些文件位置与请求相关。它只是提示而不是访问控制,并已在 2026-07-28 版规范中弃用,新项目应改用工具参数等方式。

这篇适合谁

适合正在开发 MCP 服务器、或在做文件类工具的开发者,尤其是在老示例里见过 roots 却不确定还该不该用的人。读完你应该能分清 roots 是什么、不是什么,并知道新项目该用哪种方式替代它。

roots 是什么

roots 是 MCP 客户端告诉服务器「当前请求相关的文件系统位置」的一种机制。每个 root 包含一个 file:// 开头的 URI,还可以带一个给人看的名称[1]。客户端返回的数据是这样的:

{
  "roots": [
    {
      "uri": "file:///home/user/projects/weather-service",
      "name": "Weather Service"
    }
  ]
}

roots 不是访问控制

这是最容易误解的一点。roots 只是提示,它不是对话历史的容器,不是协议会话,也不是权限系统。协议本身不会强制服务器待在列出的 root 之内[1]。所以客户端只应暴露用户批准过的位置;服务器则要把 roots 当作「哪些文件可能相关」的参考,而不是「可以读取」的证明[1]。

2026-07-28 版已经弃用

roots 在 MCP 2026-07-28 版规范中被标记为弃用。它为了兼容性仍保留在这一版里,并且有资格在 2027 年 7 月 28 日或之后发布的第一个规范修订版中被移除[1]。新实现不应再以它为基础。

2026-07-28 版的调用流程

支持 roots 的客户端,每个请求都要在 _meta 里声明这项能力[1]:

{
  "_meta": {
    "io.modelcontextprotocol/clientCapabilities": {
      "roots": {}
    }
  }
}

服务器处理请求时如果需要 roots,可以返回一个 InputRequiredResult,其中包含 roots/list 的输入请求[1]:

{
  "resultType": "input_required",
  "inputRequests": {
    "workspaceRoots": {
      "method": "roots/list"
    }
  },
  "requestState": "opaque-state-from-server"
}

客户端收集用户批准的 roots,然后带上对应的 inputResponses、原样保留的 requestState 重试原请求[1]。正是这种多轮往返让协议保持无状态:没有 initialize 握手,协议层面也没有会话[1]。

和 2025-11-25 版有什么不同

在 MCP 2025-11-25 版中,客户端在初始化时声明 roots,服务器可以直接发送 roots/list 请求,roots 变化时客户端会发送 notifications/roots/list_changed[1]。这套做法属于旧的生命周期,不要把它的初始化和通知示例混进 2026-07-28 版的实现里[1]。

新项目用什么替代

三种替代方式如下[1]。

  1. 工具参数。把需要的目录或文件写进工具的输入 schema,作为必填的显式参数。例如:
{
  "name": "analyze_project",
  "inputSchema": {
    "type": "object",
    "properties": {
      "projectDirectory": {
        "type": "string",
        "description": "Approved project directory to analyze"
      }
    },
    "required": ["projectDirectory"]
  }
}
  1. 资源 URI。服务器能通过稳定的 URI 暴露相关文件时,改用 MCP Resources。发现和读取两步都变得明确。
  1. 服务器配置。部署固定时,在服务器启动时就配置好允许访问的目录。比起在工具调用里再去发现,这通常更清楚。

不管选哪种都要做的安全检查

无论选哪一种替代方式,都要满足以下要求[1]:

  • 暴露文件系统位置之前,先取得用户同意。
  • 对路径做规范化并校验,防止路径穿越。
  • 授权和沙箱要独立实施,不能只依赖 root 的值。
  • 访问文件时重新检查权限,而不是只在列出文件时检查。
  • 不要在日志或错误信息里返回敏感路径。

怎么判断一段旧代码属于哪个版本

2025-11-25 版的特征是:客户端在初始化时声明 roots,服务器可以直接发送 roots/list,roots 变化时客户端发送 notifications/roots/list_changed[1]。2026-07-28 版则把能力声明放在每个请求的 _meta 里,服务器用 InputRequiredResult 请求 roots[1]。如果一段代码既有初始化握手,又有 InputRequiredResult 的写法,说明它把两个版本混在了一起,这种做法不建议采用[1]。

为什么不能把 roots 当成权限

客户端只应该暴露用户批准过的位置[1]。服务器收到 roots 后,只把它当作相关文件的线索,仍然要对路径做规范化和校验,防止路径穿越[1]。权限要在文件被访问时重新检查,因为列出时允许的文件,打开时未必还允许[1]。日志和错误信息里也不要暴露敏感路径[1]。

三种替代方式怎么选

工具参数最直接:调用时把目录或文件作为必填参数传进来,在 schema 里一目了然[1]。资源 URI 适合服务器能用稳定地址暴露文件的场景,发现和读取两步都更明确[1]。服务器配置适合部署固定的情况,允许访问的目录在启动时就定好,比在工具调用里再去发现更清楚[1]。

常见问题

能不能只靠 roots 限制服务器能读的范围? 不能。roots 不是访问控制机制,协议也不强制执行[1]。限制应该写在服务器自己的代码、配置和权限检查里。

为什么读文件时还要再查一次权限? 文件在列出时可以访问,不代表真正打开时仍然允许。要在访问时检查,而不只是在列出时检查[1]。

本文改编自 Microsoft 开源课程 *MCP for Beginners* 中关于 MCP roots 的章节[1],原文以 MIT 许可证发布。

来源

  1. 1
    MCP Roots (Legacy Feature)Microsoft mcp-for-beginners (MIT)

引用本页: MCP roots 是什么:文件范围提示与弃用说明. TellWorld, https://www.tellworld.net/zh/practice/practice-mcp-root-contexts. 更新于 2026-11-14

相关动态