Skip to main content
Glama

Tencent Cloud COS MCP Server

by 156554395
index.js19.5 kB
#!/usr/bin/env node /** * TX-COS-MCP 服务器主入口文件 * 基于 Model Context Protocol (MCP) 的腾讯云 COS 服务器实现 * * 提供以下功能: * - 文件上传(单个/批量) * - 文件操作(复制/移动/重命名) * - 文件删除(单个/批量) * - 文件夹管理(创建/删除/列举) * - 存储统计分析 * - 临时URL生成 * * @author 156554395@qq.com * @version 1.2.0 */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import fs from 'fs'; import { cosService } from './src/cosService.js'; // 创建MCP服务器实例 const server = new Server( { name: 'tx-cos-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); /** * MCP工具定义 * 每个工具对应一个COS操作功能,包含名称、描述和输入参数架构 */ const tools = { // 文件上传工具 upload_file: { name: 'upload_file', description: '上传单个文件到腾讯云COS', inputSchema: { type: 'object', properties: { file_path: { type: 'string', description: '本地文件路径' }, object_key: { type: 'string', description: '上传后在COS中的对象键名,如果未提供则使用文件名' }, custom_domain: { type: 'string', description: '自定义访问域名(可选)' } }, required: ['file_path'] } }, upload_multiple: { name: 'upload_multiple', description: '批量上传多个文件到腾讯云COS,支持智能并发控制和重试机制', inputSchema: { type: 'object', properties: { files: { type: 'array', items: { type: 'object', properties: { file_path: { type: 'string', description: '本地文件路径' }, object_key: { type: 'string', description: '上传后在COS中的对象键名' } }, required: ['file_path'] }, description: '文件数组,每个元素包含file_path和可选的object_key' }, concurrency: { type: 'number', description: '并发上传数量,默认3,最大20' }, max_retries: { type: 'number', description: '单个文件最大重试次数,默认3' } }, required: ['files'] } }, // 大文件分片上传工具 upload_large_file: { name: 'upload_large_file', description: '使用分片上传方式上传大文件到腾讯云COS,支持断点续传和进度监控', inputSchema: { type: 'object', properties: { file_path: { type: 'string', description: '本地文件路径' }, object_key: { type: 'string', description: '上传后在COS中的对象键名,如果未提供则使用文件名' }, custom_domain: { type: 'string', description: '自定义访问域名(可选)' }, chunk_size: { type: 'number', description: '分片大小(字节),默认1MB,最小1MB' }, concurrency: { type: 'number', description: '并发上传数量,默认3,最大10' }, force_slice: { type: 'boolean', description: '强制使用分片上传,即使文件较小' } }, required: ['file_path'] } }, // 上传进度管理工具 get_upload_progress: { name: 'get_upload_progress', description: '获取当前所有未完成上传的进度信息', inputSchema: { type: 'object', properties: { session_id: { type: 'string', description: '特定会话ID,不提供则返回所有未完成上传' } } } }, clear_upload_progress: { name: 'clear_upload_progress', description: '清理指定的上传进度记录', inputSchema: { type: 'object', properties: { session_id: { type: 'string', description: '要清理的会话ID' } }, required: ['session_id'] } }, // 临时文件管理工具 manage_temp_files: { name: 'manage_temp_files', description: '管理临时文件和目录,支持清理和统计功能', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['stats', 'cleanup'], description: '操作类型:stats(统计) 或 cleanup(清理)' }, type: { type: 'string', enum: ['progress', 'cache', 'uploads', 'all'], description: '文件类型,默认all' }, older_than_days: { type: 'number', description: '清理多少天前的文件,默认7天' } }, required: ['action'] } }, // URL工具 get_signed_url: { name: 'get_signed_url', description: '获取COS对象的临时签名URL', inputSchema: { type: 'object', properties: { object_key: { type: 'string', description: 'COS中的对象键名' }, expire_time: { type: 'number', description: 'URL有效期(秒),默认3600秒' } }, required: ['object_key'] } }, // 列表和查询工具 list_objects: { name: 'list_objects', description: '列出COS中的对象', inputSchema: { type: 'object', properties: { prefix: { type: 'string', description: '对象键的过滤前缀,默认列出所有对象' } } } }, // 删除工具 delete_object: { name: 'delete_object', description: '删除COS中的对象', inputSchema: { type: 'object', properties: { object_key: { type: 'string', description: '要删除的对象键名' } }, required: ['object_key'] } }, // 文件操作工具 copy_object: { name: 'copy_object', description: '复制COS中的对象', inputSchema: { type: 'object', properties: { source_key: { type: 'string', description: '源对象键名' }, target_key: { type: 'string', description: '目标对象键名' }, target_bucket: { type: 'string', description: '目标存储桶名称(可选,默认为当前存储桶)' } }, required: ['source_key', 'target_key'] } }, move_object: { name: 'move_object', description: '移动COS中的对象', inputSchema: { type: 'object', properties: { source_key: { type: 'string', description: '源对象键名' }, target_key: { type: 'string', description: '目标对象键名' }, target_bucket: { type: 'string', description: '目标存储桶名称(可选,默认为当前存储桶)' } }, required: ['source_key', 'target_key'] } }, rename_object: { name: 'rename_object', description: '重命名COS中的对象', inputSchema: { type: 'object', properties: { old_key: { type: 'string', description: '原对象键名' }, new_key: { type: 'string', description: '新对象键名' } }, required: ['old_key', 'new_key'] } }, // 批量操作工具 delete_multiple: { name: 'delete_multiple', description: '批量删除COS中的对象', inputSchema: { type: 'object', properties: { object_keys: { type: 'array', items: { type: 'string' }, description: '要删除的对象键名数组' } }, required: ['object_keys'] } }, // 文件夹管理工具 create_folder: { name: 'create_folder', description: '在COS中创建文件夹', inputSchema: { type: 'object', properties: { folder_path: { type: 'string', description: '文件夹路径' } }, required: ['folder_path'] } }, delete_folder: { name: 'delete_folder', description: '删除COS中的文件夹', inputSchema: { type: 'object', properties: { folder_path: { type: 'string', description: '文件夹路径' }, recursive: { type: 'boolean', description: '是否递归删除(删除文件夹及其所有内容),默认false' } }, required: ['folder_path'] } }, list_folders: { name: 'list_folders', description: '列出COS中的文件夹和文件', inputSchema: { type: 'object', properties: { prefix: { type: 'string', description: '路径前缀,默认为根目录' } } } }, // 统计分析工具 get_folder_stats: { name: 'get_folder_stats', description: '获取文件夹统计信息', inputSchema: { type: 'object', properties: { folder_path: { type: 'string', description: '文件夹路径,为空则统计整个存储桶' } } } } }; /** * 注册MCP工具列表请求处理器 * 返回所有可用工具的定义 */ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: Object.values(tools), })); /** * 注册MCP工具调用请求处理器 * 根据工具名称执行相应的COS操作 * * @param {Object} request - MCP请求对象 * @param {string} request.params.name - 工具名称 * @param {Object} request.params.arguments - 工具参数 * @returns {Promise<Object>} 工具执行结果 */ server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { // 文件上传操作 case 'upload_file': const cleanPath = validateFileExists(args.file_path); const result = await cosService.uploadFile(cleanPath, { key: args.object_key, customDomain: args.custom_domain }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: result }, null, 2) } ] }; case 'upload_multiple': // 验证所有文件是否存在 const uploadPromises = args.files.map(async (file) => { const cleanFilePath = validateFileExists(file.file_path); return { path: cleanFilePath, key: file.object_key }; }); const validatedFiles = await Promise.all(uploadPromises); // 参数验证和默认值设置 const batchConcurrency = args.concurrency ? Math.min(20, Math.max(1, args.concurrency)) : 3; const batchMaxRetries = args.max_retries ? Math.max(0, args.max_retries) : 3; const uploadResults = await cosService.uploadMultipleFiles(validatedFiles, { concurrency: batchConcurrency, maxRetries: batchMaxRetries, onProgress: (progress) => { // 静默处理批量上传进度,避免污染MCP协议 } }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: uploadResults }, null, 2) } ] }; case 'upload_large_file': const cleanLargeFilePath = validateFileExists(args.file_path); // 参数验证和默认值设置 const chunkSize = args.chunk_size ? Math.max(1024 * 1024, args.chunk_size) : 1024 * 1024; // 最小1MB const concurrency = args.concurrency ? Math.min(10, Math.max(1, args.concurrency)) : 3; // 1-10之间 const largeFileResult = await cosService.uploadFile(cleanLargeFilePath, { key: args.object_key, customDomain: args.custom_domain, useSliceUpload: args.force_slice || true, chunkSize: chunkSize, concurrency: concurrency, onProgress: (progress) => { // 这里不输出进度日志,避免污染MCP协议 } }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: largeFileResult }, null, 2) } ] }; case 'get_upload_progress': const progressResult = await cosService.getUploadProgress(args.session_id); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: progressResult }, null, 2) } ] }; case 'clear_upload_progress': const clearResult = await cosService.clearUploadProgress(args.session_id); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: clearResult }, null, 2) } ] }; case 'manage_temp_files': const tempResult = await cosService.manageTempFiles(args.action, { type: args.type, olderThanDays: args.older_than_days }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: tempResult }, null, 2) } ] }; // URL工具操作 case 'get_signed_url': const urlResult = await cosService.getSignedUrl(args.object_key, args.expire_time); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: urlResult }, null, 2) } ] }; // 列表和查询操作 case 'list_objects': const listResult = await cosService.listObjects(args.prefix || ''); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: listResult }, null, 2) } ] }; // 删除操作 case 'delete_object': const deleteResult = await cosService.deleteObject(args.object_key); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: deleteResult }, null, 2) } ] }; // 文件操作 case 'copy_object': const copyResult = await cosService.copyObject(args.source_key, args.target_key, { targetBucket: args.target_bucket }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: copyResult }, null, 2) } ] }; case 'move_object': const moveResult = await cosService.moveObject(args.source_key, args.target_key, { targetBucket: args.target_bucket }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: moveResult }, null, 2) } ] }; case 'rename_object': const renameResult = await cosService.renameObject(args.old_key, args.new_key); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: renameResult }, null, 2) } ] }; // 批量操作 case 'delete_multiple': const batchDeleteResult = await cosService.deleteMultipleObjects(args.object_keys); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: batchDeleteResult }, null, 2) } ] }; // 文件夹管理操作 case 'create_folder': const createFolderResult = await cosService.createFolder(args.folder_path); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: createFolderResult }, null, 2) } ] }; case 'delete_folder': const deleteFolderResult = await cosService.deleteFolder(args.folder_path, { recursive: args.recursive || false }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: deleteFolderResult }, null, 2) } ] }; case 'list_folders': const listFoldersResult = await cosService.listFolders(args.prefix || ''); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: listFoldersResult }, null, 2) } ] }; // 统计分析操作 case 'get_folder_stats': const folderStatsResult = await cosService.getFolderStats(args.folder_path || ''); return { content: [ { type: 'text', text: JSON.stringify({ success: true, data: folderStatsResult }, null, 2) } ] }; default: throw new Error(`未知工具: ${name}`); } } catch (error) { // 统一错误处理,返回错误信息 return { content: [ { type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) } ], isError: true }; } }); /** * 验证本地文件是否存在 * @param {string} filePath - 文件路径 * @throws {Error} 当文件不存在或路径不是文件时抛出错误 */ function validateFileExists(filePath) { // 清理文件路径,去除首尾空白字符 const cleanPath = filePath.trim(); if (!cleanPath) { throw new Error('文件路径不能为空'); } if (!fs.existsSync(cleanPath)) { throw new Error(`文件不存在: ${cleanPath}`); } const stats = fs.statSync(cleanPath); if (!stats.isFile()) { throw new Error(`路径不是文件: ${cleanPath}`); } // 返回清理后的路径,供后续使用 return cleanPath; } /** * 服务器主函数 - 启动MCP服务器 * 使用标准输入输出传输进行通信 */ async function main() { try { const transport = new StdioServerTransport(); await server.connect(transport); // 只在调试模式下输出启动信息,避免干扰 MCP 协议通信 if (process.env.MCP_DEBUG) { console.error('腾讯云COS MCP服务器已启动'); } } catch (error) { console.error('服务器启动失败:', error); process.exit(1); } } /** * 程序入口点检查 * 检查是否直接运行此文件或通过 npx/bin 调用 * 如果是,则启动服务器 */ if (process.argv[1] === new URL(import.meta.url).pathname || process.argv[1].endsWith('tx-cos-mcp')) { main(); } // 导出服务器实例供其他模块使用 export default server;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/156554395/tx-cos-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server