From a69b8337ab38707aaad2ac2fedb652f05afeb71c Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Fri, 9 Oct 2026 07:01:40 +0800 Subject: [PATCH] fix(cli): support MCP doctor help --- src/mcpDoctor.ts | 13 ++++++ tests/boundary/cli/mcpDoctorHelp.test.ts | 50 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 tests/boundary/cli/mcpDoctorHelp.test.ts diff --git a/src/mcpDoctor.ts b/src/mcpDoctor.ts index b50c450a4..f6c8510bf 100644 --- a/src/mcpDoctor.ts +++ b/src/mcpDoctor.ts @@ -11,6 +11,17 @@ import { MCP_STARTUP_POLICY } from "./mcpStartupPolicy.js"; const OUTPUT_FORMATS = ["toon", "json", "yaml", "md", "jsonl"] as const; type OutputFormat = (typeof OUTPUT_FORMATS)[number]; +const MCP_DOCTOR_HELP = `Usage: rea mcp doctor [options] + +Validate MCP server startup, tool and prompt inventories, identity, and request flow. + +Options: + --json Output JSON + --format Output format: ${OUTPUT_FORMATS.join(", ")} + --format= Set the output format + --full-output Include complete diagnostic details + -h, --help Show this help +`; interface McpDoctorOptions { readonly command: string; @@ -172,6 +183,8 @@ export const runProductionMcpDoctorCli = async ( arguments_: readonly string[], input: { readonly dispatcherPath: string; readonly packageRoot: string }, ): Promise<{ readonly output: string; readonly exitCode: number }> => { + if (arguments_.includes("--help") || arguments_.includes("-h")) + return { output: MCP_DOCTOR_HELP, exitCode: 0 }; const parsed = parseOutputArguments(arguments_); if (!parsed.ok) return { diff --git a/tests/boundary/cli/mcpDoctorHelp.test.ts b/tests/boundary/cli/mcpDoctorHelp.test.ts new file mode 100644 index 000000000..8f91efeca --- /dev/null +++ b/tests/boundary/cli/mcpDoctorHelp.test.ts @@ -0,0 +1,50 @@ +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; + +import { expect, it } from "vitest"; + +const execute = promisify(execFile); + +const runCli = async (arguments_: readonly string[]) => { + try { + const result = await execute(process.execPath, ["scripts/rea.mjs", ...arguments_], { + cwd: process.cwd(), + env: { ...process.env, REA_LOG_LEVEL: "silent" }, + timeout: 15_000, + }); + return { exitCode: 0, ...result }; + } catch (cause: unknown) { + if ( + typeof cause === "object" && + cause !== null && + "code" in cause && + typeof cause.code === "number" && + "stdout" in cause && + typeof cause.stdout === "string" && + "stderr" in cause && + typeof cause.stderr === "string" + ) + return { + exitCode: cause.code, + stdout: cause.stdout, + stderr: cause.stderr, + }; + throw cause; + } +}; + +it("shows MCP doctor help without starting a server and keeps option errors actionable", async () => { + const parentHelp = await runCli(["mcp", "--help"]); + expect(parentHelp.exitCode).toBe(0); + expect(parentHelp.stdout).toContain("doctor"); + + const help = await runCli(["mcp", "doctor", "--help"]); + expect(help.exitCode).toBe(0); + expect(help.stdout).toContain("Usage: rea mcp doctor [options]"); + expect(help.stdout).toContain("--format "); + expect(help.stdout).toContain("--full-output"); + + const invalid = await runCli(["mcp", "doctor", "--not-an-option"]); + expect(invalid.exitCode).toBe(1); + expect(invalid.stdout).toContain("Unknown mcp doctor option: --not-an-option"); +});