fix(stdio): handle bufferless text streams in fallback

Closed
SI
23d ago
+57-4

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 buffer attribute in _claim_fd's fallback path; return None when 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_place which monkeypatches sys.stdin/sys.stdout to io.StringIO and 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