Skip to Content

资源

将服务端中的数据与内容暴露给LLM使用

资源是模型上下文协议(MCP)中的核心原语,允许服务端暴露数据与内容,供客户端读取并用作LLM交互的上下文。

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

  • Claude桌面目前要求用户在使用资源前明确选择
  • 其他客户端可能会基于启发式方法自动选择资源
  • 某些实现甚至可能允许AI模型自行决定使用哪些资源

服务端开发者在实现资源支持时,应准备好处理这些交互模式。为了自动向模型暴露数据,服务端开发者应使用模型控制的原语,例如工具

概述

资源表示MCP服务端希望向客户端提供的任何类型的数据。这可以包括:

  • 文件内容
  • 数据库记录
  • API响应
  • 实时系统数据
  • 截图和图像
  • 日志文件
  • 以及其他更多内容

每个资源通过唯一的URI标识,可以包含文本或二进制数据。

资源URI

资源使用以下格式的URI进行标识:

[协议]://[主机]/[路径]

例如:

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

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

资源类型

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

文本资源

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

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

二进制资源

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

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

资源发现

客户端可以通过两种主要方法发现可用资源:

直接资源

服务端通过resources/list端点暴露具体资源列表。每个资源包括:

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

资源模板

对于动态资源,服务端可以暴露URI模板,客户端可以使用这些模板构建有效的资源URI:

{ uriTemplate: string; // 遵循RFC 6570的URI模板 name: string; // 此类资源的人类可读名称 description?: string; // 可选描述 mimeType?: string; // 所有匹配资源的可选MIME类型 }

读取资源

要读取资源,客户端使用资源URI发起resources/read请求。

服务端响应资源内容列表:

{ contents: [ { uri: string; // 资源的URI mimeType?: string; // 可选的MIME类型 // 以下之一: text?: string; // 用于文本资源 blob?: string; // 用于二进制资源(base64编码) } ] }

服务端可以响应一个resources/read请求返回多个资源。例如,当读取目录时,可以返回目录中的文件列表。

资源更新

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

列表变更

服务端可以通过notifications/resources/list_changed通知客户端可用资源列表的变化。

内容变更

客户端可以订阅特定资源的更新:

  1. 客户端发送resources/subscribe并附带资源URI
  2. 当资源发生变化时,服务端发送notifications/resources/updated
  3. 客户端可以通过resources/read获取最新内容
  4. 客户端可以通过resources/unsubscribe取消订阅

实现示例

以下是一个在MCP服务端中实现资源支持的简单示例:

const server = new Server({ name: "example-server", version: "1.0.0" }, { capabilities: { resources: {} } }); // 列出可用资源 server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: "file:///logs/app.log", name: "应用程序日志", mimeType: "text/plain" } ] }; }); // 读取资源内容 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("未找到资源"); });

最佳实践

在实现资源支持时:

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

安全注意事项

在暴露资源时:

  • 验证所有资源URI
  • 实施适当的访问控制
  • 清理文件路径以防止目录遍历
  • 谨慎处理二进制数据
  • 考虑对资源读取进行速率限制
  • 审计资源访问
  • 在传输中加密敏感数据
  • 验证MIME类型
  • 对长时间运行的读取操作实施超时
  • 适当处理资源清理
最后更新于: