tamabahan

This commit is contained in:
bipproduction
2025-10-08 19:41:57 +08:00
parent f580dba49d
commit f4e5bff621
4 changed files with 270 additions and 36 deletions

View File

@@ -9,14 +9,7 @@ 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";
function encode(str: string) {
return new TextEncoder().encode(str);
}
function sleep(ms: number) {
return new Promise((r) => setTimeout(r, ms));
}
import McpRoute from "./server/routes/mcp_route";
const Docs = new Elysia()
.use(Swagger({
@@ -55,34 +48,7 @@ const app = new Elysia()
tags: ["MCP"],
}
})
.get("/mcp", () => {
// Buat stream SSE
const stream = new ReadableStream({
async start(controller) {
// Kirim event pertama
controller.enqueue(encode("event: ready\ndata: " + JSON.stringify({ ok: true }) + "\n\n"));
// Kirim event tiap 5 detik
while (true) {
controller.enqueue(encode("event: status\ndata: " + JSON.stringify({ timestamp: Date.now() }) + "\n\n"));
await sleep(5000);
}
},
cancel() {
console.log("SSE client disconnected");
}
});
// Kembalikan Response manual
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive"
}
});
})
.use(McpRoute)
.get("*", html)
.listen(3000, () => {
console.log("Server running at http://localhost:3000");

View File

@@ -0,0 +1,94 @@
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<string, StreamableHTTPServerTransport> = {};
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
if (sessionId && transports[sessionId]) {
transport = transports[sessionId];
} else if (!sessionId && isInitializeRequest(body)) {
// Jika belum ada session & ini request inisialisasi
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sid) => {
transports[sid] = transport;
},
});
// Cleanup transport jika ditutup
transport.onclose = () => {
if (transport.sessionId) {
delete transports[transport.sessionId];
}
};
// Inisialisasi MCP server
const server = new McpServer({
name: 'example-server',
version: '1.0.0',
});
// ... di sini bisa ditambahkan tools, prompts, dsb ...
await server.connect(transport);
} else {
set.status = 400;
return {
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Bad Request: No valid session ID provided',
},
id: null,
};
}
// 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
);
},
{
body: t.Any(), // fleksibel untuk JSON-RPC
}
)
// Handler reusable untuk GET & DELETE
.derive(({ 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 { transport };
})
// GET untuk server → client via SSE
.get('/mcp', async ({ transport, request }) => {
return await transport.handleRequest(request as any, new Response() as any);
})
// DELETE untuk terminasi session
.delete('/mcp', async ({ transport, request }) => {
return await transport.handleRequest(request as any, new Response() as any);
})
export default McpRoute