From f2570bef0579bb6754fd470fedf3289cc7a2eb34 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 27 Oct 2025 02:06:06 +0800 Subject: [PATCH] tambahan --- src/server/routes/mcp_route.ts | 295 +++++++-------------------------- 1 file changed, 58 insertions(+), 237 deletions(-) diff --git a/src/server/routes/mcp_route.ts b/src/server/routes/mcp_route.ts index ddcbf99..b9823e3 100644 --- a/src/server/routes/mcp_route.ts +++ b/src/server/routes/mcp_route.ts @@ -1,243 +1,64 @@ -// src/routes/mcp_route.ts -import { Elysia, t } from "elysia"; +import { Elysia } from "elysia"; -function createStream(handler: (controller: ReadableStreamDefaultController) => Promise) { - return new ReadableStream({ - async start(controller) { - try { - controller.enqueue(":\n\n"); // Heartbeat awal - await handler(controller); - } catch (error) { - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - error: { code: -32000, message: "Internal Server Error" }, - }) + "\n" - ); - } finally { - try { - controller.close(); - } catch { } - } - }, - cancel() { - console.log("🔴 Client closed the connection"); - }, - }); -} - -export const MCPRoute = new Elysia({ - prefix: "/mcp", - tags: ["mcp"], +const MCPRoute = new Elysia({ + prefix: "/mcp", + tags: ["mcp"], }) - .get("/", ({ set }) => { - set.headers["Content-Type"] = "application/json; charset=utf-8"; - return { - jsonrpc: "2.0", - id: 1, - result: { - protocol: "2024-11-05", - capabilities: { - "tools/list": true, - "tools/call": true, - }, - status: "MCP Server Ready", - }, - }; - }) - /** - * ✅ GET /mcp/:id — streaming response untuk n8n + curl - */ - .get("/:id", ({ params, query, set }) => { - const id = params.id ?? 1; - const method = query.method as string | undefined; - - 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"; - - const stream = createStream(async (controller) => { - // Jika tidak ada method → status server - if (!method) { - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - result: { - status: "MCP Server Ready", - message: "Use ?method=mcp/version or ?method=tools/list", - availableMethods: ["mcp/version", "tools/list", "tools/sayHello"], - }, - }) + "\n" - ); - return; - } - - // 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; - } - - // 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" } }, - required: ["name"], - }, - }, - ], - }) + "\n" - ); - return; - } - - // tools/sayHello (progress + result) - 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 ${query?.name || "User"}` }, - }) + "\n" - ); - return; - } - - // Unknown method - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - error: { code: -32601, message: `Method "${method}" not found` }, - }) + "\n" - ); - }); - - return new Response(stream); - }) - /** - * ✅ POST /mcp — JSON-RPC versi body - */ - .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"; - - const stream = createStream(async (controller) => { - if (!method) { - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - error: { code: -32600, message: "Method required" }, - }) + "\n" - ); - return; - } - - 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" } }, - required: ["name"], - }, - }, - ], - }) + "\n" - ); - return; - } - - 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; - } - - controller.enqueue( - JSON.stringify({ - jsonrpc: "2.0", - id, - error: { code: -32601, message: `Method "${method}" not found` }, - }) + "\n" - ); - }); - - return new Response(stream); - }, + // ✅ n8n Fetch Tools → HARUS format { data: [...] } + .get("/", ({ set }) => { + set.headers["Content-Type"] = "application/json; charset=utf-8"; + return { + data: [ { - body: t.Object({ - jsonrpc: t.Optional(t.String()), - method: t.String(), - params: t.Optional(t.Record(t.String(), t.Any())), - id: t.Optional(t.Union([t.String(), t.Number()])), - }), + name: "sayHello", + value: "sayHello", + description: "Greets a user", + inputSchema: { + type: "object", + properties: { + name: { type: "string", description: "Your name" }, + }, + required: ["name"], + additionalProperties: false, + }, + }, + ], + }; + }) + + // ✅ Tools Execution (Streaming / JSON-RPC Optional) + .get("/call", ({ set, query }) => { + const method = query.method; + const name = query.name || "User"; + + set.headers["Content-Type"] = "application/json; charset=utf-8"; + set.headers["Cache-Control"] = "no-cache"; + set.headers["Connection"] = "keep-alive"; + set.headers["Transfer-Encoding"] = "chunked"; + + const stream = new ReadableStream({ + async start(controller) { + controller.enqueue(":\n\n"); // Heartbeat + + if (method === "sayHello") { + controller.enqueue(JSON.stringify({ status: "processing" }) + "\n"); + await Bun.sleep(500); + + controller.enqueue( + JSON.stringify({ result: `Hello ${name}` }) + "\n" + ); + controller.close(); + return; } - ); + + controller.enqueue( + JSON.stringify({ error: `Method ${method} not found` }) + "\n" + ); + controller.close(); + }, + }); + + return new Response(stream); + }); export default MCPRoute;