import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { basename } from "path";
import type { XiaomiNoteClient } from "../client";
interface UploadImageArgs {
path: string;
mimeType?: string;
filename?: string;
}
export function registerUploadImageTool(server: McpServer, client: XiaomiNoteClient): void {
server.registerTool(
"upload_image",
{
description: "上传图片并返回 minote://image/{fileId} 链接",
inputSchema: {
path: z.string().min(1, "文件路径不能为空"),
mimeType: z.string().optional().describe("图片 MIME 类型,自动推断"),
filename: z.string().optional().describe("上传时使用的文件名"),
},
},
async ({ path, mimeType, filename }: UploadImageArgs) => {
const file = Bun.file(path);
const exists = await file.exists();
if (!exists) {
throw new Error(`文件不存在:${path}`);
}
const buffer = await file.arrayBuffer();
const resolvedFilename = filename ?? basename(path);
const resolvedMimeType = mimeType ?? inferMimeType(resolvedFilename);
const result = await client.uploadImageFromBuffer(buffer, {
filename: resolvedFilename,
mimeType: resolvedMimeType,
});
const { fileId, digest } = result.data;
return {
content: [
{
type: "text" as const,
text: `上传成功:fileId=${fileId} digest=${digest}`,
},
{
type: "text" as const,
text: `Markdown:`,
},
],
};
},
);
}
function inferMimeType(filename: string): string {
const lower = filename.toLowerCase();
if (lower.endsWith(".png")) return "image/png";
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
if (lower.endsWith(".gif")) return "image/gif";
if (lower.endsWith(".webp")) return "image/webp";
if (lower.endsWith(".bmp")) return "image/bmp";
if (lower.endsWith(".svg")) return "image/svg+xml";
return "application/octet-stream";
}