From 133e344adeb1136026ec52f489384764d7e3c575 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 27 Oct 2025 01:32:08 +0800 Subject: [PATCH] tambahan --- src/server/routes/mcp_route.ts | 313 +++++++++++++++------------------ 1 file changed, 144 insertions(+), 169 deletions(-) diff --git a/src/server/routes/mcp_route.ts b/src/server/routes/mcp_route.ts index 37a1b01..d4a6f35 100644 --- a/src/server/routes/mcp_route.ts +++ b/src/server/routes/mcp_route.ts @@ -4,187 +4,162 @@ import { Elysia, t } from "elysia"; export const MCPRoute = new Elysia({ prefix: "/mcp", }) - .get("/", ({ set, query }) => { - const id = query.id ?? 1; - const method = query.method as string; - const params = query; - - // Header untuk SSE / Streaming agar diterima oleh n8n - 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"; // Matikan buffering (nginx/cloudflare) - - const stream = new ReadableStream({ - async start(controller) { - // ✅ Heartbeat agar tidak time-out di Cloudflare / n8n - controller.enqueue(":\n\n"); - - // 1. Handshake 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; // ❗ biarkan stream tetap terbuka - } - - // 2. tools/list → Informasi tools ke n8n - 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" }, - }, - required: ["name"], - }, - }, - ], - }) + "\n" - ); - return; - } - - // 3. tools/sayHello → kirim progres lalu hasil - if (method === "tools/sayHello") { - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: { status: "Processing..." }, - }) + "\n" - ); - await Bun.sleep(500); - - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: { message: `Hello ${params?.name || "User"}` }, - }) + "\n" - ); - return; - } - - // 4. Unknown method - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - error: { - code: -32601, - message: `Method "${method}" not found`, - }, - }) + "\n" - ); - }, - cancel() { - console.log("🔴 Stream closed by client"); - }, - }); - - return new Response(stream); - }) - // Optional: juga izinkan MCP pakai POST request JSON-RPC + // POST endpoint untuk MCP protocol (standard) .post("/", async ({ body, set }) => { const { id, method, params } = body as any; - set.headers["Content-Type"] = "application/json; charset=utf-8"; - set.headers["Connection"] = "keep-alive"; - set.headers["Transfer-Encoding"] = "chunked"; - set.headers["X-Accel-Buffering"] = "no"; + set.headers["Content-Type"] = "application/json"; - const stream = new ReadableStream({ - async start(controller) { - controller.enqueue(":\n\n"); + try { + // 1. Initialize - Version handshake + if (method === "initialize") { + return { + jsonrpc: "2.0", + id, + result: { + protocolVersion: "2024-11-05", + capabilities: { + tools: { + listChanged: false + } + }, + serverInfo: { + name: "elysia-mcp-server", + version: "1.0.0" + } + } + }; + } - 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; - } - - 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" }, - }, + // 2. List available tools + if (method === "tools/list") { + return { + jsonrpc: "2.0", + id, + result: { + tools: [ + { + name: "sayHello", + description: "Greets a user by name", + inputSchema: { + type: "object", + properties: { + name: { + type: "string", + description: "Name of the person to greet" + } }, - }, - ], - }) + "\n" - ); - return; + required: ["name"] + } + }, + { + name: "getCurrentTime", + description: "Returns the current server time", + inputSchema: { + type: "object", + properties: {} + } + } + ] + } + }; + } + + // 3. Call tool - Execute the requested tool + if (method === "tools/call") { + const toolName = params?.name; + const toolArgs = params?.arguments || {}; + + if (toolName === "sayHello") { + const userName = toolArgs.name || "User"; + return { + jsonrpc: "2.0", + id, + result: { + content: [ + { + type: "text", + text: `Hello, ${userName}! Welcome to the MCP server.` + } + ] + } + }; } - if (method === "tools/sayHello") { - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: { status: "Processing..." }, - }) + "\n" - ); - await Bun.sleep(500); - - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: { message: `Hello ${params?.name || ""}` }, - }) + "\n" - ); - return; + if (toolName === "getCurrentTime") { + const currentTime = new Date().toISOString(); + return { + jsonrpc: "2.0", + id, + result: { + content: [ + { + type: "text", + text: `Current server time: ${currentTime}` + } + ] + } + }; } - }, - }); - return new Response(stream); + // Tool not found + return { + jsonrpc: "2.0", + id, + error: { + code: -32602, + message: `Tool "${toolName}" not found` + } + }; + } + + // Method not found + return { + jsonrpc: "2.0", + id, + error: { + code: -32601, + message: `Method "${method}" not found` + } + }; + + } catch (error: any) { + return { + jsonrpc: "2.0", + id, + error: { + code: -32603, + message: error.message || "Internal error" + } + }; + } }, { body: t.Object({ - jsonrpc: t.Optional(t.String()), + jsonrpc: t.String(), method: t.String(), - params: t.Optional(t.Record(t.String(), t.Any())), - id: t.Optional(t.Union([t.String(), t.Number()])), - }), + params: t.Optional(t.Any()), + id: t.Union([t.String(), t.Number()]) + }) + }) + + // GET endpoint untuk testing / health check + .get("/", () => { + return { + status: "ok", + message: "MCP Server is running", + endpoints: { + main: "POST /mcp", + methods: ["initialize", "tools/list", "tools/call"] + } + }; + }) + + // Health check endpoint + .get("/health", () => { + return { + status: "healthy", + timestamp: new Date().toISOString() + }; }); -export default MCPRoute; +export default MCPRoute; \ No newline at end of file