index.ts•3.02 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
import { registerTools } from "./tools/index.js";
import fs from "node:fs";
import https from "node:https";
const server = new McpServer({
name: "Stateless Streamable HTTP Server",
version: "1.0.0",
});
registerTools(server);
const app = express();
app.use(express.json());
app.post("/mcp", async (req: express.Request, res: express.Response) => {
// In stateless mode, create a new instance of transport and server for each request
// to ensure complete isolation. A single instance would cause request ID collisions
// when multiple clients connect concurrently.
try {
const transport: StreamableHTTPServerTransport =
new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
res.on("close", () => {
console.log("Request closed");
transport.close();
server.close();
});
await server.connect(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,
});
}
}
});
app.get("/mcp", async (req: express.Request, res: express.Response) => {
console.log("Received GET MCP request");
res.writeHead(405).end(
JSON.stringify({
jsonrpc: "2.0",
error: {
code: -32000,
message: "Method not allowed.",
},
id: null,
}),
);
});
app.delete("/mcp", async (req: express.Request, res: express.Response) => {
console.log("Received DELETE MCP request");
res.writeHead(405).end(
JSON.stringify({
jsonrpc: "2.0",
error: {
code: -32000,
message: "Method not allowed.",
},
id: null,
}),
);
});
// Start the server
const PORT = process.env.PORT ?? 3000;
if (process.env.ENV !== "dev") {
if (!process.env.CERT_KEY_PATH || !process.env.CERT_CHAIN_PATH) {
throw new Error("CERT_KEY_PATH and CERT_CHAIN_PATH must be set");
}
const options = {
key: fs.readFileSync(process.env.CERT_KEY_PATH),
cert: fs.readFileSync(process.env.CERT_CHAIN_PATH),
};
https.createServer(options, app).listen(PORT, () => {
console.log(
`MCP Stateless Streamable HTTP Server listening on port ${PORT}`,
);
});
} else {
const httpServer = app.listen(PORT, () => {
console.log(
`MCP Stateless Streamable HTTP Server listening on port ${PORT}`,
);
});
// Keep the server alive
httpServer.keepAliveTimeout = 0;
// Handle graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
httpServer.close(() => {
console.log('Process terminated');
});
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down gracefully');
httpServer.close(() => {
console.log('Process terminated');
});
});
}