diff --git a/src/index.tsx b/src/index.tsx index 3f07205..e8fd47d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -9,7 +9,6 @@ import Auth from "./server/routes/auth_route"; import CredentialRoute from "./server/routes/credential_route"; import DarmasabaRoute from "./server/routes/darmasaba_route"; import { convertOpenApiToMcp } from "./server/lib/mcp-converter"; -import McpRoute from "./server/routes/mcp_route"; const Docs = new Elysia() .use(Swagger({ @@ -48,7 +47,7 @@ const app = new Elysia() tags: ["MCP"], } }) - .use(McpRoute) + // .use(McpRoute) .get("*", html) .listen(3000, () => { console.log("Server running at http://localhost:3000"); diff --git a/src/server/routes/apikey_route.ts b/src/server/routes/apikey_route.ts index f1bb9d3..b2a3e16 100644 --- a/src/server/routes/apikey_route.ts +++ b/src/server/routes/apikey_route.ts @@ -55,7 +55,8 @@ const ApiKeyRoute = new Elysia({ }, { detail: { - summary: 'create api key', + summary: 'create', + description: 'create api key by user', }, body: t.Object({ name: t.String(), @@ -77,7 +78,8 @@ const ApiKeyRoute = new Elysia({ }, { detail: { - summary: 'get api key list', + summary: 'list', + description: 'get api key list by user', }, } ) @@ -94,7 +96,8 @@ const ApiKeyRoute = new Elysia({ }, { detail: { - summary: 'delete api key', + summary: 'delete', + description: 'delete api key by id', }, body: t.Object({ id: t.String(), diff --git a/src/server/routes/auth_route.ts b/src/server/routes/auth_route.ts index 004c7cd..c274401 100644 --- a/src/server/routes/auth_route.ts +++ b/src/server/routes/auth_route.ts @@ -133,8 +133,8 @@ const Auth = new Elysia({ password: t.String(), }), detail: { - description: 'Login with phone; auto-register if not found', summary: 'login', + description: 'Login with phone; auto-register if not found', }, } ) @@ -146,8 +146,9 @@ const Auth = new Elysia({ }, { detail: { - description: 'Logout (clear token cookie)', summary: 'logout', + description: 'Logout (clear token cookie)', + }, } ) diff --git a/src/server/routes/credential_route.ts b/src/server/routes/credential_route.ts index 9b9a5e6..482d04b 100644 --- a/src/server/routes/credential_route.ts +++ b/src/server/routes/credential_route.ts @@ -20,7 +20,11 @@ const CredentialRoute = new Elysia({ body: t.Object({ name: t.String(), value: t.String(), - }) + }), + detail: { + summary: 'create', + description: 'create credential', + } }) .get("/list", async (ctx) => { const list = await prisma.credential.findMany() @@ -28,6 +32,11 @@ const CredentialRoute = new Elysia({ message: "success", list } + }, { + detail: { + summary: 'list', + description: 'get credential list', + } }) .delete("/rm", async (ctx) => { const { id } = ctx.body @@ -40,7 +49,11 @@ const CredentialRoute = new Elysia({ }, { body: t.Object({ id: t.String() - }) + }), + detail: { + summary: 'rm', + description: 'delete credential by id', + } }) export default CredentialRoute \ No newline at end of file diff --git a/src/server/routes/darmasaba_route.ts b/src/server/routes/darmasaba_route.ts index 6b2ec78..49234f4 100644 --- a/src/server/routes/darmasaba_route.ts +++ b/src/server/routes/darmasaba_route.ts @@ -30,7 +30,7 @@ const DarmasabaRoute = new Elysia({ }) }, { detail: { - summary: "/repos", + summary: "repos", description: "get list of repositories" } }) @@ -57,7 +57,7 @@ const DarmasabaRoute = new Elysia({ }) }, { detail: { - summary: "/ls", + summary: "ls", description: "get list of dir in darmasaba" } }) @@ -88,7 +88,7 @@ const DarmasabaRoute = new Elysia({ dir: t.String() }), detail: { - summary: "/ls/:dir", + summary: "ls", description: "get list of files in darmasaba/" } }) @@ -122,7 +122,7 @@ const DarmasabaRoute = new Elysia({ file_name: t.String() }), detail: { - summary: "/file/:dir/:file_name", + summary: "file", description: "get content of file in darmasaba//" } }) diff --git a/src/server/routes/mcp_route.ts b/src/server/routes/mcp_route.ts deleted file mode 100644 index 845dbeb..0000000 --- a/src/server/routes/mcp_route.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { Elysia, t } from 'elysia'; -import { randomUUID } from 'node:crypto'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; -import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js'; - -// Map untuk menyimpan transport berdasarkan sessionId -const transports: Record = {}; - -const McpRoute = new Elysia() - .post( - '/mcp', - async ({ request, body, set }) => { - const sessionId = request.headers.get('mcp-session-id') ?? undefined; - let transport: StreamableHTTPServerTransport; - - // Reuse existing session jika ada - if (sessionId && transports[sessionId]) { - transport = transports[sessionId]; - } - // Jika ini permintaan inisialisasi MCP baru - else if (!sessionId && isInitializeRequest(body)) { - transport = new StreamableHTTPServerTransport({ - sessionIdGenerator: () => randomUUID(), - onsessioninitialized: (sid) => { - transports[sid] = transport; - console.log(`🟢 Session initialized: ${sid}`); - }, - }); - - transport.onclose = () => { - if (transport.sessionId) { - console.log(`🔴 Session closed: ${transport.sessionId}`); - delete transports[transport.sessionId]; - } - }; - - // Buat instance MCP server - const server = new McpServer({ - name: 'elysia-mcp-server', - version: '1.0.0', - }); - - // Contoh: tambahkan dummy tool/resource di sini jika mau - // server.addTool('ping', async () => 'pong'); - - await server.connect(transport); - - // Tunggu hingga session ID terbentuk - await new Promise((resolve) => { - const wait = () => { - if (transport.sessionId) resolve(); - else setTimeout(wait, 5); - }; - wait(); - }); - - // Kirim sessionId ke client - set.headers['mcp-session-id'] = transport.sessionId!; - set.status = 200; - return { sessionId: transport.sessionId }; - } - // Jika tidak valid - else { - set.status = 400; - return { - jsonrpc: '2.0', - error: { - code: -32000, - message: 'Bad Request: No valid session ID provided', - }, - id: null, - }; - } - - // ✅ Gunakan interface Web (Bun/Elysia) langsung - const webTransport = transport as any; - if (typeof webTransport.handleRequestWeb === 'function') { - // handleRequestWeb() adalah versi WebAPI (Request/Response) - return await webTransport.handleRequestWeb(request); - } else { - // fallback – manual handle body - return new Response(JSON.stringify({ ok: true }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - }, - { body: t.Any() } - ) - // Server-sent events (SSE) - .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; - return 'Invalid or missing session ID'; - } - - const webTransport = transport as any; - if (typeof webTransport.handleRequestWeb === 'function') { - return await webTransport.handleRequestWeb(request); - } - - set.status = 200; - return new Response('SSE not supported by this transport'); - }) - // Session cleanup - .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'; - } - - const webTransport = transport as any; - if (typeof webTransport.handleRequestWeb === 'function') { - return await webTransport.handleRequestWeb(request); - } - - set.status = 200; - return new Response('Session deleted'); - }) - -export default McpRoute \ No newline at end of file