Resources 是 Model Context Protocol (MCP) 中的一个核心原语,它允许服务器暴露可以被 clients 读取并用作 LLM 交互上下文的数据和内容。

Resources 被设计为由应用控制,这意味着客户端应用程序可以决定如何以及何时使用它们。 不同的 MCP clients 可能会以不同的方式处理 resources。例如:

  • Claude Desktop 目前要求用户在可以使用 resources 之前显式地选择它们
  • 其他 clients 可能会根据启发式方法自动选择 resources
  • 某些实现甚至可能允许 AI 模型本身来确定使用哪些 resources

服务器作者在实现 resource 支持时,应该准备好处理任何这些交互模式。为了自动向模型暴露数据,服务器作者应该使用模型控制原语,例如 Tools

概述

Resources 代表 MCP server 想要提供给 clients 的任何类型的数据。这可以包括:

  • 文件内容
  • 数据库记录
  • API 响应
  • 实时系统数据
  • 屏幕截图和图像
  • 日志文件
  • 等等

每个 resource 都由一个唯一的 URI 标识,并且可以包含文本或二进制数据。

Resource URIs

Resources 使用以下格式的 URIs 进行标识:

[protocol]://[host]/[path]

例如:

  • file:///home/user/documents/report.pdf
  • postgres://database/customers/schema
  • screen://localhost/display1

协议和路径结构由 MCP server 实现定义。服务器可以定义自己的自定义 URI 方案。

Resource 类型

Resources 可以包含两种类型的内容:

文本资源

文本资源包含 UTF-8 编码的文本数据。这些适用于:

  • 源代码
  • 配置文件
  • 日志文件
  • JSON/XML 数据
  • 纯文本

二进制资源

二进制资源包含以 base64 编码的原始二进制数据。这些适用于:

  • 图像
  • PDFs
  • 音频文件
  • 视频文件
  • 其他非文本格式

Resource 发现

Clients 可以通过两种主要方法发现可用的 resources:

直接 resources

服务器通过 resources/list 端点暴露一个具体的 resources 列表。每个 resource 包括:

{
  uri: string;           // 资源的唯一标识符
  name: string;          // 易于理解的名称
  description?: string;  // 可选描述
  mimeType?: string;     // 可选 MIME 类型
}

Resource 模板

对于动态 resources,服务器可以暴露 URI 模板,clients 可以使用它们来构造有效的 resource URIs:

{
  uriTemplate: string;   // 遵循 RFC 6570 的 URI 模板
  name: string;          // 这种类型的易于理解的名称
  description?: string;  // 可选描述
  mimeType?: string;     // 所有匹配资源的 MIME 类型(可选)
}

读取 resources

要读取 resource,clients 使用 resource URI 发送 resources/read 请求。

服务器使用一个 resource 内容列表进行响应:

{
  contents: [
    {
      uri: string;        // 资源的 URI
      mimeType?: string;  // 可选 MIME 类型

      // 其中之一:
      text?: string;      // 对于文本资源
      blob?: string;      // 对于二进制资源(base64 编码)
    }
  ]
}

服务器可能会响应一个 resources/read 请求返回多个 resources。例如,当读取目录时,可以使用此方法返回目录中的文件列表。

Resource 更新

MCP 通过两种机制支持 resources 的实时更新:

列表更改

当可用 resources 列表发生更改时,服务器可以通过 notifications/resources/list_changed 通知 clients。

内容更改

Clients 可以订阅特定 resources 的更新:

  1. Client 使用 resource URI 发送 resources/subscribe
  2. 当 resource 更改时,服务器发送 notifications/resources/updated
  3. Client 可以使用 resources/read 获取最新内容
  4. Client 可以使用 resources/unsubscribe 取消订阅

实现示例

以下是在 MCP server 中实现 resource 支持的简单示例:

const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {
    resources: {}
  }
});

// 列出可用 resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
  return {
    resources: [
      {
        uri: "file:///logs/app.log",
        name: "Application Logs",
        mimeType: "text/plain"
      }
    ]
  };
});

// 读取 resource 内容
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  const uri = request.params.uri;

  if (uri === "file:///logs/app.log") {
    const logContents = await readLogFile();
    return {
      contents: [
        {
          uri,
          mimeType: "text/plain",
          text: logContents
        }
      ]
    };
  }

  throw new Error("Resource not found");
});

最佳实践

在实现 resource 支持时:

  1. 使用清晰、描述性的 resource 名称和 URIs
  2. 包含有用的描述以指导 LLM 理解
  3. 在已知时设置适当的 MIME 类型
  4. 为动态内容实现 resource 模板
  5. 对频繁更改的 resources 使用订阅
  6. 使用清晰的错误消息优雅地处理错误
  7. 考虑对大型 resource 列表进行分页
  8. 在适当的时候缓存 resource 内容
  9. 在处理之前验证 URIs
  10. 记录你的自定义 URI 方案

安全考虑

在暴露 resources 时:

  • 验证所有 resource URIs
  • 实现适当的访问控制
  • 清理文件路径以防止目录遍历
  • 谨慎处理二进制数据
  • 考虑对 resource 读取进行速率限制
  • 审核 resource 访问
  • 加密传输中的敏感数据
  • 验证 MIME 类型
  • 为长时间运行的读取操作实现超时
  • 适当处理 resource 清理