@@ -4,8 +4,10 @@ import cors from 'cors';
4
4
import cookieParser from 'cookie-parser';
5
5
import { StatusCodes } from 'http-status-codes';
6
6
import { config } from 'dotenv';
7
-
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
8
7
import { AgentMcpServer, normalizeEnvVar } from '@cheqd/mcp-toolkit-server';
8
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
9
+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
10
+
import { randomUUID } from 'node:crypto';
9
11
10
12
config();
11
13
@@ -47,39 +49,75 @@ class App {
47
49
48
50
private routes() {
49
51
const app = this.express;
50
-
const transports: { [sessionId: string]: SSEServerTransport } = {};
52
+
app.use(express.json());
51
53
52
-
app.get('/sse', async (_req: Request, res: Response) => {
53
-
const transport = new SSEServerTransport('/messages', res);
54
-
transports[transport.sessionId] = transport;
54
+
// Map to store transports by session ID
55
+
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
55
56
56
-
console.log('SSE session started:', transport.sessionId);
57
-
58
-
res.on('close', () => {
59
-
console.log('SSE session closed:', transport.sessionId);
60
-
delete transports[transport.sessionId];
61
-
});
62
-
63
-
console.log('Connecting to server...');
64
-
await this.server.getStatus();
65
-
if (this.server) {
66
-
await this.server.start(transport).catch((err) => {
67
-
console.error('Unhandled error in server startup:', err);
57
+
// Handle POST requests for client-to-server communication
58
+
app.post('/mcp', async (_req: Request, res: Response) => {
59
+
// Check for existing session ID
60
+
const sessionId = _req.headers['mcp-session-id'] as string | undefined;
61
+
let transport: StreamableHTTPServerTransport;
62
+
if (sessionId && transports[sessionId]) {
63
+
transport = transports[sessionId];
64
+
} else if (!sessionId && isInitializeRequest(_req.body)) {
65
+
transport = new StreamableHTTPServerTransport({
66
+
sessionIdGenerator: () => randomUUID(),
67
+
onsessioninitialized: (sessionId) => {
68
+
// Store the transport by session ID
69
+
transports[sessionId] = transport;
70
+
},
71
+
});
72
+
// Clean up transport when closed
73
+
transport.onclose = () => {
74
+
if (transport.sessionId) {
75
+
console.log('Session closed:', transport.sessionId);
76
+
delete transports[transport.sessionId];
77
+
}
78
+
};
79
+
console.log('Connecting to server...');
80
+
// Connect to the MCP server
81
+
await this.server.getStatus();
82
+
if (this.server) {
83
+
await this.server.start(transport).catch((err) => {
84
+
console.error('Unhandled error in server startup:', err);
85
+
});
86
+
console.log('Connected.');
87
+
}
88
+
console.log('MCP session started');
89
+
} else {
90
+
// Invalid request
91
+
res.status(400).json({
92
+
jsonrpc: '2.0',
93
+
error: {
94
+
code: -32000,
95
+
message: 'Bad Request: No valid session ID provided',
96
+
},
97
+
id: null,
68
98
});
69
-
console.log('Connected.');
99
+
return;
70
100
}
101
+
102
+
// Handle the request
103
+
await transport.handleRequest(_req, res, _req.body);
71
104
});
105
+
// Reusable handler for GET and DELETE requests
106
+
const handleSessionRequest = async (req: express.Request, res: express.Response) => {
107
+
const sessionId = req.headers['mcp-session-id'] as string | undefined;
108
+
if (!sessionId || !transports[sessionId]) {
109
+
res.status(400).send('Invalid or missing session ID');
110
+
return;
111
+
}
72
112
73
-
app.post('/messages', async (req, res) => {
74
-
const sessionId = req.query.sessionId as string;
75
113
const transport = transports[sessionId];
114
+
await transport.handleRequest(req, res);
115
+
};
116
+
// Handle GET requests for server-to-client notifications via SSE
117
+
app.get('/mcp', handleSessionRequest);
76
118
77
-
if (transport) {
78
-
await transport.handlePostMessage(req, res);
79
-
} else {
80
-
res.status(400).send('No transport found for sessionId');
81
-
}
82
-
});
119
+
// Handle DELETE requests for session termination
120
+
app.delete('/mcp', handleSessionRequest);
83
121
84
122
app.get('/status', async (_req: Request, res: Response) => {
85
123
// Basic server information
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4