在MCP的核心概念中,**资源(Resources)**是最重要的基础组件之一。资源允许MCP服务器向客户端暴露数据和内容,为大语言模型(LLM)提供丰富的上下文信息。
什么是MCP资源?
MCP资源是服务器向客户端暴露的任何类型的数据,可以包括:
- 文件内容(源代码、配置文件、日志文件)
- 数据库记录
- API响应数据
- 实时系统数据
- 屏幕截图和图像
- 以及更多类型的数据
每个资源都通过唯一的URI进行标识,并且可以包含文本或二进制数据。
资源的控制模式
MCP资源采用应用程序控制的设计模式,这意味着客户端应用程序决定如何以及何时使用这些资源。不同的MCP客户端可能会以不同方式处理资源:
- Claude Desktop目前要求用户明确选择资源才能使用
- 其他客户端可能基于启发式算法自动选择资源
- 某些实现甚至允许AI模型自己决定使用哪些资源
资源URI结构
资源通过URI进行标识,遵循以下格式:
<protocol>://<host>/<path>
例如:
file:///home/user/documents/report.pdfpostgres://database/customers/schemascreen://localhost/display1
协议和路径结构由MCP服务器实现定义,服务器可以定义自己的自定义URI方案。
资源类型
MCP支持两种类型的资源内容:
文本资源
包含UTF-8编码的文本数据,适用于:
- 源代码
- 配置文件
- 日志文件
- JSON/XML数据
- 纯文本
二进制资源
包含base64编码的原始二进制数据,适用于:
- 图像文件
- PDF文档
- 音频文件
- 视频文件
- 其他非文本格式
TypeScript SDK实战演示
下面我们使用TypeScript SDK来创建一个完整的MCP服务器示例,展示如何实现资源功能:
1. 基础服务器设置
import { McpServer } from '@modelcontextprotocol/sdk';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import fs from 'fs/promises';
import path from 'path';
// 创建MCP服务器实例
const server = new McpServer({
name: "FileSystemResourceServer",
version: "1.0.0",
});
// 定义支持的资源类型
interface FileResource {
uri: string;
name: string;
description: string;
mimeType: string;
}
// 模拟文件系统数据
const fileDatabase: FileResource[] = [
{
uri: "file:///projects/config.json",
name: "应用配置文件",
description: "包含应用程序的主要配置信息",
mimeType: "application/json"
},
{
uri: "file:///projects/logs/app.log",
name: "应用日志",
description: "应用程序运行日志文件",
mimeType: "text/plain"
},
{
uri: "file:///projects/docs/readme.md",
name: "项目文档",
description: "项目的详细说明文档",
mimeType: "text/markdown"
}
];
2. 实现资源发现功能
// 处理资源列表请求
server.setRequestHandler('resources/list', async () => {
return {
resources: fileDatabase.map(file => ({
uri: file.uri,
name: file.name,
description: file.description,
mimeType: file.mimeType
}))
};
});
// 实现资源模板支持(用于动态资源)
server.setRequestHandler('resources/templates', async () => {
return {
resourceTemplates: [
{
uriTemplate: "file:///projects/{category}/{filename}",
name: "项目文件模板",
description: "访问项目目录下的任意文件",
mimeType: "text/plain"
}
]
};
});
3. 实现资源读取功能
// 处理资源读取请求
server.setRequestHandler('resources/read', async (request) => {
const { uri } = request.params;
try {
// 解析URI并获取文件路径
const filePath = decodeURIComponent(uri.replace('file://', ''));
// 安全检查:防止目录遍历攻击
if (filePath.includes('..') || !filePath.startsWith('/projects/')) {
throw new Error('无效的文件路径');
}
// 读取文件内容
const content = await fs.readFile(filePath, 'utf-8');
const stats = await fs.stat(filePath);
// 确定MIME类型
const mimeType = getMimeType(filePath);
return {
contents: [
{
uri: uri,
mimeType: mimeType,
text: content
}
]
};
} catch (error) {
throw new Error(`读取资源失败: ${error.message}`);
}
});
// MIME类型判断辅助函数
function getMimeType(filePath: string): string {
const ext = path.extname(filePath).toLowerCase();
const mimeTypes: { [key: string]: string } = {
'.json': 'application/json',
'.js': 'application/javascript',
'.ts': 'application/typescript',
'.md': 'text/markdown',
'.txt': 'text/plain',
'.log': 'text/plain',
'.html': 'text/html',
'.css': 'text/css',
'.xml': 'application/xml',
'.pdf': 'application/pdf',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg'
};
return mimeTypes[ext] || 'text/plain';
}
4. 实现资源订阅和更新机制
// 存储订阅信息
const subscriptions = new Set<string>();
// 处理资源订阅请求
server.setRequestHandler('resources/subscribe', async (request) => {
const { uri } = request.params;
subscriptions.add(uri);
console.log(`订阅资源: ${uri}`);
// 可以在这里设置文件监控
watchFileChanges(uri);
return {};
});
// 处理取消订阅请求
server.setRequestHandler('resources/unsubscribe', async (request) => {
const { uri } = request.params;
subscriptions.delete(uri);
console.log(`取消订阅资源: ${uri}`);
return {};
});
// 文件变化监控函数
async function watchFileChanges(uri: string) {
const filePath = decodeURIComponent(uri.replace('file://', ''));
try {
// 使用Node.js的fs.watch监控文件变化
const watcher = fs.watch(filePath, (eventType) => {
if (eventType === 'change' && subscriptions.has(uri)) {
// 发送资源更新通知
server.sendNotification('notifications/resources/updated', {
uri: uri
});
}
});
// 存储watcher以便后续清理
// 在实际应用中,你需要管理这些watcher的生命周期
} catch (error) {
console.error(`监控文件失败: ${error.message}`);
}
}
5. 高级功能:批量资源处理
// 处理目录资源读取(返回多个文件)
server.setRequestHandler('resources/read', async (request) => {
const { uri } = request.params;
if (uri.endsWith('/')) {
// 处理目录请求
return await readDirectory(uri);
} else {
// 处理单个文件请求
return await readSingleFile(uri);
}
});
async function readDirectory(dirUri: string) {
const dirPath = decodeURIComponent(dirUri.replace('file://', ''));
try {
const files = await fs.readdir(dirPath);
const contents = [];
for (const file of files) {
const filePath = path.join(dirPath, file);
const stats = await fs.stat(filePath);
if (stats.isFile()) {
const content = await fs.readFile(filePath, 'utf-8');
contents.push({
uri: `file://${filePath}`,
mimeType: getMimeType(filePath),
text: content
});
}
}
return { contents };
} catch (error) {
throw new Error(`读取目录失败: ${error.message}`);
}
}
async function readSingleFile(uri: string) {
const filePath = decodeURIComponent(uri.replace('file://', ''));
try {
const content = await fs.readFile(filePath, 'utf-8');
return {
contents: [{
uri: uri,
mimeType: getMimeType(filePath),
text: content
}]
};
} catch (error) {
throw new Error(`读取文件失败: ${error.message}`);
}
}
6. 启动服务器
// 启动服务器
async function main() {
// 创建标准输入输出传输
const transport = new StdioServerTransport();
// 连接服务器和传输
await server.connect(transport);
console.log('MCP资源服务器已启动');
}
// 处理优雅关闭
process.on('SIGINT', async () => {
console.log('正在关闭服务器...');
await server.close();
process.exit(0);
});
// 启动应用
main().catch(console.error);
最佳实践
在实现MCP资源支持时,应该遵循以下最佳实践:
1. 安全性考虑
- 验证所有资源URI
- 实现适当的访问控制
- 清理文件路径以防止目录遍历攻击
- 谨慎处理二进制数据
- 考虑对资源读取进行速率限制
2. 性能优化
- 为大型资源列表考虑分页
- 适当缓存资源内容
- 实现读取操作的超时机制
- 对频繁变化的资源使用订阅机制
3. 用户体验
- 使用清晰、描述性的资源名称和URI
- 包含有用的描述来指导LLM理解
- 在已知时设置适当的MIME类型
- 优雅地处理错误并提供清晰的错误消息
4. 扩展性设计
- 为动态内容实现资源模板
- 记录你的自定义URI方案
- 考虑未来的扩展需求
- 保持API的向后兼容性
总结
MCP资源是连接AI应用程序与外部数据源的强大机制。通过TypeScript SDK,我们可以轻松构建功能丰富、安全可靠的MCP服务器,为大语言模型提供所需的上下文数据。
资源的设计哲学体现了MCP协议的核心价值:标准化、安全性和灵活性。无论是简单的文件访问还是复杂的数据库查询,MCP资源都能提供一致的接口,让AI应用程序能够无缝访问各种数据源。
随着MCP生态系统的不断发展,资源概念将继续演进,为AI应用程序的数据集成提供更多可能性。掌握MCP资源的概念和实现方法,将为构建下一代AI应用程序奠定坚实的基础。