From df6df16885d365fd17da4efa6c5837a260a0ac89 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 27 Oct 2025 02:01:13 +0800 Subject: [PATCH] tambahan --- src/server/routes/mcp_route.ts | 441 +++++++++++++++++---------------- 1 file changed, 228 insertions(+), 213 deletions(-) diff --git a/src/server/routes/mcp_route.ts b/src/server/routes/mcp_route.ts index fbc8534..ddcbf99 100644 --- a/src/server/routes/mcp_route.ts +++ b/src/server/routes/mcp_route.ts @@ -2,227 +2,242 @@ import { Elysia, t } 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"); - }, - }); + 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"], + prefix: "/mcp", + tags: ["mcp"], }) - /** - * ✅ 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({ + .get("/", ({ set }) => { + set.headers["Content-Type"] = "application/json; charset=utf-8"; + return { jsonrpc: "2.0", - id, + id: 1, 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"], - }, + capabilities: { + "tools/list": true, + "tools/call": true, }, - ], - }) + "\n" - ); - return; + 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); + }, + { + 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()])), + }), } - - 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); - }, - { - 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()])), - }), - } - ); + ); export default MCPRoute;