import express, { Request, Response } from "express";
import helmet from "helmet";
import { randomUUID } from "node:crypto";
import debug from "debug"; // typed after you install @types/debug
import "dotenv/config";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport }
from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { triggerAttachedResources } from "./functions/resource/triggerAttachedResources.js";
import { createMcpServer } from "./functions/server/createMcpServer.js";
import { statusEndpoint } from "./functions/requests/statusEndpoint.js";
import type { SessionData, PlannerState, PlanCycle } from "./interfaces.js";
import { handleMcpRequest } from "./functions/requests/handleMcpRequest.js";
import { cleanupEndpoint } from "./functions/requests/cleanupEndpoint.js";
/* ───────── config ───────── */
const PORT = Number(process.env.PORT) || 4000;
const HOST = process.env.HOST ?? "localhost";
const REQUIRE_AUTH = process.env.MCP_REQUIRE_AUTH !== "false";
const MCP_BURST_CLIENT = process.env.MCP_BURST_CLIENT === "true";
const MAX_CONCURRENT_CONNECTIONS = Number(process.env.MAX_CONCURRENT_CONNECTIONS) || 100;
/* ───────── debug helpers ───────── */
export const logReq = debug("hub:req");
export const logRes = debug("hub:res");
export const logErr = debug("hub:err");
export const logBrid = debug("hub:bridge");
// Track active connections for monitoring
export const activeConnectionsRef = { value: 0 };
let activeConnections = activeConnectionsRef.value;
// Update active connections
export const updateActiveConnections = (delta: number) => {
activeConnectionsRef.value += delta;
activeConnections = activeConnectionsRef.value;
};
// Session-aware server management
export const activeSessions = new Map<string, SessionData>();
export const sessionServers = new Map<string, StreamableHTTPServerTransport>();
export const sessionMcpServers = new Map<string, McpServer>();
console.error("activeSessions, sessionServers, sessionMcpServers initialized.");
/* ───────── launch hub ───────── */
// Create a factory function for creating MCP servers
async function getOrCreateMcpServer(sessionId: string): Promise<McpServer> {
if (sessionMcpServers.has(sessionId)) {
return sessionMcpServers.get(sessionId)!;
}
const newServer = await createMcpServer();
sessionMcpServers.set(sessionId, newServer);
return newServer;
}
// Usage example:
const sessionId = "1";
const hub = await getOrCreateMcpServer(sessionId);
/* ───────── transport ───────── */
export const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
enableDnsRebindingProtection: true,
allowedHosts: process.env.ALLOWED_HOSTS ? JSON.parse(process.env.ALLOWED_HOSTS) : undefined,
});
transport.onclose = () => {
if (transport.sessionId) {
//TODO: Persist session data if needed
const sid = transport.sessionId;
delete transport.sessionId;
updateActiveConnections(-1);
sessionServers.delete(sid); // Use Map API to remove session
}
};
// Enable session isolation by creating the connection with proper options
await hub.connect(transport);
/* ───────── Express façade ───────── */
const app = express();
app.use(helmet());
app.use(express.json());
/* optional bearer-token gate */
const requireBearer: express.RequestHandler = (req, res, next) => {
if (!REQUIRE_AUTH) return next();
if (!req.headers.authorization?.startsWith("Bearer ")) {
res.status(401).setHeader("WWW-Authenticate", 'Bearer realm="mcp"').end();
return;
}
next();
};
app.all("/mcp", requireBearer, handleMcpRequest("/mcp"));
/* Add root endpoint handler for MCP client discovery */
if (MCP_BURST_CLIENT) {
app.get("/", (req: Request, res: Response): void => {
res.json({
name: req.body.params.clientInfo?.name || "MCP Hub",
version: "1.0.0",
capabilities: {
tools: true,
prompts: true,
multipleClients: true
},
endpoints: {
mcp: "/mcp",
status: "/status",
health: "/health",
cleanup: "/cleanup"
},
message: "This is an MCP server. Use POST requests for MCP communication."
});
});
app.all("/", requireBearer, handleMcpRequest("/"));
}
/* Add monitoring endpoint */
app.get('/status', (_req: Request, res: Response): void => {
statusEndpoint(
app,
"status",
res,
sessionServers,
activeConnections,
MAX_CONCURRENT_CONNECTIONS
);
});
/* Add health check endpoint */
app.get('/health', (req: Request, res: Response): void => {
res.send('Server is healthy. MCP Hub is running, active sessions: ' + activeSessions.size);
});
/* Add session cleanup endpoint */
app.post('/cleanup', (req: Request, res: Response): void => {
cleanupEndpoint(req, res, activeSessions, sessionServers);
});
app.listen(PORT, HOST, () =>
console.error(`🌐 MCP hub on http://${HOST}:${PORT}/mcp — auth ${REQUIRE_AUTH ? "ON" : "OFF"}${MCP_BURST_CLIENT ? " — root endpoint enabled" : ""}`),
);