alioss-mcp
Version:
为AI编码工具提供阿里云OSS访问能力的Model Context Protocol服务器
680 lines • 27.8 kB
JavaScript
"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