From 4bf052b15e0608e9cf608856a5e2f4a20740bf21 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Wed, 8 Oct 2025 19:51:40 +0800 Subject: [PATCH] tamabahan --- src/server/routes/mcp_route.ts | 74 +++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/src/server/routes/mcp_route.ts b/src/server/routes/mcp_route.ts index 6c1ab86..1ce0236 100644 --- a/src/server/routes/mcp_route.ts +++ b/src/server/routes/mcp_route.ts @@ -8,22 +8,18 @@ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js'; const transports: Record = {}; const McpRoute = new Elysia() - // Middleware global untuk JSON parsing & header access - .onRequest(({ set }) => { - set.headers['Content-Type'] = 'application/json'; - }) - // Route utama untuk komunikasi client → server .post( '/mcp', async ({ body, request, set }) => { const sessionId = request.headers.get('mcp-session-id') ?? undefined; let transport: StreamableHTTPServerTransport; - // Jika ada sessionId, pakai transport lama + // Jika ada sessionId lama, gunakan ulang transport-nya if (sessionId && transports[sessionId]) { transport = transports[sessionId]; - } else if (!sessionId && isInitializeRequest(body)) { - // Jika belum ada session & ini request inisialisasi + } + // Jika request inisialisasi baru + else if (!sessionId && isInitializeRequest(body)) { transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (sid) => { @@ -31,22 +27,35 @@ const McpRoute = new Elysia() }, }); - // Cleanup transport jika ditutup transport.onclose = () => { - if (transport.sessionId) { - delete transports[transport.sessionId]; - } + if (transport.sessionId) delete transports[transport.sessionId]; }; - // Inisialisasi MCP server + // Inisialisasi MCP Server const server = new McpServer({ name: 'example-server', version: '1.0.0', }); - // ... di sini bisa ditambahkan tools, prompts, dsb ... + // Tambahkan resource, tools, dsb di sini jika perlu await server.connect(transport); - } else { + + // Tunggu sampai session terbentuk + await new Promise((resolve) => { + const check = () => { + if (transport.sessionId) return resolve(); + setTimeout(check, 5); + }; + check(); + }); + + // Kirim sessionId kembali ke client + set.headers['mcp-session-id'] = transport.sessionId!; + set.status = 200; + return { sessionId: transport.sessionId }; + } + // Jika request invalid + else { set.status = 400; return { jsonrpc: '2.0', @@ -59,36 +68,37 @@ const McpRoute = new Elysia() } // Jalankan handler HTTP MCP - return await transport.handleRequest( - request as any, - // Simulasi `Response` agar Elysia bisa mengembalikan hasil - new Response(null, { status: 200 }) as any, - body - ); + return await transport.handleRequest(request, new Response(), body); }, { - body: t.Any(), // fleksibel untuk JSON-RPC + body: t.Any(), } ) - // Handler reusable untuk GET & DELETE - .derive(({ request, set }) => { + // Handler GET untuk SSE (server → client) + .get('/mcp', async ({ request, set }) => { const sessionId = request.headers.get('mcp-session-id') ?? undefined; const transport = sessionId ? transports[sessionId] : undefined; if (!transport) { set.status = 400; - throw new Error('Invalid or missing session ID'); + return 'Invalid or missing session ID'; } - return { transport }; - }) - // GET untuk server → client via SSE - .get('/mcp', async ({ transport, request }) => { - return await transport.handleRequest(request as any, new Response() as any); + set.status = 200; + return await transport.handleRequest(request, new Response()); }) // DELETE untuk terminasi session - .delete('/mcp', async ({ transport, request }) => { - return await transport.handleRequest(request as any, new Response() as any); + .delete('/mcp', async ({ request, set }) => { + const sessionId = request.headers.get('mcp-session-id') ?? undefined; + const transport = sessionId ? transports[sessionId] : undefined; + + if (!transport) { + set.status = 400; + return 'Invalid or missing session ID'; + } + + set.status = 200; + return await transport.handleRequest(request, new Response()); }) export default McpRoute \ No newline at end of file