import { apiKeyAuthMiddleware } from "xmcp";
import { checkUserToolAllotment } from "./utils/token-auth";
// Normalize incoming paths so clients hitting `/` (or legacy SSE paths)
// are transparently routed to the XMCP endpoint (default `/mcp`).
const pathRewrite = (req: any, res: any, next: any) => {
try {
const url = req.url || "/";
// Simple health endpoints for GET
if (req.method === "GET" && (url === "/" || url === "/health")) {
res.statusCode = 200;
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.end("OK");
return;
}
// Rewrite SSE-like endpoints to XMCP SSE
if (req.method === "GET" && (url === "/sse" || url === "/events")) {
req.url = "/mcp/sse";
}
// For JSON-RPC POSTs sent to root, forward to /mcp
if (req.method === "POST" && (url === "/" || url === "")) {
req.url = "/mcp";
}
} catch {
// no-op; continue
}
return next();
};
const auth = apiKeyAuthMiddleware({
headerName: "x-api-key",
validateApiKey: async (apiKey: string) => {
if (process.env.BYPASS_AUTH_FOR_TESTS === "true") {
console.warn("[middleware] BYPASS_AUTH_FOR_TESTS is enabled - API key validation bypassed!");
return true;
}
console.log("[middleware] Invoked validateApiKey (API key redacted)");
if (!apiKey) {
console.warn("[middleware] No API key provided");
return false;
}
try {
await checkUserToolAllotment(apiKey);
console.log("[middleware] Allotment decremented (API key redacted)");
return true;
} catch (err: any) {
console.error("[middleware] API key validation or usage error:", err, "(API key redacted)");
return false;
}
}
// To customize the error message, see xmcp docs for 'onError' pattern support in future versions
});
// Chain middlewares: path rewrite first, then auth
const middleware = [pathRewrite, auth] as any;
export default middleware;