Skip to main content
Glama

MinIO Storage MCP

index.ts23.9 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { MinIOStorageClient } from './minio-client.js'; import { MinIOConfigSchema } from './types.js'; // 解析命令行参数 function parseArgs() { const args = process.argv.slice(2); const config: any = {}; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg.startsWith('--endpoint=')) { config.endPoint = arg.split('=')[1]; } else if (arg.startsWith('--port=')) { config.port = parseInt(arg.split('=')[1]); } else if (arg.startsWith('--access-key=')) { config.accessKey = arg.split('=')[1]; } else if (arg.startsWith('--secret-key=')) { config.secretKey = arg.split('=')[1]; } else if (arg.startsWith('--use-ssl=')) { config.useSSL = arg.split('=')[1] === 'true'; } else if (arg.startsWith('--region=')) { config.region = arg.split('=')[1]; } } return config; } class MinIOMCPServer { private server: Server; private minioClient: MinIOStorageClient; private autoConnectConfig: any; constructor() { this.server = new Server( { name: 'minio-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.minioClient = new MinIOStorageClient(); this.autoConnectConfig = parseArgs(); this.setupToolHandlers(); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'connect_minio', description: '连接到MinIO服务器', inputSchema: { type: 'object', properties: { endPoint: { type: 'string', description: 'MinIO服务器地址' }, port: { type: 'number', description: 'MinIO服务器端口' }, useSSL: { type: 'boolean', description: '是否使用SSL连接', default: false }, accessKey: { type: 'string', description: '访问密钥' }, secretKey: { type: 'string', description: '秘密密钥' }, region: { type: 'string', description: '区域设置(可选)' } }, required: ['endPoint', 'port', 'accessKey', 'secretKey'] } }, { name: 'list_buckets', description: '列出所有存储桶', inputSchema: { type: 'object', properties: {} } }, { name: 'create_bucket', description: '创建存储桶', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, region: { type: 'string', description: '区域设置(可选)' } }, required: ['bucketName'] } }, { name: 'delete_bucket', description: '删除存储桶', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' } }, required: ['bucketName'] } }, { name: 'bucket_exists', description: '检查存储桶是否存在', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' } }, required: ['bucketName'] } }, { name: 'list_objects', description: '列出存储桶中的对象', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, prefix: { type: 'string', description: '对象名前缀(可选)' }, recursive: { type: 'boolean', description: '是否递归列出', default: false } }, required: ['bucketName'] } }, { name: 'upload_file', description: '上传文件到存储桶', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, objectName: { type: 'string', description: '对象名称' }, filePath: { type: 'string', description: '本地文件路径' }, metadata: { type: 'object', description: '文件元数据(可选)' } }, required: ['bucketName', 'objectName', 'filePath'] } }, { name: 'download_file', description: '从存储桶下载文件', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, objectName: { type: 'string', description: '对象名称' }, filePath: { type: 'string', description: '本地保存路径' } }, required: ['bucketName', 'objectName', 'filePath'] } }, { name: 'delete_object', description: '删除存储桶中的对象', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, objectName: { type: 'string', description: '对象名称' } }, required: ['bucketName', 'objectName'] } }, { name: 'delete_objects', description: '批量删除存储桶中的对象', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, objectNames: { type: 'array', items: { type: 'string' }, description: '对象名称列表' } }, required: ['bucketName', 'objectNames'] } }, { name: 'copy_object', description: '复制对象', inputSchema: { type: 'object', properties: { sourceBucket: { type: 'string', description: '源存储桶名称' }, sourceObject: { type: 'string', description: '源对象名称' }, destBucket: { type: 'string', description: '目标存储桶名称' }, destObject: { type: 'string', description: '目标对象名称' } }, required: ['sourceBucket', 'sourceObject', 'destBucket', 'destObject'] } }, { name: 'get_object_info', description: '获取对象信息', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, objectName: { type: 'string', description: '对象名称' } }, required: ['bucketName', 'objectName'] } }, { name: 'generate_presigned_url', description: '生成预签名URL', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, objectName: { type: 'string', description: '对象名称' }, method: { type: 'string', enum: ['GET', 'PUT', 'DELETE'], description: 'HTTP方法', default: 'GET' }, expires: { type: 'number', description: '过期时间(秒)', default: 3600 } }, required: ['bucketName', 'objectName'] } }, { name: 'get_storage_stats', description: '获取存储统计信息', inputSchema: { type: 'object', properties: {} } }, { name: 'upload_files', description: '批量上传文件', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, files: { type: 'array', items: { type: 'object', properties: { localPath: { type: 'string', description: '本地文件路径' }, objectName: { type: 'string', description: '对象名称' }, metadata: { type: 'object', description: '文件元数据(可选)' } }, required: ['localPath', 'objectName'] }, description: '文件列表' } }, required: ['bucketName', 'files'] } }, { name: 'download_files', description: '批量下载文件', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, files: { type: 'array', items: { type: 'object', properties: { objectName: { type: 'string', description: '对象名称' }, localPath: { type: 'string', description: '本地保存路径' } }, required: ['objectName', 'localPath'] }, description: '文件列表' } }, required: ['bucketName', 'files'] } }, { name: 'set_bucket_policy', description: '设置存储桶策略', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' }, policy: { type: 'string', description: 'JSON格式的策略' } }, required: ['bucketName', 'policy'] } }, { name: 'get_bucket_policy', description: '获取存储桶策略', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' } }, required: ['bucketName'] } }, { name: 'delete_bucket_policy', description: '删除存储桶策略', inputSchema: { type: 'object', properties: { bucketName: { type: 'string', description: '存储桶名称' } }, required: ['bucketName'] } } ] }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'connect_minio': { const config = MinIOConfigSchema.parse(args); await this.minioClient.connect(config); return { content: [ { type: 'text', text: `成功连接到MinIO服务器 ${config.endPoint}:${config.port}` } ] }; } case 'list_buckets': { const buckets = await this.minioClient.listBuckets(); return { content: [ { type: 'text', text: `找到 ${buckets.length} 个存储桶:\n${buckets.map(b => `- ${b.name} (创建时间: ${b.creationDate.toISOString()})`).join('\n')}` } ] }; } case 'create_bucket': { const { bucketName, region } = z.object({ bucketName: z.string(), region: z.string().optional() }).parse(args); await this.minioClient.createBucket(bucketName, region); return { content: [ { type: 'text', text: `成功创建存储桶: ${bucketName}` } ] }; } case 'delete_bucket': { const { bucketName } = z.object({ bucketName: z.string() }).parse(args); await this.minioClient.deleteBucket(bucketName); return { content: [ { type: 'text', text: `成功删除存储桶: ${bucketName}` } ] }; } case 'bucket_exists': { const { bucketName } = z.object({ bucketName: z.string() }).parse(args); const exists = await this.minioClient.bucketExists(bucketName); return { content: [ { type: 'text', text: `存储桶 ${bucketName} ${exists ? '存在' : '不存在'}` } ] }; } case 'list_objects': { const { bucketName, prefix, recursive } = z.object({ bucketName: z.string(), prefix: z.string().optional(), recursive: z.boolean().default(false) }).parse(args); const objects = await this.minioClient.listObjects(bucketName, prefix, recursive); return { content: [ { type: 'text', text: `存储桶 ${bucketName} 中找到 ${objects.length} 个对象:\n${objects.map(obj => `- ${obj.name} (大小: ${obj.size} 字节, 修改时间: ${obj.lastModified.toISOString()})` ).join('\n')}` } ] }; } case 'upload_file': { const { bucketName, objectName, filePath, metadata } = z.object({ bucketName: z.string(), objectName: z.string(), filePath: z.string(), metadata: z.record(z.string()).optional() }).parse(args); await this.minioClient.uploadFile(bucketName, objectName, filePath, metadata); return { content: [ { type: 'text', text: `成功上传文件 ${filePath} 到 ${bucketName}/${objectName}` } ] }; } case 'download_file': { const { bucketName, objectName, filePath } = z.object({ bucketName: z.string(), objectName: z.string(), filePath: z.string() }).parse(args); await this.minioClient.downloadFile(bucketName, objectName, filePath); return { content: [ { type: 'text', text: `成功下载文件 ${bucketName}/${objectName} 到 ${filePath}` } ] }; } case 'delete_object': { const { bucketName, objectName } = z.object({ bucketName: z.string(), objectName: z.string() }).parse(args); await this.minioClient.deleteObject(bucketName, objectName); return { content: [ { type: 'text', text: `成功删除对象: ${bucketName}/${objectName}` } ] }; } case 'delete_objects': { const { bucketName, objectNames } = z.object({ bucketName: z.string(), objectNames: z.array(z.string()) }).parse(args); const result = await this.minioClient.deleteObjects(bucketName, objectNames); return { content: [ { type: 'text', text: `批量删除完成: 成功 ${result.successCount} 个, 失败 ${result.failureCount} 个${result.errors.length > 0 ? '\n错误:\n' + result.errors.map(e => `- ${e.item}: ${e.error}`).join('\n') : ''}` } ] }; } case 'copy_object': { const { sourceBucket, sourceObject, destBucket, destObject } = z.object({ sourceBucket: z.string(), sourceObject: z.string(), destBucket: z.string(), destObject: z.string() }).parse(args); await this.minioClient.copyObject(sourceBucket, sourceObject, destBucket, destObject); return { content: [ { type: 'text', text: `成功复制对象从 ${sourceBucket}/${sourceObject} 到 ${destBucket}/${destObject}` } ] }; } case 'get_object_info': { const { bucketName, objectName } = z.object({ bucketName: z.string(), objectName: z.string() }).parse(args); const info = await this.minioClient.getObjectInfo(bucketName, objectName); return { content: [ { type: 'text', text: `对象信息:\n- 名称: ${info.name}\n- 大小: ${info.size} 字节\n- 最后修改: ${info.lastModified.toISOString()}\n- ETag: ${info.etag}\n- 内容类型: ${info.contentType || '未知'}` } ] }; } case 'generate_presigned_url': { const { bucketName, objectName, method, expires } = z.object({ bucketName: z.string(), objectName: z.string(), method: z.enum(['GET', 'PUT', 'DELETE']).default('GET'), expires: z.number().default(3600) }).parse(args); const url = await this.minioClient.generatePresignedUrl(bucketName, objectName, method, { expires }); return { content: [ { type: 'text', text: `预签名URL (${method}, 有效期${expires}秒):\n${url}` } ] }; } case 'get_storage_stats': { const stats = await this.minioClient.getStorageStats(); return { content: [ { type: 'text', text: `存储统计信息:\n- 总存储桶数: ${stats.totalBuckets}\n- 总对象数: ${stats.totalObjects}\n- 总大小: ${(stats.totalSize / 1024 / 1024).toFixed(2)} MB\n\n各存储桶详情:\n${stats.bucketStats.map(b => `- ${b.bucketName}: ${b.objectCount} 个对象, ${(b.totalSize / 1024 / 1024).toFixed(2)} MB`).join('\n')}` } ] }; } case 'upload_files': { const { bucketName, files } = z.object({ bucketName: z.string(), files: z.array(z.object({ localPath: z.string(), objectName: z.string(), metadata: z.record(z.string()).optional() })) }).parse(args); const result = await this.minioClient.uploadFiles(bucketName, files); return { content: [ { type: 'text', text: `批量上传完成: 成功 ${result.successCount} 个, 失败 ${result.failureCount} 个${result.errors.length > 0 ? '\n错误:\n' + result.errors.map(e => `- ${e.item}: ${e.error}`).join('\n') : ''}` } ] }; } case 'download_files': { const { bucketName, files } = z.object({ bucketName: z.string(), files: z.array(z.object({ objectName: z.string(), localPath: z.string() })) }).parse(args); const result = await this.minioClient.downloadFiles(bucketName, files); return { content: [ { type: 'text', text: `批量下载完成: 成功 ${result.successCount} 个, 失败 ${result.failureCount} 个${result.errors.length > 0 ? '\n错误:\n' + result.errors.map(e => `- ${e.item}: ${e.error}`).join('\n') : ''}` } ] }; } case 'set_bucket_policy': { const { bucketName, policy } = z.object({ bucketName: z.string(), policy: z.string() }).parse(args); await this.minioClient.setBucketPolicy(bucketName, policy); return { content: [ { type: 'text', text: `成功设置存储桶 ${bucketName} 的策略` } ] }; } case 'get_bucket_policy': { const { bucketName } = z.object({ bucketName: z.string() }).parse(args); const policy = await this.minioClient.getBucketPolicy(bucketName); return { content: [ { type: 'text', text: `存储桶 ${bucketName} 的策略:\n${policy}` } ] }; } case 'delete_bucket_policy': { const { bucketName } = z.object({ bucketName: z.string() }).parse(args); await this.minioClient.deleteBucketPolicy(bucketName); return { content: [ { type: 'text', text: `成功删除存储桶 ${bucketName} 的策略` } ] }; } default: throw new McpError(ErrorCode.MethodNotFound, `未知工具: ${name}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new McpError(ErrorCode.InternalError, `执行工具 ${name} 时出错: ${errorMessage}`); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); // 如果提供了连接参数,自动连接 if (this.autoConnectConfig.endPoint && this.autoConnectConfig.accessKey && this.autoConnectConfig.secretKey) { try { const config = MinIOConfigSchema.parse(this.autoConnectConfig); await this.minioClient.connect(config); console.error(`MinIO MCP服务器已启动并连接到 ${config.endPoint}:${config.port || 9000}`); } catch (error) { console.error('自动连接MinIO失败:', error instanceof Error ? error.message : String(error)); console.error('MinIO MCP服务器已启动(未连接)'); } } else { console.error('MinIO MCP服务器已启动(未连接)'); } } } const server = new MinIOMCPServer(); server.run().catch(console.error);

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/pickstar-2002/minio-storage-mcp'

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