Skip to content

Fix: WP_CLI::line() in serve command corrupts JSON-RPC stdout stream#194

Open
graham73may wants to merge 1 commit into
WordPress:trunkfrom
graham73may:fix/stdio-serve-stdout-pollution
Open

Fix: WP_CLI::line() in serve command corrupts JSON-RPC stdout stream#194
graham73may wants to merge 1 commit into
WordPress:trunkfrom
graham73may:fix/stdio-serve-stdout-pollution

Conversation

@graham73may

Copy link
Copy Markdown

Problem

When wp mcp-adapter serve is called without a --server flag, it falls back to the first registered server and calls WP_CLI::line() to report the selection:

\WP_CLI::line( sprintf( 'Using server: %s', $server_id ) );

WP_CLI::line() writes to stdout. The serve command communicates with MCP clients over STDIO using the JSON-RPC 2.0 protocol, so anything written to stdout before the first { is treated as part of the protocol stream. This causes the client's JSON parser to fail immediately.

Real-world impact: Any MCP client using STDIO transport (Claude Code, VS Code Copilot, Cursor, etc.) that omits --server will fail to connect. Workarounds require piping output through grep '^{' to strip the non-JSON line.

Fix

Change WP_CLI::line() to WP_CLI::debug(). Debug output goes to stderr and is suppressed by default, so:

  • Normal usage is unaffected
  • The message is still visible when running with --debug
  • The JSON-RPC stream on stdout is clean

Change

includes/Cli/McpCommand.php line 78:

- \WP_CLI::line( sprintf( 'Using server: %s', $server_id ) );
+ \WP_CLI::debug( sprintf( 'Using server: %s', $server_id ) );

When no --server flag is passed, the serve command called WP_CLI::line()
to report which server it selected. WP_CLI::line() writes to stdout,
which corrupts the JSON-RPC stream and breaks any MCP client using STDIO
transport (Claude Code, VS Code, Cursor, etc.).

WP_CLI::debug() writes to stderr and is suppressed by default, so it
has no impact on normal usage but remains visible with --debug.
Copilot AI review requested due to automatic review settings May 21, 2026 07:36
@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @gmay-sbx.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: gmay-sbx.

Co-authored-by: webmyc <urbankidro@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

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

Fixes a protocol-breaking stdout write in the wp mcp-adapter serve command when no --server flag is provided, ensuring the JSON-RPC 2.0 STDIO stream remains clean for MCP clients.

Changes:

  • Replace WP_CLI::line() (stdout) with WP_CLI::debug() (stderr) when auto-selecting the first registered server.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.85%. Comparing base (7cc42a0) to head (00a02e5).

Additional details and impacted files
@@             Coverage Diff              @@
##              trunk     #194      +/-   ##
============================================
- Coverage     88.45%   87.85%   -0.60%     
  Complexity     1243     1243              
============================================
  Files            53       53              
  Lines          4035     4035              
============================================
- Hits           3569     3545      -24     
- Misses          466      490      +24     
Flag Coverage Δ
unit 87.85% <100.00%> (-0.60%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@webmyc

webmyc commented May 21, 2026

Copy link
Copy Markdown

Thanks for this. Inhale (the wp.org settings page i shipped on top of the Abilities API + MCP Adapter) had users hitting the corrupted-stream bug on every fresh wp mcp-adapter serve connection. The one-line stderr move is exactly right for STDIO.

webmyc added a commit to respira-press/inhale-mcp-abilities that referenced this pull request May 21, 2026
Snippet in the Connection section pointed at a non-existent subcommand:
`wp mcp stdio` is not a registered command on the upstream MCP Adapter.
The McpCommand class only registers `serve` and `list`. Anyone copying
the snippet into Claude Desktop hit "command not found" on first run.

This patch makes three changes to the STDIO snippet:

  1. `mcp` + `stdio` → `mcp-adapter` + `serve` (canonical subcommand
     per the upstream README + cli-usage.md guide).
  2. Add `--user=admin` to the args. Without it the serve process runs
     as the OS user (root/www-data) and can't see admin-scoped abilities.
  3. New line above the snippet noting that STDIO transport requires
     WordPress and the MCP client to run on the same machine. The HTTP
     transport snippet below is the correct path for remote sites.

Surfaced while reviewing WordPress/mcp-adapter#194 (a separate fix for
stdout corruption in the `serve` fallback path). Inhale's snippet always
passes --server explicitly so it does not hit #194's buggy fallback; the
fixes in this release are independent.

No change to the abilities filter, the settings page UI, or any stored
option. Safe upgrade.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants