import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import express, { Express, Request, Response } from 'express';
import { randomUUID } from 'crypto';
/**
* Runs the MCP server using Streamable HTTP transport
* This allows the server to be accessed over HTTP, enabling memory sharing across instances
*/
export async function runHttpServer(server: Server, port: number): Promise<void> {
const app: Express = express();
// Parse JSON bodies
app.use(express.json());
// Enable CORS for cross-origin requests
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Mcp-Session-Id');
res.header('Access-Control-Expose-Headers', 'Mcp-Session-Id');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
return;
}
next();
});
// Store transports by session ID for stateful connections
const transports = new Map<string, StreamableHTTPServerTransport>();
// Main MCP endpoint
app.post('/mcp', async (req: Request, res: Response) => {
try {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
let transport: StreamableHTTPServerTransport;
// Reuse existing transport for this session, or create a new one
if (sessionId && transports.has(sessionId)) {
transport = transports.get(sessionId)!;
} else {
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
});
await server.connect(transport);
// Store transport by session ID
const newSessionId = transport.sessionId;
if (newSessionId) {
transports.set(newSessionId, transport);
}
}
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error('Error handling MCP request:', error);
if (!res.headersSent) {
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32603,
message: 'Internal server error'
},
id: null
});
}
}
});
// GET endpoint (not typically used in Streamable HTTP, but included for completeness)
app.get('/mcp', (req: Request, res: Response) => {
res.status(405).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Method not allowed. Use POST to /mcp'
},
id: null
});
});
// Health check endpoint
app.get('/health', (req: Request, res: Response) => {
res.json({ status: 'ok', sessions: transports.size });
});
app.listen(port, () => {
console.log(`MCP Server running on http://localhost:${port}/mcp`);
console.log(`Health check available at http://localhost:${port}/health`);
});
}