http.ts•2.58 kB
import { randomUUID } from 'node:crypto';
import express, { type Request, type Response } from 'express';
import config from '../config.js';
import createMcpServer from '../server.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { ListToolsRequest, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
const yieldGenericServerError = (res: Response) => {
res.status(500).json({
id: null,
jsonrpc: '2.0',
error: { code: -32603, message: 'Internal server error' },
});
};
const transports = new Map<string, StreamableHTTPServerTransport>();
const isListToolsRequest = (value: unknown): value is ListToolsRequest =>
ListToolsRequestSchema.safeParse(value).success;
const getTransport = async (request: Request): Promise<StreamableHTTPServerTransport> => {
// Check for an existing session
const sessionId = request.headers['mcp-session-id'] as string;
if (sessionId && transports.has(sessionId)) {
return transports.get(sessionId)!;
}
// We have a special case where we'll permit ListToolsRequest w/o a session ID
if (!sessionId && isListToolsRequest(request.body)) {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
const mcpServer = createMcpServer();
await mcpServer.connect(transport);
return transport;
}
// Otherwise, start a new transport/session
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sessionId) => {
transports.set(sessionId, transport);
},
});
const mcpServer = createMcpServer();
await mcpServer.connect(transport);
return transport;
};
const createApp = () => {
const app = express();
app.use(express.json());
app.all('/mcp', async (req: Request, res: Response) => {
try {
const transport = await getTransport(req);
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error(error);
if (!res.headersSent) {
yieldGenericServerError(res);
}
}
});
app.all('/ping', (req: Request, res: Response) => {
res.status(200).json({ message: 'pong' });
});
return app;
};
const start = () => {
if (!config.ready) {
console.error('Invalid configuration');
process.exit(1);
}
const app = createApp();
app.listen(config.port, config.host, () => {
console.log(`Server is running on http://${config.host}:${config.port}/mcp`);
});
};
export default { start, createApp };