|
1 |
| -import type { IncomingMessage, ServerResponse } from "node:http"; |
2 | 1 | import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
3 | 2 | import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
4 |
| -import { type RequestHandlers, createBaseHttpServer } from "../utils"; |
| 3 | +import express from "express"; |
5 | 4 |
|
6 | 5 | export const startSSEMcpServer = async (
|
7 | 6 | server: Server,
|
8 | 7 | endpoint = "/sse",
|
9 | 8 | port = 1122,
|
10 | 9 | ): Promise<void> => {
|
11 |
| - const activeTransports: Record<string, SSEServerTransport> = {}; |
12 |
| - |
13 |
| - // Define the request handler for SSE-specific logic |
14 |
| - const handleRequest: RequestHandlers["handleRequest"] = async ( |
15 |
| - req: IncomingMessage, |
16 |
| - res: ServerResponse, |
17 |
| - ) => { |
18 |
| - if (!req.url) { |
19 |
| - res.writeHead(400).end("No URL"); |
20 |
| - return; |
21 |
| - } |
22 |
| - |
23 |
| - const reqUrl = new URL(req.url, "http://localhost"); |
24 |
| - |
25 |
| - // Handle GET requests to the SSE endpoint |
26 |
| - if (req.method === "GET" && reqUrl.pathname === endpoint) { |
27 |
| - const transport = new SSEServerTransport("/messages", res); |
28 |
| - |
29 |
| - activeTransports[transport.sessionId] = transport; |
30 |
| - |
31 |
| - let closed = false; |
32 |
| - |
33 |
| - res.on("close", async () => { |
34 |
| - closed = true; |
35 |
| - |
36 |
| - try { |
37 |
| - await server.close(); |
38 |
| - } catch (error) { |
39 |
| - console.error("Error closing server:", error); |
40 |
| - } |
41 |
| - |
42 |
| - delete activeTransports[transport.sessionId]; |
43 |
| - }); |
44 |
| - |
45 |
| - try { |
46 |
| - await server.connect(transport); |
47 |
| - |
48 |
| - await transport.send({ |
49 |
| - jsonrpc: "2.0", |
50 |
| - method: "sse/connection", |
51 |
| - params: { message: "SSE Connection established" }, |
52 |
| - }); |
53 |
| - } catch (error) { |
54 |
| - if (!closed) { |
55 |
| - console.error("Error connecting to server:", error); |
56 |
| - |
57 |
| - res.writeHead(500).end("Error connecting to server"); |
58 |
| - } |
59 |
| - } |
60 |
| - |
61 |
| - return; |
| 10 | + const app = express(); |
| 11 | + app.use(express.json()); |
| 12 | + |
| 13 | + const transports: Record<string, SSEServerTransport> = {}; |
| 14 | + |
| 15 | + app.get(endpoint, async (req, res) => { |
| 16 | + try { |
| 17 | + const transport = new SSEServerTransport('/messages', res); |
| 18 | + transports[transport.sessionId] = transport; |
| 19 | + transport.onclose = () => delete transports[transport.sessionId]; |
| 20 | + await server.connect(transport); |
| 21 | + } catch (error) { |
| 22 | + if (!res.headersSent) res.status(500).send('Error establishing SSE stream'); |
62 | 23 | }
|
| 24 | + }); |
63 | 25 |
|
64 |
| - // Handle POST requests to the messages endpoint |
65 |
| - if (req.method === "POST" && req.url?.startsWith("/messages")) { |
66 |
| - const sessionId = new URL( |
67 |
| - req.url, |
68 |
| - "https://example.com", |
69 |
| - ).searchParams.get("sessionId"); |
70 |
| - |
71 |
| - if (!sessionId) { |
72 |
| - res.writeHead(400).end("No sessionId"); |
73 |
| - return; |
74 |
| - } |
75 |
| - |
76 |
| - const activeTransport: SSEServerTransport | undefined = |
77 |
| - activeTransports[sessionId]; |
78 |
| - |
79 |
| - if (!activeTransport) { |
80 |
| - res.writeHead(400).end("No active transport"); |
81 |
| - return; |
82 |
| - } |
83 |
| - |
84 |
| - await activeTransport.handlePostMessage(req, res); |
85 |
| - return; |
86 |
| - } |
87 |
| - |
88 |
| - // If we reach here, no handler matched |
89 |
| - res.writeHead(404).end("Not found"); |
90 |
| - }; |
91 |
| - |
92 |
| - // Custom cleanup for SSE server |
93 |
| - const cleanup = () => { |
94 |
| - // Close all active transports |
95 |
| - for (const transport of Object.values(activeTransports)) { |
96 |
| - transport.close(); |
| 26 | + app.post('/messages', async (req, res) => { |
| 27 | + const sessionId = req.query.sessionId as string; |
| 28 | + if (!sessionId) return res.status(400).send('Missing sessionId parameter'); |
| 29 | + |
| 30 | + const transport = transports[sessionId]; |
| 31 | + if (!transport) return res.status(404).send('Session not found'); |
| 32 | + |
| 33 | + try { |
| 34 | + await transport.handlePostMessage(req, res, req.body); |
| 35 | + } catch (error) { |
| 36 | + if (!res.headersSent) res.status(500).send('Error handling request'); |
97 | 37 | }
|
98 |
| - server.close(); |
99 |
| - }; |
| 38 | + }); |
100 | 39 |
|
101 |
| - // Create the HTTP server using our factory |
102 |
| - createBaseHttpServer(port, endpoint, { |
103 |
| - handleRequest, |
104 |
| - cleanup, |
105 |
| - serverType: "SSE Server", |
| 40 | + app.listen(port, () => { |
| 41 | + console.log(`SSE Server listening on http://localhost:${port}${endpoint}`); |
106 | 42 | });
|
107 | 43 | };
|
0 commit comments