doctor
Run connection scans, security audits, and latency benchmarks for all your MCP servers with a single command.
Instructions
Run all checks at once: scan connections, audit security, and benchmark latency for all configured MCP servers
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/server.ts:116-188 (handler)The MCP tool handler for 'doctor'. It scans all configs, tests connections (scan), runs security audit, and benchmarks latency, then returns a combined report.
server.tool( "doctor", "Run all checks at once: scan connections, audit security, and benchmark latency for all configured MCP servers", {}, async () => { const servers = await scanConfigs(); if (servers.length === 0) { return { content: [{ type: "text", text: "No MCP servers found in any configuration." }], }; } const results: ScanResult[] = []; for (const s of servers) { results.push(await testServer(s)); } const issues = checkSecurity(servers); const ok = results.filter((r) => r.status === "ok").length; const responding = results.filter((r) => r.latencyMs !== undefined); const avgLatency = responding.length ? Math.round(responding.reduce((sum, r) => sum + (r.latencyMs ?? 0), 0) / responding.length) : 0; const scanLines = results.map((r) => { const status = r.status === "ok" ? "OK" : r.status === "timeout" ? "TIMEOUT" : "ERROR"; const latency = r.latencyMs ? `${r.latencyMs}ms` : "—"; const tools = r.tools !== undefined ? `${r.tools} tools` : "—"; return `${r.server.name} (${r.server.source}) — ${status} | ${latency} | ${tools}`; }); const secLines = issues.length === 0 ? ["No security issues found."] : issues.map((i) => `[${i.severity.toUpperCase()}] ${i.server.name}: ${i.message}`); const benchLines = responding.length === 0 ? ["No servers responded for benchmarking."] : responding .sort((a, b) => (a.latencyMs ?? 0) - (b.latencyMs ?? 0)) .map((r, i) => { const ms = r.latencyMs!; const rating = ms < 500 ? "Fast" : ms < 2000 ? "OK" : "Slow"; return `${i + 1}. ${r.server.name} — ${ms}ms (${rating})`; }); const summary = [ `\n--- Summary ---`, `Servers: ${results.length} found, ${ok} healthy`, `Security: ${issues.length} issue(s)`, responding.length > 0 ? `Avg latency: ${avgLatency}ms` : null, ok === results.length && issues.length === 0 ? "All clear — your MCP setup looks healthy." : `Needs attention: ${[ok < results.length ? `${results.length - ok} server(s) down` : null, issues.length > 0 ? `${issues.length} security issue(s)` : null].filter(Boolean).join(", ")}`, ].filter(Boolean); const output = [ "=== Scan ===", ...scanLines, "", "=== Security ===", ...secLines, "", "=== Benchmark ===", ...benchLines, ...summary, ].join("\n"); return { content: [{ type: "text", text: output }], }; } ); - src/index.ts:88-134 (handler)CLI command handler for 'doctor' - the CLI version that runs scan + security + bench and prints results including a doctor summary.
program .command("doctor") .description("Run all checks: scan + security + bench") .option("--json", "Output results as JSON") .action(async (opts) => { const servers = await discoverServers(opts.json); if (servers.length === 0) return; // Run scan const results = await testAllServers(servers, opts.json, "Testing connections...", "Connection tests complete"); // Run security let issues: SecurityIssue[]; if (!opts.json) { const secSpinner = ora("Running security checks...").start(); issues = checkSecurity(servers); secSpinner.succeed("Security scan complete"); console.log(); } else { issues = checkSecurity(servers); } if (opts.json) { console.log(JSON.stringify({ scan: results.map(formatScanResult), security: issues.map(formatSecurityIssue), bench: results .filter((r) => r.status === "ok" && r.latencyMs !== undefined) .sort((a, b) => (a.latencyMs ?? 0) - (b.latencyMs ?? 0)) .map(formatScanResult), summary: { servers: servers.length, healthy: results.filter((r) => r.status === "ok").length, securityIssues: issues.length, avgLatencyMs: Math.round( results.filter((r) => r.latencyMs).reduce((sum, r) => sum + (r.latencyMs ?? 0), 0) / (results.filter((r) => r.latencyMs).length || 1) ), }, }, null, 2)); } else { printScanResults(results); printSecurityIssues(issues); printBenchResults(results); printDoctorSummary(results, issues); } }); - src/server.ts:9-12 (registration)The MCP server is registered with name 'mcp-doctor', and the 'doctor' tool is registered via server.tool() on line 116.
const server = new McpServer({ name: "mcp-doctor", version: "0.3.0", }); - src/ui.ts:143-167 (helper)printDoctorSummary helper - prints a formatted summary including server count, healthy count, security issues, and avg latency.
export function printDoctorSummary(results: ScanResult[], issues: SecurityIssue[]): void { const ok = results.filter((r) => r.status === "ok").length; const total = results.length; const responding = results.filter((r) => r.latencyMs !== undefined); const avgLatency = responding.length ? Math.round(responding.reduce((sum, r) => sum + (r.latencyMs ?? 0), 0) / responding.length) : 0; console.log(chalk.bold("\n Summary")); console.log(chalk.dim(" " + "─".repeat(40))); console.log(` Servers: ${chalk.cyan(String(total))} found, ${chalk.green(String(ok))} healthy`); console.log(` Security: ${issues.length === 0 ? chalk.green("0 issues") : chalk.yellow(`${issues.length} issue(s)`)}`); if (responding.length > 0) { console.log(` Avg latency: ${chalk.cyan(`${avgLatency}ms`)}`); } if (ok === total && issues.length === 0) { console.log(chalk.green("\n All clear — your MCP setup looks healthy.\n")); } else { const problems: string[] = []; if (ok < total) problems.push(`${total - ok} server(s) not responding`); if (issues.length > 0) problems.push(`${issues.length} security issue(s)`); console.log(chalk.yellow(`\n Needs attention: ${problems.join(", ")}\n`)); } } - src/types.ts:1-25 (schema)Type definitions used by the doctor tool: McpServer, ScanResult, and SecurityIssue interfaces.
export interface McpServer { name: string; source: string; // e.g. "Claude Code (~/.claude.json)" configPath: string; // full path to the config file type: "stdio" | "sse" | "unknown"; command?: string; args?: string[]; url?: string; env?: Record<string, string>; } export interface ScanResult { server: McpServer; status: "ok" | "error" | "timeout"; latencyMs?: number; tools?: number; error?: string; } export interface SecurityIssue { server: McpServer; severity: "high" | "medium" | "low"; message: string; detail: string; }