Skip to main content
Glama

server_doctor

Analyze server health proactively to detect disk capacity, swap usage, stale packages, security alerts, and reclaimable space. Provides severity-based findings with remediation commands.

Instructions

Run proactive health analysis on a server. Detects disk trending full, high swap, stale packages, elevated fail2ban bans, audit regression streaks, old backups, and reclaimable Docker space. Uses cached metrics by default — pass fresh=true to fetch live data via SSH. Returns findings grouped by severity (critical/warning/info) with remediation commands. For a full scored security audit across 27 categories, use server_audit instead.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
serverNoServer name or IP. Auto-selected if only one server exists.
freshNoFetch live data via SSH instead of using cached metrics. Default: false (reads cache only).
formatNoOutput format: summary (grouped findings with counts), json (full DoctorResult).summary

Implementation Reference

  • The main handler function for the `server_doctor` tool, which orchestrates the call to `runServerDoctor` and formats the output.
    export async function handleServerDoctor(params: {
      server?: string;
      fresh?: boolean;
      format?: "summary" | "json";
    }): Promise<McpResponse> {
      try {
        const servers = getServers();
        if (servers.length === 0) {
          return mcpError("No servers found", undefined, [
            { command: "kastell add", reason: "Add a server first" },
          ]);
        }
    
        const server = resolveServerForMcp(params, servers);
        if (!server) {
          if (params.server) {
            return mcpError(
              `Server not found: ${params.server}`,
              `Available servers: ${servers.map((s) => s.name).join(", ")}`,
            );
          }
          return mcpError(
            "Multiple servers found. Specify which server to use.",
            `Available: ${servers.map((s) => s.name).join(", ")}`,
          );
        }
    
        const fresh = params.fresh ?? false;
        const result = await runServerDoctor(server.ip, server.name, { fresh });
    
        if (!result.success || !result.data) {
          return mcpError(result.error ?? "Doctor analysis failed", result.hint);
        }
    
        const doctorResult = result.data;
        const format = params.format ?? "summary";
    
        if (format === "json") {
          return {
            content: [{ type: "text", text: JSON.stringify(doctorResult) }],
          };
        }
    
        // summary format: group findings by severity
        const bySeverity = {
          critical: doctorResult.findings.filter((f) => f.severity === "critical"),
          warning: doctorResult.findings.filter((f) => f.severity === "warning"),
          info: doctorResult.findings.filter((f) => f.severity === "info"),
        };
    
        const findingLines: string[] = [];
        for (const [severity, findings] of Object.entries(bySeverity)) {
          for (const f of findings) {
            findingLines.push(`  [${severity.toUpperCase()}] ${f.description} (fix: ${f.command})`);
          }
        }
    
        return mcpSuccess({
          server: doctorResult.serverName,
          total: doctorResult.findings.length,
          critical: bySeverity.critical.length,
          warning: bySeverity.warning.length,
          info: bySeverity.info.length,
          ranAt: doctorResult.ranAt,
          usedFreshData: doctorResult.usedFreshData,
          findings: findingLines,
        });
      } catch (error: unknown) {
        return mcpError(getErrorMessage(error));
      }
    }
  • Zod schema definition for the `server_doctor` tool inputs.
    export const serverDoctorSchema = {
      server: z.string().optional().describe("Server name or IP. Auto-selected if only one server exists."),
      fresh: z.boolean().default(false).describe("Fetch live data via SSH instead of using cached metrics. Default: false (reads cache only)."),
      format: z.enum(["summary", "json"]).default("summary").describe("Output format: summary (grouped findings with counts), json (full DoctorResult)."),
    };
  • Registration of the `server_doctor` tool with the MCP server instance.
    server.registerTool("server_doctor", {
      description:
        "Run proactive health analysis on a server. Detects disk trending full, high swap, stale packages, elevated fail2ban bans, audit regression streaks, old backups, and reclaimable Docker space. Uses cached metrics by default — pass fresh=true to fetch live data via SSH. Returns findings grouped by severity (critical/warning/info) with remediation commands. For a full scored security audit across 27 categories, use server_audit instead.",
      inputSchema: serverDoctorSchema,
      annotations: {
        title: "Server Doctor",
        readOnlyHint: true,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: true,
      },
    }, async (params) => {
      return handleServerDoctor(params);
    });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kastelldev/kastell'

If you have feedback or need assistance with the MCP directory API, please join our Discord server