fix(stdio): handle bufferless text streams in fallback
Summary
Fixes a crash in stdio_server() when sys.stdin/sys.stdout are replaced with bufferless text streams (e.g., io.StringIO).
The _claim_fd function's fallback path unconditionally dereferences stream.buffer, but _is_backed_by_fd returns False when a stream has no .buffer attribute. This raises AttributeError before the transport serves a single message.
This is a distinct issue from the original #1933 (which was fixed by #3117). The original issue was about the wrapper closing the real stdio handles; this is about bufferless text streams (like io.StringIO) crashing the fallback path.
Changes
-
src/mcp/server/stdio.py: Check for the
bufferattribute in_claim_fd's fallback path; returnNonewhen the stream is bufferless. The caller (stdio_server) then wraps the text stream in place since there is no binary layer to re-encode. -
tests/server/test_stdio.py: Added
test_stdio_server_serves_bufferless_text_streams_in_placewhich monkeypatchessys.stdin/sys.stdouttoio.StringIOand verifies the transport serves messages correctly.
Test results
- All 5971 tests pass
- New test covers the regression case
- Ruff and pyright clean
Disclosure
Written with AI assistance, reviewed and verified by me.
Fixes #1933