|
1 | | -import { nanoid } from 'nanoid' |
2 | 1 | import express from 'express' |
3 | | -import cors from 'cors' |
4 | 2 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' |
5 | | -import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js' |
6 | 3 | import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js' |
7 | | -import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js' |
8 | 4 | import type { OptionsType } from '@/types' |
9 | 5 |
|
10 | 6 | export async function webServer(server: McpServer, options: OptionsType) { |
11 | 7 | const app = express() |
12 | | - app.use(cors()) |
13 | 8 | app.use(express.json()) |
14 | 9 |
|
15 | | - const transports = { |
16 | | - streamable: {} as Record<string, StreamableHTTPServerTransport>, |
17 | | - sse: {} as Record<string, SSEServerTransport>, |
18 | | - } |
19 | 10 | app.post('/mcp', async (req, res) => { |
20 | | - const sessionId = req.headers['mcp-session-id'] as string | undefined |
21 | | - let transport: StreamableHTTPServerTransport |
22 | | - |
23 | | - if (sessionId && transports.streamable[sessionId]) { |
24 | | - transport = transports.streamable[sessionId] |
25 | | - } else if (!sessionId && isInitializeRequest(req.body)) { |
26 | | - transport = new StreamableHTTPServerTransport({ |
27 | | - sessionIdGenerator: () => nanoid(), |
28 | | - onsessioninitialized: sessionId => { |
29 | | - transports.streamable[sessionId] = transport |
30 | | - }, |
31 | | - }) |
| 11 | + const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ |
| 12 | + sessionIdGenerator: undefined, |
| 13 | + }) |
| 14 | + res.on('close', () => { |
| 15 | + transport.close() |
| 16 | + server.close() |
| 17 | + }) |
| 18 | + await server.connect(transport) |
| 19 | + await transport.handleRequest(req, res, req.body) |
| 20 | + }) |
32 | 21 |
|
33 | | - transport.onclose = () => { |
34 | | - if (transport.sessionId) { |
35 | | - delete transports.streamable[transport.sessionId] |
36 | | - } |
37 | | - } |
38 | | - await server.connect(transport) |
39 | | - } else { |
40 | | - res.status(400).json({ |
| 22 | + const handleRequest = async (req: express.Request, res: express.Response) => { |
| 23 | + res.writeHead(405).end( |
| 24 | + JSON.stringify({ |
41 | 25 | jsonrpc: '2.0', |
42 | 26 | error: { |
43 | 27 | code: -32000, |
44 | | - message: 'Bad Request: No valid session ID provided', |
| 28 | + message: 'Method not allowed.', |
45 | 29 | }, |
46 | 30 | id: null, |
47 | | - }) |
48 | | - return |
49 | | - } |
50 | | - |
51 | | - await transport.handleRequest(req, res, req.body) |
52 | | - }) |
53 | | - |
54 | | - const handleSessionRequest = async (req: express.Request, res: express.Response) => { |
55 | | - const sessionId = req.headers['mcp-session-id'] as string | undefined |
56 | | - if (!sessionId || !transports.streamable[sessionId]) { |
57 | | - res.status(400).send('Invalid or missing session ID') |
58 | | - return |
59 | | - } |
60 | | - |
61 | | - const transport = transports.streamable[sessionId] |
62 | | - await transport.handleRequest(req, res) |
| 31 | + }), |
| 32 | + ) |
63 | 33 | } |
64 | 34 |
|
65 | | - app.get('/mcp', handleSessionRequest) |
66 | | - |
67 | | - app.delete('/mcp', handleSessionRequest) |
| 35 | + app.get('/mcp', handleRequest) |
68 | 36 |
|
69 | | - app.get('/sse', async (req, res) => { |
70 | | - const transport = new SSEServerTransport('/messages', res) |
71 | | - transports.sse[transport.sessionId] = transport |
72 | | - |
73 | | - res.on('close', () => { |
74 | | - delete transports.sse[transport.sessionId] |
75 | | - }) |
76 | | - |
77 | | - await server.connect(transport) |
78 | | - }) |
79 | | - |
80 | | - app.post('/messages', async (req, res) => { |
81 | | - const sessionId = req.query.sessionId as string |
82 | | - const transport = transports.sse[sessionId] |
83 | | - if (transport) { |
84 | | - await transport.handlePostMessage(req, res, req.body) |
85 | | - } else { |
86 | | - res.status(400).send('No transport found for sessionId') |
87 | | - } |
88 | | - }) |
| 37 | + app.delete('/mcp', handleRequest) |
89 | 38 |
|
90 | 39 | app.listen(options.port) |
91 | | - console.log(`MCP server started on port ${options.port}. SSE endpoint: /sse, streamable endpoint: /mcp`) |
| 40 | + console.log(`MCP server started on port ${options.port}, streamable endpoint: /mcp`) |
92 | 41 | } |
0 commit comments