#!/usr/bin/env node // 引入必要的模块 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import http from "http"; import { z } from "zod"; import { ApifoxService } from './apifox.js' // 创建 MCP 服务器实例 // 为其设置一个名字和版本 const server = new McpServer( { name: "apifox-mcp-server", version: "1.0.0", }, { capabilities: { prompts: {}, tools: {}, }, } ); const apifoxService = new ApifoxService({ projectId: Number(process.env.APIFOX_PROJECT_ID), token: process.env.APIFOX_TOKEN }); server.registerTool( 'get_api_by_path', { description: "根据接口path获取Apifox接口定义数据", inputSchema: z.object({ path: z.string().describe("接口路径"), projectId: z.number().optional().describe("Apifox项目ID"), }), }, async ({ path, projectId }) => { try { const apiData = await apifoxService.getApiDetailByPath(path, projectId); return { content: [ { type: "text", text: JSON.stringify(apiData, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `获取接口详情失败: ${error.message}`, }, ], isError: true, }; } } ) server.registerTool( 'get_apis_by_folder', { description: "根据接口目录获取Apifox目录下所有接口定义数据", inputSchema: z.object({ folderName: z.string().describe("接口路径"), projectId: z.number().optional().describe("Apifox项目ID"), }), }, async ({ folderName, projectId }) => { try { const apiData = await apifoxService.getApiListByFolderName(folderName, projectId); return { content: [ { type: "text", text: JSON.stringify(apiData, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `获取接口数据失败: ${error.message}`, }, ], isError: true, }; } } ) // 定义 prompts server.registerPrompt( 'gen-api-by-path', { title: 'Generate API by Path', description: '根据输入的接口路径获取Apifox接口文档定义,然后根据定义生成 api 接口代码定义', }, () => ({ messages: [ { role: "user", content: { type: "text", text: `请根据以下步骤获取Apifox的接口定义并生成 api 定义代码。 步骤: 1. 使用 get_api_by_path 工具获取对应的接口定义schema 2. 根据返回的 path,method, requestSchema, responseSchema 和其他必要字段,生成 api 定义代码 3. 接口的参数和返回值的类型定义,可以分别参考 requestSchema 和 responseSchema 里面的jsonSchema定义,可以根据description要补充相关的注释 4. 所有字段先定义为可空类型 5. 请根据当前项目的项目接口和代码风格生成 api 接口代码 请开始执行这些步骤。`, }, }, ], }) ); server.registerPrompt( 'gen-api-by-folder', { title: 'Generate API by Folder', description: '根据输入的接口文件夹获取Apifox相关文件夹下所有的接口文档定义,然后根据定义生成 api 接口代码定义', }, () => ({ messages: [ { role: "user", content: { type: "text", text: `请根据以下步骤获取Apifox的接口定义并生成 api 定义代码。 步骤: 1. 使用 get_api_by_folder 工具获取对应的接口定义schema,可能会返回多条接口的数据 2. 根据返回的 path,method, requestSchema, responseSchema 和其他必要字段,生成 api 定义代码 3. 接口的参数和返回值的类型定义,可以分别参考 requestSchema 和 responseSchema 里面的jsonSchema定义,可以根据description要补充相关的注释 4. 所有字段先定义为可空类型 5. 请根据当前项目的项目接口和代码风格生成 api 接口代码 请开始执行这些步骤。`, }, }, ], }) ); // 启动服务器,使用标准输入/输出(stdio)与客户端通信 // 启动服务器,使用标准输入/输出(stdio)与客户端通信 async function main() { const args = process.argv.slice(2); const transportArgIndex = args.indexOf('--transport') !== -1 ? args.indexOf('--transport') : args.indexOf('-t'); const transportType = transportArgIndex !== -1 ? args[transportArgIndex + 1] : 'stdio'; const portArgIndex = args.indexOf('--port') !== -1 ? args.indexOf('--port') : args.indexOf('-p'); const port = portArgIndex !== -1 ? parseInt(args[portArgIndex + 1]) : 8889; if (transportType === 'http') { const transport = new StreamableHTTPServerTransport({}); await server.connect(transport); const httpServer = http.createServer(async (req, res) => { // 设置 CORS 头 res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, mcp-session-id'); if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; } if (req.method === 'GET' && req.url === '/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok' })); return; } try { await transport.handleRequest(req, res); } catch (error) { console.error("Error handling request:", error); if (!res.headersSent) { res.writeHead(500); res.end("Internal Server Error"); } } }); httpServer.listen(port, () => { console.error(`Apifox MCP 服务器已通过 http 启动,监听端口 ${port}`); console.error(`MCP 服务 endpoint: http://localhost:${port}/sse`); }); } else { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Apifox MCP 服务器已通过 stdio 启动"); // 日志输出到 stderr } } // 运行主函数,并捕获可能的错误 main().catch((error) => { console.error("服务器运行出现致命错误:", error); process.exit(1); }); export { server };