Skip to content

Commit 2e949f8

Browse files
committed
Fix stdio transport closing sys.stdin.buffer/sys.stdout.buffer on exit
When using transport='stdio', anyio.wrap_file() wraps a TextIOWrapper around sys.stdin.buffer/sys.stdout.buffer. When the server exits and the async file is closed, TextIOWrapper.close() propagates to close the underlying buffer, making subsequent stdio operations fail with: ValueError: underlying buffer has been closed This adds a _DetachingTextIOWrapper that detaches from the buffer on close, preventing the propagation. The original comment in the code stated 'Purposely not using context managers for these, as we don't want to close standard process handles' — this fix aligns the implementation with that intent. Fixes #1933
1 parent 616476f commit 2e949f8

1 file changed

Lines changed: 13 additions & 2 deletions

File tree

src/mcp/server/stdio.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ async def run_server():
2121
from contextlib import asynccontextmanager
2222
from io import TextIOWrapper
2323

24+
25+
class _DetachingTextIOWrapper(TextIOWrapper):
26+
"""TextIOWrapper that detaches from its buffer on close, preventing
27+
the underlying buffer (e.g. sys.stdin.buffer) from being closed."""
28+
29+
def close(self):
30+
try:
31+
self.detach()
32+
except (ValueError, OSError):
33+
pass
34+
2435
import anyio
2536
import anyio.lowlevel
2637

@@ -39,9 +50,9 @@ async def stdio_server(stdin: anyio.AsyncFile[str] | None = None, stdout: anyio.
3950
# python is platform-dependent (Windows is particularly problematic), so we
4051
# re-wrap the underlying binary stream to ensure UTF-8.
4152
if not stdin:
42-
stdin = anyio.wrap_file(TextIOWrapper(sys.stdin.buffer, encoding="utf-8", errors="replace"))
53+
stdin = anyio.wrap_file(_DetachingTextIOWrapper(sys.stdin.buffer, encoding="utf-8", errors="replace"))
4354
if not stdout:
44-
stdout = anyio.wrap_file(TextIOWrapper(sys.stdout.buffer, encoding="utf-8"))
55+
stdout = anyio.wrap_file(_DetachingTextIOWrapper(sys.stdout.buffer, encoding="utf-8"))
4556

4657
read_stream_writer, read_stream = create_context_streams[SessionMessage | Exception](0)
4758
write_stream, write_stream_reader = create_context_streams[SessionMessage](0)

0 commit comments

Comments
 (0)