模型上下文协议(MCP)资源概念的解析
资源是指将MCP服务器上提供给 LLM 使用的数据和内容。 资源是模型上下文协议(MCP)的核心概念之一,允许服务器向客户端提供数据和内容,供 LLM 交互时使用。

资源是指将MCP服务器上提供给 LLM 使用的数据和内容。
资源是模型上下文协议(MCP)的核心概念之一,允许服务器向客户端提供数据和内容,供 LLM 交互时使用。
资源是 应用程序控制 的,这意味着客户端应用程序可以决定如何以及何时使用它们。不同的 MCP 客户端可能会以不同的方式处理资源。例如:
- Claude 桌面版目前要求用户明确选择资源后才能使用
- 其他客户端可能基于启发式方法自动选择资源
- 某些实现甚至允许 AI 模型自身决定使用哪些资源
MCP服务器开发者在实现资源支持时,应准备好处理这些不同的交互模式。如果需要自动向模型提供数据,MCP服务器开发者应使用 模型控制 的另一个核心概念,例如:工具。
概览
资源可以表示 MCP 服务器希望向客户端提供的任何类型的数据。这些数据包括但不限于:
- 文件内容
- 数据库记录
- API 响应
- 实时系统数据
- 屏幕截图和图像
- 日志文件
- 其他类型的数据
每个资源都有唯一的 URI 标识,可以包含文本或二进制数据。
资源 URI
资源通过以下格式的 URI 进行标识:
[protocol]://[host]/[path]
例如:
file:///home/user/documents/report.pdf
postgres://database/customers/schema
screen://localhost/display1
URI 的协议和路径结构由 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
通知通知客户端资源列表的变更。
内容变更
客户端可以订阅特定资源的更新:
- 客户端发送
resources/subscribe
请求,附带资源 URI - 服务器通过
notifications/resources/updated
通知资源变更 - 客户端可以通过
resources/read
获取最新内容 - 客户端可以通过
resources/unsubscribe
取消订阅
示例实现
以下是 MCP 服务器中实现资源支持的简单示例:
TypeScript
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: "Application Logs",
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("Resource not found");
});
Python
app = Server("example-server")
@app.list_resources()
async def list_resources() -> list[types.Resource]:
return [
types.Resource(
uri="file:///logs/app.log",
name="Application Logs",
mimeType="text/plain"
)
]
@app.read_resource()
async def read_resource(uri: AnyUrl) -> str:
if str(uri) == "file:///logs/app.log":
log_contents = await read_log_file()
return log_contents
raise ValueError("Resource not found")
# 启动服务器
async with stdio_server() as streams:
await app.run(
streams[0],
streams[1],
app.create_initialization_options()
)
最佳实践
实现资源支持时:
- 使用清晰、描述性的资源名称和 URI
- 包含有帮助的描述,指导 LLM 理解
- 在已知情况下设置适当的 MIME 类型
- 为动态内容实现资源模板
- 为频繁变更的资源使用订阅机制
- 处理错误时提供清晰的错误消息
- 对于大资源列表考虑分页
- 在适当情况下缓存资源内容
- 在处理前验证 URI
- 文档化自定义 URI 方案
安全注意事项
暴露资源时:
- 验证所有资源 URI
- 实现适当的访问控制
- 清理文件路径,防止目录遍历攻击
- 谨慎处理二进制数据
- 对资源读取进行速率限制
- 审计资源访问
- 在传输中加密敏感数据
- 验证 MIME 类型
- 为长时间运行的读取操作实现超时机制
- 正确处理资源清理