Skip to content

Commit 59848b1

Browse files
committed
fix: use os.dup to prevent closing real stdio after server exits
When using transport="stdio", the server wraps sys.stdin.buffer and sys.stdout.buffer with TextIOWrapper. When these wrappers are closed, they also close the underlying sys.stdin/sys.stdout, causing subsequent stdio operations to fail with 'ValueError: I/O operation on closed file'. This fix uses os.dup() to create duplicate file descriptors, ensuring that closing our wrappers doesn't affect the original process streams. Fixes #1933
1 parent 16969be commit 59848b1

File tree

1 file changed

+10
-2
lines changed

1 file changed

+10
-2
lines changed

src/mcp/server/stdio.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ async def run_server():
1717
```
1818
"""
1919

20+
import os
2021
import sys
2122
from contextlib import asynccontextmanager
2223
from io import TextIOWrapper
@@ -38,10 +39,17 @@ async def stdio_server(stdin: anyio.AsyncFile[str] | None = None, stdout: anyio.
3839
# standard process handles. Encoding of stdin/stdout as text streams on
3940
# python is platform-dependent (Windows is particularly problematic), so we
4041
# re-wrap the underlying binary stream to ensure UTF-8.
42+
# Use dup to create copies of stdin/stdout file descriptors so that closing
43+
# our wrappers doesn't close the original process streams. This prevents
44+
# "ValueError: I/O operation on closed file" after the server exits.
4145
if not stdin:
42-
stdin = anyio.wrap_file(TextIOWrapper(sys.stdin.buffer, encoding="utf-8"))
46+
stdin_fd = os.dup(sys.stdin.fileno())
47+
stdin_bin = os.fdopen(stdin_fd, "rb", closefd=True)
48+
stdin = anyio.wrap_file(TextIOWrapper(stdin_bin, encoding="utf-8"))
4349
if not stdout:
44-
stdout = anyio.wrap_file(TextIOWrapper(sys.stdout.buffer, encoding="utf-8"))
50+
stdout_fd = os.dup(sys.stdout.fileno())
51+
stdout_bin = os.fdopen(stdout_fd, "wb", closefd=True)
52+
stdout = anyio.wrap_file(TextIOWrapper(stdout_bin, encoding="utf-8"))
4553

4654
read_stream: MemoryObjectReceiveStream[SessionMessage | Exception]
4755
read_stream_writer: MemoryObjectSendStream[SessionMessage | Exception]

0 commit comments

Comments
 (0)