import {
StrictnessResponse,
strictnessResponseSchema,
setStrictnessRequestSchema,
} from "@mcpx/shared-model";
import { loggableError } from "@mcpx/toolkit-core/logging";
import express, { Router } from "express";
import { Logger } from "winston";
import z from "zod/v4";
import { Services } from "../services/services.js";
export function buildAdminRouter(
authGuard: express.RequestHandler,
services: Services,
logger: Logger,
): Router {
const router = Router();
// Middleware to check if caller is admin (based on identity from Hub)
const adminOnly: express.RequestHandler = (_req, res, next) => {
if (!services.identityService.isAdmin()) {
res.status(401).json({ error: "Admin access required" });
return;
}
next();
};
// Get current strictness state
router.get("/strictness", authGuard, (_req, res) => {
const systemStrictnessRequired =
services.catalogManager.isStrictnessRequired;
logger.debug("Got system strictness from catalog-manager", {
isStrictnessRequired: systemStrictnessRequired,
});
if (systemStrictnessRequired) {
const response: StrictnessResponse = {
strictnessFeatureEnabled: true,
isStrict: services.catalogManager.isStrict(),
adminOverride: services.catalogManager.getAdminStrictnessOverride(),
};
res.json(response satisfies z.infer<typeof strictnessResponseSchema>);
return;
}
// strictness isn't required - isStrict is false for everyone
const response: StrictnessResponse = {
strictnessFeatureEnabled: false,
};
res.json(response satisfies z.infer<typeof strictnessResponseSchema>);
});
// Set admin strictness override
router.post("/strictness", authGuard, adminOnly, (req, res) => {
const parsed = setStrictnessRequestSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({
error: "Invalid request",
details: z.treeifyError(parsed.error),
});
return;
}
services.catalogManager.setAdminStrictnessOverride(parsed.data.override);
logger.info("Admin strictness override updated via API", {
override: parsed.data.override,
});
const response: StrictnessResponse = {
strictnessFeatureEnabled: true,
isStrict: services.catalogManager.isStrict(),
adminOverride: services.catalogManager.getAdminStrictnessOverride(),
};
res.json(response satisfies z.infer<typeof strictnessResponseSchema>);
});
// TODO: extract `Sessions` service and use reload here & in webserver
router.post("/reload", authGuard, async (_req, res) => {
try {
logger.info("Reloading target servers");
await services.upstreamHandler.initialize();
logger.debug(
"Current clientsByService (global)",
Object.fromEntries(services.upstreamHandler.clientsByService.entries()),
);
// Close all existing sessions so they can reconnect and get the updated tools
await services.sessions.shutdown();
logger.info("All sessions closed");
res
.status(200)
.send(
"Target server configuration reloaded (clients will be lazy-loaded per session)",
);
} catch (e) {
const error = loggableError(e);
logger.error("Error reloading target servers", error);
res.status(500).send("Error connecting to target servers");
}
});
return router;
}