From ebb0ae9163aa49b3562bc609d9d9f6bf772a18bf Mon Sep 17 00:00:00 2001 From: xieyao <1297754537@qq.com> Date: Fri, 10 Jul 2026 18:46:10 +0800 Subject: [PATCH] update --- mcp-server/package.json | 10 +++ mcp-server/server.js | 167 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 mcp-server/package.json create mode 100644 mcp-server/server.js diff --git a/mcp-server/package.json b/mcp-server/package.json new file mode 100644 index 0000000..4b2fb11 --- /dev/null +++ b/mcp-server/package.json @@ -0,0 +1,10 @@ +{ + "name": "vision-tool-mcp", + "version": "1.0.0", + "description": "MCP server for vision-tool — image analysis via Agnes AI", + "type": "module", + "main": "server.js", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.0" + } +} diff --git a/mcp-server/server.js b/mcp-server/server.js new file mode 100644 index 0000000..d0cf456 --- /dev/null +++ b/mcp-server/server.js @@ -0,0 +1,167 @@ +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 { readFileSync } from "node:fs"; +import { basename } from "node:path"; + +// ============================================================================ +// Configuration (mirrors config.go defaults) +// ============================================================================ + +const DEFAULT_API_KEY = "sk-68FL9xXRrJhxcY5TCGxoaFlQ94oCBioIJhyGfeDMCkCvA0SV"; +const DEFAULT_MODEL = "agnes-2.0-flash"; +const DEFAULT_BASE_URL = "https://apihub.agnes-ai.com/v1/chat/completions"; + +const API_KEY = process.env.AGNES_API_KEY || DEFAULT_API_KEY; +const MODEL = process.env.VISION_MODEL || DEFAULT_MODEL; +const BASE_URL = process.env.VISION_BASE_URL || DEFAULT_BASE_URL; + +// ============================================================================ +// MIME type detection +// ============================================================================ + +function detectMimeType(path) { + const ext = path.split(".").pop().toLowerCase(); + const map = { + png: "image/png", + jpg: "image/jpeg", + jpeg: "image/jpeg", + gif: "image/gif", + webp: "image/webp", + bmp: "image/bmp", + tiff: "image/tiff", + tif: "image/tiff", + }; + return map[ext] || "image/png"; +} + +// ============================================================================ +// Vision API client +// ============================================================================ + +async function analyzeImage(imagePath, prompt) { + const imgData = readFileSync(imagePath); + const mimeType = detectMimeType(imagePath); + const base64Img = Buffer.from(imgData).toString("base64"); + const dataURL = `data:${mimeType};base64,${base64Img}`; + + if (!prompt) { + prompt = "请详细描述这张图片的内容。如果图片中有文字,请完整识别出来。请用中文回答。"; + } + + const body = { + model: MODEL, + messages: [ + { + role: "user", + content: [ + { type: "image_url", image_url: { url: dataURL } }, + { type: "text", text: prompt }, + ], + }, + ], + chat_template_kwargs: { enable_thinking: true }, + }; + + const resp = await fetch(BASE_URL, { + method: "POST", + headers: { + "Authorization": `Bearer ${API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + signal: AbortSignal.timeout(120_000), + }); + + if (!resp.ok) { + const text = await resp.text(); + throw new Error(`API error (HTTP ${resp.status}): ${text}`); + } + + const data = await resp.json(); + if (data.error) { + throw new Error(`API error: [${data.error.code}] ${data.error.message}`); + } + + const content = data.choices?.[0]?.message?.content; + if (!content) { + throw new Error("No content in API response"); + } + + return content; +} + +// ============================================================================ +// MCP Server +// ============================================================================ + +const server = new Server( + { + name: "vision-tool", + version: "1.0.0", + }, + { + capabilities: { + tools: {}, + }, + } +); + +// Register tool list +server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: "analyze_image", + description: + "分析图片内容。使用 Agnes AI 视觉模型识别图片中的对象、场景、文字等。" + + "支持 PNG、JPG、GIF、WebP、BMP、TIFF 格式。可以用自定义 prompt 指定分析重点。", + inputSchema: { + type: "object", + properties: { + image_path: { + type: "string", + description: "图片文件的绝对路径(例如 C:\\Users\\xxx\\photo.png)", + }, + prompt: { + type: "string", + description: + "自定义分析提示词。默认为详细描述图片内容并识别所有文字。例如:" + + '"这张图里有什么文字?" 或 "描述图片中的UI界面布局"。', + }, + }, + required: ["image_path"], + }, + }, + ], +})); + +// Register tool handler +server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + if (name !== "analyze_image") { + throw new Error(`Unknown tool: ${name}`); + } + + const imagePath = args.image_path; + const prompt = args.prompt || ""; + + try { + const result = await analyzeImage(imagePath, prompt); + return { + content: [{ type: "text", text: result }], + }; + } catch (err) { + return { + content: [{ type: "text", text: `Error: ${err.message}` }], + isError: true, + }; + } +}); + +// Start +const transport = new StdioServerTransport(); +await server.connect(transport);