import { Elysia } from "elysia"; import { getMcpTools } from "../lib/mcp_tool_convert"; var tools = [] as any[]; const OPENAPI_URL = process.env.BUN_PUBLIC_BASE_URL + "/docs/json"; const FILTER_TAG = "mcp"; if (!process.env.BUN_PUBLIC_BASE_URL) { throw new Error("BUN_PUBLIC_BASE_URL environment variable is not set"); } // ===================== // MCP Protocol Types // ===================== type JSONRPCRequest = { jsonrpc: "2.0"; id: string | number; method: string; params?: any; }; type JSONRPCResponse = { jsonrpc: "2.0"; id: string | number; result?: any; error?: { code: number; message: string; data?: any; }; }; // ===================== // Tool Executor // ===================== export async function executeTool( tool: any, args: Record = {}, baseUrl: string ) { const x = tool["x-props"] || {}; const method = (x.method || "GET").toUpperCase(); const path = x.path || `/${tool.name}`; const url = `${baseUrl}${path}`; const opts: RequestInit = { method, headers: { "Content-Type": "application/json" }, }; if (["POST", "PUT", "PATCH", "DELETE"].includes(method)) { opts.body = JSON.stringify(args || {}); } const res = await fetch(url, opts); const contentType = res.headers.get("content-type") || ""; const data = contentType.includes("application/json") ? await res.json() : await res.text(); return { success: res.ok, status: res.status, method, path, data, }; } // ===================== // MCP Handler (Async) // ===================== async function handleMCPRequestAsync( request: JSONRPCRequest ): Promise { const { id, method, params } = request; switch (method) { case "initialize": return { jsonrpc: "2.0", id, result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "elysia-mcp-server", version: "1.0.0" }, }, }; case "tools/list": return { jsonrpc: "2.0", id, result: { tools: tools.map(({ name, description, inputSchema, ["x-props"]: x }) => ({ name, description, inputSchema, "x-props": x, })), }, }; case "tools/call": { const toolName = params?.name; const tool = tools.find((t) => t.name === toolName); if (!tool) { return { jsonrpc: "2.0", id, error: { code: -32601, message: `Tool '${toolName}' not found` }, }; } try { const baseUrl = process.env.BUN_PUBLIC_BASE_URL || "http://localhost:3000"; const result = await executeTool(tool, params?.arguments || {}, baseUrl); const data = result.data.data; const isObject = typeof data === "object" && data !== null; return { jsonrpc: "2.0", id, result: { content: [ isObject ? { type: "json", data: data } : { type: "text", text: JSON.stringify(data || result.data || result) }, ], }, }; } catch (error: any) { return { jsonrpc: "2.0", id, error: { code: -32603, message: error.message }, }; } } case "ping": return { jsonrpc: "2.0", id, result: {} }; default: return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method '${method}' not found` }, }; } } // ===================== // Elysia MCP Server // ===================== export const MCPRoute = new Elysia({ tags: ["MCP Server"] }) .post("/mcp", async ({ request, set }) => { if (!tools.length) { tools = await getMcpTools(OPENAPI_URL, FILTER_TAG); } set.headers["Content-Type"] = "application/json"; set.headers["Access-Control-Allow-Origin"] = "*"; try { const body = await request.json(); if (!Array.isArray(body)) { const res = await handleMCPRequestAsync(body); return res; } const results = await Promise.all( body.map((req) => handleMCPRequestAsync(req)) ); return results; } catch (error: any) { set.status = 400; return { jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error", data: error.message, }, }; } }) // Tools list (debug) .get("/mcp/tools", async ({ set }) => { if (!tools.length) { tools = await getMcpTools(OPENAPI_URL, FILTER_TAG); } set.headers["Access-Control-Allow-Origin"] = "*"; return { tools: tools.map(({ name, description, inputSchema, ["x-props"]: x }) => ({ name, description, inputSchema, "x-props": x, })), }; }) // MCP status .get("/mcp/status", ({ set }) => { set.headers["Access-Control-Allow-Origin"] = "*"; return { status: "active", timestamp: Date.now() }; }) // Health check .get("/health", ({ set }) => { set.headers["Access-Control-Allow-Origin"] = "*"; return { status: "ok", timestamp: Date.now(), tools: tools.length }; }) .get("/mcp/init", async ({ set }) => { const _tools = await getMcpTools(OPENAPI_URL, FILTER_TAG); tools = _tools; return { success: true, message: "MCP initialized", tools: tools.length, }; }) // CORS .options("/mcp", ({ set }) => { set.headers["Access-Control-Allow-Origin"] = "*"; set.headers["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS"; set.headers["Access-Control-Allow-Headers"] = "Content-Type,Authorization,X-API-Key"; set.status = 204; return ""; }) .options("/mcp/tools", ({ set }) => { set.headers["Access-Control-Allow-Origin"] = "*"; set.headers["Access-Control-Allow-Methods"] = "GET,OPTIONS"; set.headers["Access-Control-Allow-Headers"] = "Content-Type,Authorization,X-API-Key"; set.status = 204; return ""; });