Skip to content

fix(stdio): bidi transport closes on parse error; strip UTF-8 BOM#797

Merged
joshrotenberg merged 1 commit intomainfrom
fix/stdio-parse-error
May 7, 2026
Merged

fix(stdio): bidi transport closes on parse error; strip UTF-8 BOM#797
joshrotenberg merged 1 commit intomainfrom
fix/stdio-parse-error

Conversation

@joshrotenberg
Copy link
Copy Markdown
Owner

Summary

Two related fixes in transport/stdio.rs:

1. Bidirectional transport tears down on parse errors

BidirectionalStdioTransport::handle_incoming_message propagated serde_json errors via ?, which killed the entire run loop on a single malformed line. The peer couldn't tell parse failure from a normal stream close, and any subsequent valid input was lost.

Now both serde_json::from_str call sites catch the error, emit a -32700 Parse error JSON-RPC response with id = null, and continue reading. Mirrors the existing behavior on the unidirectional StdioTransport::run path. Extracted a small write_parse_error helper to keep the two error sites consistent.

2. UTF-8 BOM stripping

Added a clean_input_line helper that strips a leading \u{feff} BOM, then trims whitespace. Wired into all four stdio read sites:

  • StdioTransport::run
  • GenericStdioTransport::process_input
  • SyncStdioTransport::run_blocking
  • BidirectionalStdioTransport::run

Windows tools that emit a BOM-prefixed first line now parse correctly across all transports.

Mirrors rmcp #833.

Tests

5 new tests in stdio::tests:

  • test_clean_input_line_no_bom
  • test_clean_input_line_strips_leading_bom
  • test_clean_input_line_strips_bom_then_trims
  • test_clean_input_line_does_not_strip_internal_bom
  • test_clean_input_line_empty
  • test_process_line_with_bom_stripped_input_parses (BOM round-trip)

Closes #795.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test -p tower-mcp --lib --all-features
  • cargo test -p tower-mcp --test '*' --all-features
  • cargo doc --no-deps --all-features

BidirectionalStdioTransport::handle_incoming_message previously
propagated serde_json errors via `?`, tearing down the run loop on
a single malformed line. Per JSON-RPC 2.0, malformed input should
produce a -32700 Parse error response (id = null) and the transport
should keep reading. The unidirectional StdioTransport already did
this; bring the bidirectional path to parity.

While here, add a clean_input_line helper that strips a leading
UTF-8 BOM and trims whitespace, and wire it into all four stdio read
sites (StdioTransport, GenericStdioTransport, SyncStdioTransport,
BidirectionalStdioTransport). Windows tools that emit a BOM-prefixed
first line now parse correctly across all transports.

Closes #795
@joshrotenberg joshrotenberg merged commit 8233b79 into main May 7, 2026
18 checks passed
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.

fix(stdio): bidi transport closes on parse error; add UTF-8 BOM strip

1 participant