UNPKG

alioss-mcp

Version:

为AI编码工具提供阿里云OSS访问能力的Model Context Protocol服务器

680 lines 27.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Logger = void 0; const alioss_js_1 = require("./servers/alioss.js"); const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const zod_1 = require("zod"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const express_1 = __importDefault(require("express")); const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js"); const mime_1 = __importDefault(require("mime")); const multipart_js_1 = require("./servers/multipart.js"); exports.Logger = { log: (...args) => { console.log(...args); }, error: (...args) => { console.error(...args); }, }; class AliOssMcpServer { accessKeyId; accessKeySecret; bucket; region; workingDirectory; aliOssServer; multipartManager; server; sseTransport = null; constructor(accessKeyId, accessKeySecret, bucket, region, workingDirectory) { this.accessKeyId = accessKeyId; this.accessKeySecret = accessKeySecret; this.bucket = bucket; this.region = region; this.workingDirectory = workingDirectory; this.accessKeyId = accessKeyId; this.accessKeySecret = accessKeySecret; this.bucket = bucket; this.workingDirectory = workingDirectory; this.aliOssServer = new alioss_js_1.AliOssServer(accessKeyId, accessKeySecret, bucket, region); this.multipartManager = new multipart_js_1.MultipartUploadManager(this.aliOssServer); this.server = new mcp_js_1.McpServer({ name: "alioss", version: "1.0.0", }); this.registerTools(); this.registerResources(); } registerTools() { this.server.tool("uploadFile", "阿里云OSS上传文件", { fileName: zod_1.z.string().describe("指定文件名"), file: zod_1.z.string().describe("指定文件路径"), }, async ({ fileName, file }) => { try { const filePath = path_1.default.join(this.workingDirectory, file); console.log("filePath", filePath); const result = await this.aliOssServer.uploadFile(fileName, filePath); console.log("result", result); return { content: [ { type: "text", text: result, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "上传失败" }] }; } }); this.server.tool("downloadFile", "阿里云OSS下载文件", { fileName: zod_1.z.string().describe("指定要下载的OSS文件名"), localPath: zod_1.z.string().describe("本地保存路径"), }, async ({ fileName, localPath }) => { try { const content = await this.aliOssServer.downloadFile(fileName); if (!content) { return { content: [ { type: "text", text: "下载失败,文件不存在或无法访问" }, ], isError: true, }; } const savePath = path_1.default.join(this.workingDirectory, localPath); fs_1.default.writeFileSync(savePath, content); return { content: [ { type: "text", text: `文件已下载到 ${savePath}`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "下载失败" }] }; } }); this.server.tool("listObjects", "阿里云OSS列出文件", { prefix: zod_1.z.string().optional().describe("文件前缀,可选"), maxKeys: zod_1.z.number().optional().describe("最大返回数量,可选,默认100"), }, async ({ prefix, maxKeys }) => { try { const result = await this.aliOssServer.listObjects(prefix || "", maxKeys || 100); if (!result) { return { content: [{ type: "text", text: "列出文件失败" }], isError: true, }; } return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "列出文件失败" }] }; } }); this.server.tool("deleteObject", "阿里云OSS删除文件", { fileName: zod_1.z.string().describe("指定要删除的OSS文件名"), }, async ({ fileName }) => { try { const result = await this.aliOssServer.deleteObject(fileName); if (!result) { return { content: [{ type: "text", text: "删除失败" }], isError: true, }; } return { content: [ { type: "text", text: `文件 ${fileName} 已成功删除`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "删除失败" }] }; } }); this.server.tool("getObjectUrl", "获取阿里云OSS文件的签名URL", { fileName: zod_1.z.string().describe("指定OSS文件名"), expires: zod_1.z.number().optional().describe("URL有效期(秒),默认3600"), }, async ({ fileName, expires }) => { try { const result = await this.aliOssServer.getObjectUrl(fileName, expires || 3600); return { content: [ { type: "text", text: result, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "获取URL失败" }] }; } }); this.server.tool("copyObject", "阿里云OSS复制文件", { source: zod_1.z.string().describe("源文件路径"), target: zod_1.z.string().describe("目标文件路径"), }, async ({ source, target }) => { try { const result = await this.aliOssServer.copyObject(source, target); if (!result) { return { content: [{ type: "text", text: "复制失败" }], isError: true, }; } return { content: [ { type: "text", text: `文件已从 ${source} 复制到 ${target}`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "复制失败" }] }; } }); this.server.tool("deleteMultipleObjects", "批量删除阿里云OSS文件", { fileNames: zod_1.z.array(zod_1.z.string()).describe("要删除的OSS文件名列表"), }, async ({ fileNames }) => { try { const result = await this.aliOssServer.deleteMultipleObjects(fileNames); if (!result) { return { content: [{ type: "text", text: "批量删除失败" }], isError: true, }; } return { content: [ { type: "text", text: `已成功删除 ${result.deleted?.length || 0} 个文件`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "批量删除失败" }] }; } }); this.server.tool("putObjectACL", "设置阿里云OSS文件的访问权限", { fileName: zod_1.z.string().describe("指定OSS文件名"), acl: zod_1.z .enum(["public-read", "public-read-write", "private"]) .describe("访问权限设置"), }, async ({ fileName, acl }) => { try { const result = await this.aliOssServer.putObjectACL(fileName, acl); if (!result) { return { content: [{ type: "text", text: "设置访问权限失败" }], isError: true, }; } return { content: [ { type: "text", text: `文件 ${fileName} 的访问权限已设置为 ${acl}`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "设置访问权限失败" }] }; } }); this.server.tool("getObjectACL", "获取阿里云OSS文件的访问权限", { fileName: zod_1.z.string().describe("指定OSS文件名"), }, async ({ fileName }) => { try { const result = await this.aliOssServer.getObjectACL(fileName); if (!result) { return { content: [{ type: "text", text: "获取访问权限失败" }], isError: true, }; } return { content: [ { type: "text", text: `文件 ${fileName} 的访问权限为: ${result.acl}`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "获取访问权限失败" }] }; } }); this.server.tool("getObjectMeta", "获取阿里云OSS文件的元数据", { fileName: zod_1.z.string().describe("指定OSS文件名"), }, async ({ fileName }) => { try { const result = await this.aliOssServer.getObjectMeta(fileName); if (!result) { return { content: [{ type: "text", text: "获取元数据失败" }], isError: true, }; } return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "获取元数据失败" }] }; } }); this.server.tool("multipartUpload", "使用分片上传大文件到阿里云OSS", { fileName: zod_1.z.string().describe("指定OSS文件名"), filePath: zod_1.z.string().describe("要上传的本地文件路径"), partSize: zod_1.z.number().optional().describe("分片大小(字节),默认1MB"), }, async ({ fileName, filePath, partSize }) => { try { const localFilePath = path_1.default.join(this.workingDirectory, filePath); if (!fs_1.default.existsSync(localFilePath)) { return { content: [{ type: "text", text: `文件 ${localFilePath} 不存在` }], isError: true, }; } const result = await this.multipartManager.multipartUpload(fileName, localFilePath, partSize || 1024 * 1024); if (!result) { return { content: [{ type: "text", text: "分片上传失败" }], isError: true, }; } return { content: [ { type: "text", text: `文件已上传成功,文件名: ${result.name}`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "分片上传失败" }] }; } }); this.server.tool("initMultipartUpload", "初始化分片上传任务", { fileName: zod_1.z.string().describe("指定OSS文件名"), }, async ({ fileName }) => { try { const result = await this.multipartManager.initMultipartUpload(fileName); if (!result) { return { content: [{ type: "text", text: "初始化分片上传失败" }], isError: true, }; } return { content: [ { type: "text", text: JSON.stringify({ uploadId: result.uploadId, name: result.name, }, null, 2), }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "初始化分片上传失败" }] }; } }); this.server.tool("uploadPart", "上传文件分片", { fileName: zod_1.z.string().describe("指定OSS文件名"), uploadId: zod_1.z.string().describe("分片上传ID"), partNo: zod_1.z.number().describe("分片序号"), filePath: zod_1.z.string().describe("要上传的本地文件路径"), start: zod_1.z.number().describe("分片起始位置(字节)"), end: zod_1.z.number().describe("分片结束位置(字节)"), }, async ({ fileName, uploadId, partNo, filePath, start, end }) => { try { const localFilePath = path_1.default.join(this.workingDirectory, filePath); if (!fs_1.default.existsSync(localFilePath)) { return { content: [{ type: "text", text: `文件 ${localFilePath} 不存在` }], isError: true, }; } const result = await this.multipartManager.uploadPart(fileName, uploadId, partNo, localFilePath, start, end); if (!result) { return { content: [{ type: "text", text: "上传分片失败" }], isError: true, }; } return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "上传分片失败" }] }; } }); this.server.tool("completeMultipartUpload", "完成分片上传", { fileName: zod_1.z.string().describe("指定OSS文件名"), uploadId: zod_1.z.string().describe("分片上传ID"), parts: zod_1.z .array(zod_1.z.object({ number: zod_1.z.number().describe("分片序号"), etag: zod_1.z.string().describe("分片ETag"), })) .describe("已上传的分片信息列表"), }, async ({ fileName, uploadId, parts }) => { try { const result = await this.multipartManager.completeMultipartUpload(fileName, uploadId, parts); if (!result) { return { content: [{ type: "text", text: "完成分片上传失败" }], isError: true, }; } return { content: [ { type: "text", text: `分片上传完成,文件名: ${result.name}`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "完成分片上传失败" }] }; } }); this.server.tool("abortMultipartUpload", "取消分片上传", { fileName: zod_1.z.string().describe("指定OSS文件名"), uploadId: zod_1.z.string().describe("分片上传ID"), }, async ({ fileName, uploadId }) => { try { const result = await this.multipartManager.abortMultipartUpload(fileName, uploadId); if (!result) { return { content: [{ type: "text", text: "取消分片上传失败" }], isError: true, }; } return { content: [ { type: "text", text: `已成功取消分片上传任务`, }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "取消分片上传失败" }] }; } }); this.server.tool("listUploads", "列出未完成的分片上传任务", { prefix: zod_1.z.string().optional().describe("文件名前缀,可选"), maxUploads: zod_1.z.number().optional().describe("最大返回数量,可选"), }, async ({ prefix, maxUploads }) => { try { const result = await this.multipartManager.listUploads(prefix, maxUploads); if (!result) { return { content: [{ type: "text", text: "获取未完成上传列表失败" }], isError: true, }; } return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { if (error instanceof Error) { this.server.server.sendLoggingMessage({ level: "error", data: error.message, }); return { content: [{ type: "text", text: error.message }], isError: true, }; } return { content: [{ type: "text", text: "获取未完成上传列表失败" }], }; } }); } registerResources() { const files = fs_1.default.readdirSync(this.workingDirectory); files.forEach((file) => { this.server.resource(file, path_1.default.join(this.workingDirectory, file), async (uri) => { const mimeType = mime_1.default.getType(file); const fileContent = fs_1.default.readFileSync(path_1.default.join(uri.toString())); return { contents: [ { uri: uri.toString(), text: fileContent.toString("base64"), mimeType: mimeType || "application/octet-stream", }, ], }; }); }); } async start() { const transport = new stdio_js_1.StdioServerTransport(); this.server.connect(transport); } startHttpServer(port) { const app = (0, express_1.default)(); app.get("/sse", async (req, res) => { console.log("New SSE connection established"); this.sseTransport = new sse_js_1.SSEServerTransport("/messages", res); await this.server.connect(this.sseTransport); }); app.post("/messages", async (req, res) => { if (!this.sseTransport) { res.status(400).send("No SSE connection established"); return; } await this.sseTransport.handlePostMessage(req, res); }); app.listen(port, () => { console.log(`HTTP server listening on port ${port}`); }); } } exports.default = AliOssMcpServer; //# sourceMappingURL=server.js.map