Skip to content

fix: disable newline translation in stdio TextIOWrapper to prevent CRLF on Windows#2552

Open
Maanik23 wants to merge 1 commit intomodelcontextprotocol:mainfrom
Maanik23:fix/stdio-windows-crlf
Open

fix: disable newline translation in stdio TextIOWrapper to prevent CRLF on Windows#2552
Maanik23 wants to merge 1 commit intomodelcontextprotocol:mainfrom
Maanik23:fix/stdio-windows-crlf

Conversation

@Maanik23
Copy link
Copy Markdown

@Maanik23 Maanik23 commented May 7, 2026

Fixes #2433

Summary

On Windows, TextIOWrapper without newline="" performs universal newline translation (\n\r\n on write), corrupting the newline-delimited JSON wire format used by MCP's stdio transport.

Root Cause

mcp/server/stdio.py creates TextIOWrapper(sys.stdout.buffer, encoding="utf-8") without specifying newline="". On Windows, the default newline=None causes \n\r\n translation, so every JSON-RPC message written to stdout ends with \r\n instead of \n.

The comment on line 43 even acknowledges: "Encoding of stdin/stdout as text streams on python is platform-dependent (Windows is particularly problematic)" — but the fix applied (re-wrap to ensure UTF-8) didn't also fix the newline translation mode.

Changes

src/mcp/server/stdio.py

  • Added newline="" to both the stdin and stdout TextIOWrapper calls
  • newline="" disables universal newline translation while still operating in text mode
  • This ensures bare \n is emitted on all platforms, matching the NDJSON wire format

tests/server/test_stdio.py

  • Added test_stdio_server_no_crlf_on_windows — exercises the default sys.stdout.buffer code path via monkeypatch, inspects raw bytes in the output buffer, and asserts no \r bytes are present

Verification

# With newline="":
buf = io.BytesIO()
wrapper = TextIOWrapper(buf, encoding="utf-8", newline="")
wrapper.write('{"jsonrpc":"2.0","result":"ok"}\n')
wrapper.flush()
repr(buf.getvalue())
# b'{"jsonrpc":"2.0","result":"ok"}\n'  ← correct on all platforms

Compatibility

  • No behavioral change on Linux/macOS (where \n was already preserved)
  • On Windows, fixes the \r\n corruption without any new dependencies
  • The stdin wrapper also gets newline="" to prevent future issues if a client sends strict LF

On Windows, TextIOWrapper without newline=" performs universal newline
translation (\n -> \r\n on write), corrupting the newline-delimited JSON
wire format used by MCP's stdio transport.

Add newline=" to both the stdin and stdout TextIOWrapper calls in
stdio_server() to disable translation on all platforms.

Add a regression test that inspects the raw bytes written to the output
buffer and asserts no \r bytes are present.

Fixes modelcontextprotocol#2433
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

1 participant