Skip to content

Route CLI parse-error help to stderr - #3177

Open
Pybsama wants to merge 2 commits into
microsoft:mainfrom
Pybsama:codex/mcp-3098-stderr
Open

Route CLI parse-error help to stderr#3177
Pybsama wants to merge 2 commits into
microsoft:mainfrom
Pybsama:codex/mcp-3098-stderr

Conversation

@Pybsama

@Pybsama Pybsama commented Jul 29, 2026

Copy link
Copy Markdown

What does this PR do?

Routes Azure MCP command-line parse failures to stderr so stdout stays reserved for MCP JSON-RPC messages.

The root cause had two parts:

  • CustomHelpAction wrote directly to Console, bypassing the output writer selected by System.CommandLine.
  • Azure MCP invoked failed parse results with the default invocation configuration, which sent generated help to stdout.

This PR makes the shared custom help action honor InvocationConfiguration.Output and configures both the output and error writers as stderr only when parsing fails. Successful invocations keep the default stream routing, so explicit --help continues to exit successfully with help on stdout and valid server start JSON-RPC framing is unchanged.

Process-level tests cover both bare invocation and explicit help, and a bug-fix changelog entry records the user-visible correction.

GitHub issue number?

Fixes #3098

Validation

  • TDD regression: the bare-invocation test failed before the implementation because stdout contained the root help, then passed after the fix.
  • dotnet build servers/Azure.Mcp.Server/src/Azure.Mcp.Server.csproj — passed with zero errors.
  • dotnet test servers/Azure.Mcp.Server/tests/Azure.Mcp.Server.Tests/Azure.Mcp.Server.Tests.csproj --filter "FullyQualifiedName~CliOutputTests" — passed 2/2.
  • dotnet test servers/Azure.Mcp.Server/tests/Azure.Mcp.Server.Tests/Azure.Mcp.Server.Tests.csproj --filter "FullyQualifiedName~ServerModeCoverageTests.SingleMode_Should_List_Tools_Without_Initialize" — passed 1/1 and retained JSON-RPC output on stdout.
  • Targeted dotnet format --verify-no-changes checks for all modified C# files — passed.
  • Compile-Changelog.ps1 -DryRun — passed without changing the generated changelog.
  • Invoke-Cspell.ps1 and git diff --check — passed.
  • Direct executable verification:
    • bare invocation: exit 1, stdout 0 lines, stderr 300 lines;
    • --help: exit 0, stdout 298 lines, stderr 0 lines.

Local restore note: the configured Azure Artifacts feed returned 401 for packages that were not already cached, and the repository's currently pinned dependency graph reports NU1903. The targeted local restore/build/test pass therefore used NuGet.org as a command-line package source with NuGetAudit=false. No dependencies or nuget.config files were changed.

Pre-merge Checklist

  • Required for All PRs
    • Read contribution guidelines
    • PR title clearly describes the change
    • Commit history is clean with descriptive messages (cleanup guide)
    • Added comprehensive tests for new/modified functionality
    • Added the required bug-fix changelog entry
  • For MCP tool changes — not applicable; this changes Core CLI stream routing and does not add or modify an MCP tool.
  • Extra steps for Azure MCP Server tool changes — not applicable for the same reason.

Copilot AI review requested due to automatic review settings July 29, 2026 07:16
@Pybsama
Pybsama requested review from a team as code owners July 29, 2026 07:16
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution @Pybsama! We will review the pull request and get back to you soon.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Azure MCP CLI stdio stream hygiene by ensuring command-line parse failures (e.g., missing subcommand) emit generated help/usage and diagnostics to stderr, keeping stdout reserved for MCP JSON-RPC messages. It updates the shared help action to honor the System.CommandLine-selected output writer and adds process-level tests to lock in stdout/stderr behavior for both bare invocation and explicit --help.

Changes:

  • Route parse-error invocations through a custom InvocationConfiguration that sets both Output and Error to Console.Error.
  • Update CustomHelpAction to write via parseResult.InvocationConfiguration.Output (instead of writing directly to Console).
  • Add CLI process tests and a bug-fix changelog entry documenting the user-visible behavior change.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
servers/Azure.Mcp.Server/tests/Azure.Mcp.Server.Tests/Infrastructure/CliOutputTests.cs Adds process-level assertions for stdout/stderr routing on bare invocation vs explicit --help.
servers/Azure.Mcp.Server/src/Program.cs Introduces parse-error-only invocation configuration to route help/diagnostics to stderr.
servers/Azure.Mcp.Server/changelog-entries/pybsama-missing-command-stderr.yml Records the bug fix in changelog-entry YAML format.
core/Microsoft.Mcp.Core/src/Commands/CustomHelpAction.cs Ensures help/version output uses System.CommandLine’s configured output writer (supports stderr routing on parse errors).

@Pybsama

Pybsama commented Jul 29, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

azmcp: write missing-subcommand diagnostics to stderr

2 participants