From b5ffc161ec0335b5bcf049b641a9602ba41eb0d4 Mon Sep 17 00:00:00 2001 From: Alberto Farah Date: Sat, 7 Mar 2026 08:36:40 +0000 Subject: [PATCH] fix: preserve stdin/stdout file descriptors after server exits Using os.dup() to create copies of file descriptors prevents closing the original sys.stdin/sys.stdout when the TextIOWrapper is closed. Fixes: https://github.com/modelcontextprotocol/python-sdk/issues/1933 --- src/mcp/server/stdio.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mcp/server/stdio.py b/src/mcp/server/stdio.py index e526bab56..89f6f8d1e 100644 --- a/src/mcp/server/stdio.py +++ b/src/mcp/server/stdio.py @@ -17,6 +17,7 @@ async def run_server(): ``` """ +import os import sys from contextlib import asynccontextmanager from io import TextIOWrapper @@ -38,10 +39,16 @@ async def stdio_server(stdin: anyio.AsyncFile[str] | None = None, stdout: anyio. # standard process handles. Encoding of stdin/stdout as text streams on # python is platform-dependent (Windows is particularly problematic), so we # re-wrap the underlying binary stream to ensure UTF-8. + # Use os.dup() to create copies of file descriptors to prevent closing + # the original sys.stdin/sys.stdout when the wrappers are closed. if not stdin: - stdin = anyio.wrap_file(TextIOWrapper(sys.stdin.buffer, encoding="utf-8")) + stdin_fd = os.dup(sys.stdin.fileno()) + stdin_bin = os.fdopen(stdin_fd, "rb", closefd=True) + stdin = anyio.wrap_file(TextIOWrapper(stdin_bin, encoding="utf-8")) if not stdout: - stdout = anyio.wrap_file(TextIOWrapper(sys.stdout.buffer, encoding="utf-8")) + stdout_fd = os.dup(sys.stdout.fileno()) + stdout_bin = os.fdopen(stdout_fd, "wb", closefd=True) + stdout = anyio.wrap_file(TextIOWrapper(stdout_bin, encoding="utf-8")) read_stream: MemoryObjectReceiveStream[SessionMessage | Exception] read_stream_writer: MemoryObjectSendStream[SessionMessage | Exception]