From 6d6cbdf8c440507bd0a9f39aa8dd77fe9f2cd869 Mon Sep 17 00:00:00 2001 From: Ezra Brilliant <64943463+ezrabrilliant@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:39:41 +0700 Subject: [PATCH] fix(doctor): report a missing --format value as MISSING_OUTPUT_VALUE (#6489) `mcp doctor --format` and `--format=` reported VALIDATION_ERROR "Invalid output format: missing", although docs/cli.md documents MISSING_OUTPUT_VALUE for valued output flags without a value and `capabilities` already returns it. Classify absent and empty values as missing, share the existing missing-value message, and keep VALIDATION_ERROR for a present but unsupported format. Fixes #6455 Claude-Session: https://claude.ai/code/session_01LjhoPGpqAwHwcCgectzRsp Co-authored-by: Claude Opus 5.5 --- src/cliOutput.ts | 6 ++++- src/mcpDoctor.ts | 16 +++++++++-- tests/boundary/cli/mcpDoctorHelpCli.test.ts | 30 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/cliOutput.ts b/src/cliOutput.ts index d505d85c32..ea2fba6f6a 100644 --- a/src/cliOutput.ts +++ b/src/cliOutput.ts @@ -241,6 +241,10 @@ const hasUnclosedFilterSlice = (expression: string): boolean => { return unclosed; }; +/** Name a valued output flag that was given without a value. */ +export const missingCliOutputValueMessage = (flag: string): string => + `${flag} requires a value, as in ${flag} or ${flag}=.`; + /** Reject output controls that cannot safely produce a complete result. */ export const validateCliOutputArguments = ( arguments_: readonly string[], @@ -273,7 +277,7 @@ export const validateCliOutputArguments = ( ok: false, format: format === "yaml" || format === "jsonl" ? format : "json", code: "MISSING_OUTPUT_VALUE", - message: `${missingValue} requires a value, as in ${missingValue} or ${missingValue}=.`, + message: missingCliOutputValueMessage(missingValue), }; return { ok: true }; }; diff --git a/src/mcpDoctor.ts b/src/mcpDoctor.ts index 4cfd927a1b..06678618dc 100644 --- a/src/mcpDoctor.ts +++ b/src/mcpDoctor.ts @@ -7,6 +7,7 @@ import { StringDecoder } from "node:string_decoder"; import { createCatalogIdentity } from "./catalogIdentity.js"; import { jsonOutputParts } from "./cli/jsonOutput.js"; +import { missingCliOutputValueMessage } from "./cliOutput.js"; import { compactPresentationForMcpInputSchemaProfile, parseMcpInputSchemaProfile, @@ -338,7 +339,7 @@ export const runProductionMcpDoctorCli = async ( return { output: `${renderMcpDoctorHelp()}\n`, exitCode: 0 }; return { output: renderOutput( - { code: "VALIDATION_ERROR", message: parsed.message }, + { code: parsed.code, message: parsed.message }, parsed.format, input.stdoutIsTerminal, ), @@ -442,6 +443,7 @@ type McpDoctorArguments = readonly ok: false; readonly help: false; readonly format: OutputFormat; + readonly code: "MISSING_OUTPUT_VALUE" | "VALIDATION_ERROR"; readonly message: string; }; @@ -476,12 +478,21 @@ const parseOutputArguments = ( if (argument === "--format" || inline !== undefined) { const candidate = inline ?? arguments_[index + 1]; if (inline === undefined) index += 1; + if (candidate === undefined || candidate === "") + return { + ok: false, + help: false, + format, + code: "MISSING_OUTPUT_VALUE", + message: missingCliOutputValueMessage("--format"), + }; if (!isOutputFormat(candidate)) return { ok: false, help: false, format, - message: `Invalid output format: ${candidate ?? "missing"}`, + code: "VALIDATION_ERROR", + message: `Invalid output format: ${candidate}`, }; format = candidate; continue; @@ -495,6 +506,7 @@ const parseOutputArguments = ( ok: false, help: false, format, + code: "VALIDATION_ERROR", message: `Unknown mcp doctor option: ${argument ?? "missing"}`, }; } diff --git a/tests/boundary/cli/mcpDoctorHelpCli.test.ts b/tests/boundary/cli/mcpDoctorHelpCli.test.ts index d839616d72..7ca04e7ccb 100644 --- a/tests/boundary/cli/mcpDoctorHelpCli.test.ts +++ b/tests/boundary/cli/mcpDoctorHelpCli.test.ts @@ -83,4 +83,34 @@ describe("MCP doctor help boundary", () => { }, CLI_INTEGRATION_TIMEOUT_MS, ); + + workspaceCliTest( + "reports an absent or empty --format value as a missing output value", + async ({ cli }) => { + const message = + "--format requires a value, as in --format or --format=."; + for (const arguments_ of [ + ["mcp", "doctor", "--format"], + ["mcp", "doctor", "--format="], + ]) { + const result = await cli.run({ arguments: arguments_ }); + expect(result).toMatchObject({ + exitCode: 1, + stdout: `code: MISSING_OUTPUT_VALUE\nmessage: "${message}"\n`, + }); + } + for (const arguments_ of [ + ["mcp", "doctor", "--json", "--format"], + ["mcp", "doctor", "--json", "--format="], + ]) { + const result = await cli.run({ arguments: arguments_ }); + expect(result.exitCode).toBe(1); + expect(JSON.parse(result.stdout)).toEqual({ + code: "MISSING_OUTPUT_VALUE", + message, + }); + } + }, + CLI_INTEGRATION_TIMEOUT_MS, + ); });