Skip to content

fix: stop newline translation corrupting stdio framing on Windows#3132

Open
bmdhodl wants to merge 1 commit into
modelcontextprotocol:mainfrom
bmdhodl:fix-2433-stdio-crlf
Open

fix: stop newline translation corrupting stdio framing on Windows#3132
bmdhodl wants to merge 1 commit into
modelcontextprotocol:mainfrom
bmdhodl:fix-2433-stdio-crlf

Conversation

@bmdhodl

@bmdhodl bmdhodl commented Jul 19, 2026

Copy link
Copy Markdown

Summary

Fixes #2433. Supersedes stale #2552 (credit to its author for the direction).

stdio_server() wraps sys.stdout.buffer in a TextIOWrapper with default newline handling, which translates \n to os.linesep on write. On Windows every outgoing frame is terminated with \r\n, breaking the newline-delimited JSON framing required by the spec.

Change

newline="" on the stdout wrapper only. Write-side frames must be spec-exact (\n-delimited); stdin keeps universal-newline reading so CRLF from clients stays tolerated (liberal in what we accept).

How Has This Been Tested?

Reproduced the bug natively on Windows 11 (Python 3.13, current main) with a raw-bytes client against a real stdio server subprocess: frames arrived as b'...}\r\n' before the fix, b'...}\n' after.

The new regression test asserts on the raw bytes written to stdout, so the platform's newline translation itself is under test. Verified red/green on native Windows: fails on main with b'{"jsonrpc":"2.0","id":1,"result":{}}\r\n', passes with the fix. It runs on the windows-latest unit-test CI cell, which currently has no byte-level framing coverage (the test in #2552 asserted endswith(b"\n"), which cannot detect CRLF).

Local gate on Windows: full suite 5438 passed / 15 skipped / 1 xfailed, ruff check + ruff format clean, pyright (pinned 1.1.405) 0 errors.

Breaking Changes

None. Output on POSIX platforms is byte-identical; Windows output changes from out-of-spec \r\n framing to the spec's \n.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

stdio_server() wrapped sys.stdout.buffer in a TextIOWrapper with default
newline handling, which translates "\n" to os.linesep on write. On
Windows every outgoing frame was terminated with "\r\n", breaking the
newline-delimited JSON framing required by the spec.

Fix: newline="" on the stdout wrapper only. Outgoing frames must be
spec-exact; stdin keeps universal-newline reading so CRLF from clients
stays tolerated.

The regression test asserts on the raw bytes written to stdout, so the
platform's newline translation itself is under test: it fails on main on
Windows (b'...}\r\n') and passes with the fix. Runs on the
windows-latest CI cell.

Fixes modelcontextprotocol#2433

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 19, 2026 21:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

Re-trigger cubic

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.

Windows: TextIOWrapper in stdio_server() emits CRLF instead of LF, corrupting newline-delimited JSON messages

2 participants