UNPKG

structurize-mcp

Version:

Anthropic MCP Server for generating structured CSV files from natural language descriptions

241 lines (214 loc) 6.49 kB
// 使用最新的 MCP SDK 导入方式 import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import { generateCSV } from './csvGenerator'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import express from 'express'; import fileUpload from 'express-fileupload'; import fs from 'fs-extra'; import path from 'path'; import net from 'net'; // 检查端口是否可用 function isPortAvailable(port: number): Promise<boolean> { return new Promise((resolve) => { const server = net.createServer() .once('error', () => { resolve(false); }) .once('listening', () => { server.close(); resolve(true); }) .listen(port); }); } // 查找可用端口 async function findAvailablePort(startPort: number): Promise<number> { let port = startPort; let attempts = 0; const maxAttempts = 50; // 最多尝试50个端口 while (!(await isPortAvailable(port))) { port++; attempts++; if (attempts >= maxAttempts) { console.warn(`警告: 尝试了${maxAttempts}个端口后仍未找到可用端口,将使用随机端口`); return 0; // 使用随机端口 } } return port; } // 解析命令行参数 const argv = yargs(hideBin(process.argv)) .option('gemini-api-key', { alias: 'k', type: 'string', description: 'Gemini API Key', demandOption: false }) .option('csv-dir', { alias: 'd', type: 'string', description: 'CSV 文件保存目录', demandOption: false }) .option('port', { alias: 'p', type: 'number', description: 'HTTP 服务器端口', default: 3000 }) .option('auto-port', { alias: 'a', type: 'boolean', description: '自动查找可用端口', default: true }) .help() .alias('help', 'h') .parseSync(); // 提取命令行参数 const apiKey = argv['gemini-api-key']; const csvDir = argv['csv-dir']; let port = argv['port']; const autoPort = argv['auto-port']; // 记录启动参数 console.log('启动参数:'); console.log(`- CSV 保存目录: ${csvDir || '默认目录'}`); console.log(`- Gemini API Key: ${apiKey ? '已提供' : '未提供'}`); console.log(`- HTTP 服务器端口: ${port}`); console.log(`- 自动查找端口: ${autoPort ? '启用' : '禁用'}`); // 启动 HTTP 服务器 const app = express(); app.use(express.json()); app.use(fileUpload({ limits: { fileSize: 5 * 1024 * 1024 }, // 限制文件大小为 5MB })); // 处理 CSV 生成请求 app.post('/generate-csv', async (req, res) => { try { const { description } = req.body; if (!description) { return res.status(400).json({ error: '缺少描述文本' }); } if (!apiKey) { return res.status(400).json({ error: '未提供 Gemini API Key' }); } // 在这里调用 CSV 生成逻辑 // 省略具体实现,因为这里只是添加路由 return res.json({ message: 'CSV 生成功能尚未实现' }); } catch (error) { console.error('CSV 生成错误:', error); return res.status(500).json({ error: error instanceof Error ? error.message : 'CSV 生成失败' }); } }); // 添加健康检查端点 app.get('/health', (req, res) => { res.json({ status: 'ok', version: '1.1.0' }); }); // 启动 HTTP 服务器 async function startHttpServer() { try { // 如果启用自动端口,则查找可用端口 if (autoPort) { const availablePort = await findAvailablePort(port); if (availablePort !== port) { console.log(`端口 ${port} 已被占用,将使用端口 ${availablePort}`); port = availablePort; } } return new Promise<void>((resolve, reject) => { // 创建服务器 const server = app.listen(port, () => { console.log(`HTTP 服务器已启动,监听端口 ${port}`); resolve(); }); // 处理启动错误 server.on('error', (err: any) => { if (err.code === 'EADDRINUSE' && !autoPort) { console.error(`错误: 端口 ${port} 已被占用`); console.error(`提示: 使用以下命令启用自动端口查找功能:`); console.error(` --auto-port 或 -a`); console.error(`或指定其他端口:`); console.error(` --port <端口号> 或 -p <端口号>`); reject(err); } else { console.error('HTTP 服务器启动错误:', err); reject(err); } }); // 优雅关闭 process.on('SIGINT', () => { console.log('正在关闭 HTTP 服务器...'); server.close(() => { console.log('HTTP 服务器已关闭'); process.exit(0); }); }); }); } catch (error) { console.error('启动 HTTP 服务器失败:', error); throw error; } } // 创建 MCP Server 实例 const mcpServer = new McpServer({ name: 'structurize-mcp', version: '1.1.0', }); // 参数定义 const csvToolParams = { title: z.string(), structure: z.string(), data: z.string(), delimiter: z.string().optional(), }; // 注册生成 CSV 文件的工具 mcpServer.tool( 'generate-csv', csvToolParams, async (params) => { try { const { title, structure, data, delimiter = ',' } = params; // 调用 CSV 生成函数,传递命令行参数 const result = await generateCSV(title, structure, data, delimiter, csvDir, apiKey); return { content: [ { type: 'text', text: `CSV 文件已成功生成!\n文件路径: ${result.filePath}\n行数: ${result.rowCount}\n列数: ${result.columnCount}`, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: 'text', text: `生成 CSV 文件时出错: ${errorMessage}`, }, ], }; } } ); // 主函数 async function main() { try { // 启动 HTTP 服务器 await startHttpServer(); // 启动 MCP Server const transport = new StdioServerTransport(); await mcpServer.connect(transport); console.log('MCP 服务器已启动,等待请求...'); } catch (error) { console.error('服务器启动失败:', error); process.exit(1); } } // 执行主函数 main();