From 1ca95cf71b90710d99ab9ba46f18d3bd68adcb9b Mon Sep 17 00:00:00 2001 From: bipproduction Date: Sun, 26 Oct 2025 22:01:41 +0800 Subject: [PATCH] tambahan --- src/server/routes/mcp_route.ts | 341 ++++++++++++++++++++++++--------- 1 file changed, 250 insertions(+), 91 deletions(-) diff --git a/src/server/routes/mcp_route.ts b/src/server/routes/mcp_route.ts index 18061fb..709f48d 100644 --- a/src/server/routes/mcp_route.ts +++ b/src/server/routes/mcp_route.ts @@ -1,103 +1,262 @@ -// src/routes/mcp_route.ts -import { Elysia } from "elysia"; +import { Elysia, t } from "elysia"; +import { cors } from '@elysiajs/cors'; export const MCPRoute = new Elysia({ - prefix: "/mcp-server", + prefix: "/mcp-server", + tags: ["mcp-server"], }) - .get("/mcp", ({ set, query }) => { - // n8n akan mengirim method & params lewat query seperti: - // ?method=tools/list atau ?method=tools/sayHello&name=Malik +.use(cors()) - const id = query.id ?? 1; - const method = query.method as string; - const params = query; +// ✅ Health check endpoint +.get("/", () => ({ + status: "ok", + message: "MCP Server is running", + protocol: "2024-11-05", + transport: "sse", + endpoints: { + sse: "/mcp-server/sse", + message: "/mcp-server/message" + } +})) - // ✅ Header wajib agar n8n mau streaming - set.headers["Content-Type"] = "application/json; charset=utf-8"; - set.headers["Cache-Control"] = "no-cache, no-transform"; - set.headers["Connection"] = "keep-alive"; - set.headers["Transfer-Encoding"] = "chunked"; - set.headers["X-Accel-Buffering"] = "no"; // Untuk Cloudflare & Nginx +// ✅ SSE endpoint - Server-Sent Events untuk n8n +.get("/sse", ({ set }) => { + set.headers = { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache, no-transform", + "Connection": "keep-alive", + "X-Accel-Buffering": "no" + }; - const stream = new ReadableStream({ - async start(controller) { - // --- Handle mcp/version --- - if (method === "mcp/version") { - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: { - protocol: "2024-11-05", - capabilities: { - "tools/list": true, - "tools/call": true, - }, - }, - }) + "\n" - ); - return; // Tidak ditutup → tetap open - } + const encoder = new TextEncoder(); + let isClosed = false; + + const stream = new ReadableStream({ + start(controller) { + // Send endpoint info immediately + const endpointMsg = { + type: "endpoint", + endpoint: "/mcp-server/message" + }; + + try { + controller.enqueue( + encoder.encode(`data: ${JSON.stringify(endpointMsg)}\n\n`) + ); + } catch (e) { + console.error("Failed to send endpoint message:", e); + } + + // Keep connection alive with heartbeat + const heartbeat = setInterval(() => { + if (isClosed) { + clearInterval(heartbeat); + return; + } + + try { + controller.enqueue(encoder.encode(`: heartbeat\n\n`)); + } catch (e) { + console.error("Heartbeat failed:", e); + clearInterval(heartbeat); + isClosed = true; + try { + controller.close(); + } catch (closeErr) { + // Already closed + } + } + }, 15000); // Every 15 seconds + + // Cleanup on connection close + return () => { + isClosed = true; + clearInterval(heartbeat); + }; + } + }); + + return new Response(stream); +}) - // --- Handle tools/list --- - if (method === "tools/list") { - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: [ - { - name: "sayHello", - description: "Greets a user", - inputSchema: { - type: "object", - properties: { - name: { type: "string", description: "Your name" }, - }, - }, - }, - ], - }) + "\n" - ); - return; - } +// ✅ Message endpoint - untuk JSON-RPC messages +.post("/message", async ({ body, set }) => { + set.headers["Content-Type"] = "application/json"; + + const { jsonrpc, id, method, params } = body as any; + + try { + // Initialize + if (method === "initialize") { + return { + jsonrpc: "2.0", + id, + result: { + protocolVersion: "2024-11-05", + capabilities: { + tools: {} + }, + serverInfo: { + name: "tentang-darmasaba-mcp", + version: "1.0.0" + } + } + }; + } - // --- Handle tools/sayHello (progress streaming) --- - if (method === "tools/sayHello") { - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: { status: "Processing..." }, - }) + "\n" - ); - await Bun.sleep(500); + // Ping + if (method === "ping") { + return { + jsonrpc: "2.0", + id, + result: {} + }; + } - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: { message: `Hello ${params?.name || "User"}` }, - }) + "\n" - ); - return; - } - - // --- Jika method tidak dikenal --- - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - error: { - code: -32601, - message: `Method "${method}" not found`, - }, - }) + "\n" - ); + // List tools + if (method === "tools/list") { + return { + jsonrpc: "2.0", + id, + result: { + tools: [ + { + name: "sayHello", + description: "Greets user with a personalized message", + inputSchema: { + type: "object", + properties: { + name: { + type: "string", + description: "Name of the person to greet" + } + }, + required: ["name"] + } }, - }); + { + name: "getCurrentTime", + description: "Returns current server time", + inputSchema: { + type: "object", + properties: {} + } + }, + { + name: "getTentangDarmasaba", + description: "Get information about Tentang Darmasaba platform", + inputSchema: { + type: "object", + properties: { + section: { + type: "string", + description: "Specific section to get info about", + enum: ["about", "features", "contact"] + } + } + } + } + ] + } + }; + } - return new Response(stream); - }); + // Call tool + if (method === "tools/call") { + const toolName = params?.name; + const args = params?.arguments || {}; -export default MCPRoute; + if (toolName === "sayHello") { + return { + jsonrpc: "2.0", + id, + result: { + content: [ + { + type: "text", + text: `Hello ${args.name || "there"}! 👋 Welcome to Tentang Darmasaba MCP Server!` + } + ] + } + }; + } + + if (toolName === "getCurrentTime") { + return { + jsonrpc: "2.0", + id, + result: { + content: [ + { + type: "text", + text: `Current server time: ${new Date().toISOString()}` + } + ] + } + }; + } + + if (toolName === "getTentangDarmasaba") { + const section = args.section || "about"; + const info: Record = { + about: "Tentang Darmasaba adalah platform pembelajaran dan kolaborasi.", + features: "Fitur: MCP Server integration, Real-time collaboration, API tools", + contact: "Contact: support@tentangdarmasaba.com" + }; + + return { + jsonrpc: "2.0", + id, + result: { + content: [ + { + type: "text", + text: info[section] || info.about + } + ] + } + }; + } + + // Tool not found + return { + jsonrpc: "2.0", + id, + error: { + code: -32602, + message: `Unknown tool: ${toolName}` + } + }; + } + + // Method not found + return { + jsonrpc: "2.0", + id, + error: { + code: -32601, + message: `Unknown method: ${method}` + } + }; + + } catch (error: any) { + console.error("MCP Error:", error); + return { + jsonrpc: "2.0", + id, + error: { + code: -32603, + message: `Internal error: ${error.message}` + } + }; + } +}, { + body: t.Object({ + jsonrpc: t.String(), + method: t.String(), + params: t.Optional(t.Any()), + id: t.Union([t.String(), t.Number()]), + }) +}); + +export default MCPRoute; \ No newline at end of file