stdio_server: text a tool prints still reaches the client, because sys.stdout is flushed after fd 1 is restored
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- python
- Domain
- backend-api-design
Research direction
Start by reading the stdio_server() implementation and tracing where fd 1 is diverted and restored; the issue does not name a file or test. Reproduce the reported case with a tool that calls print() while stdout is piped, then check that pending text goes to stderr before fd 1 is restored and does not appear as a JSON-RPC message.
Written by the indexing model from the issue text.
Description
Summary
Since #3117, stdio_server() points fd 1 at stderr while it serves, so that stray output from handlers misses the wire. Text written with print() still reaches the client. It goes into sys.stdout's own buffer, which is block-buffered when stdout is a pipe. That buffer is flushed when the interpreter exits, after stdio_server() has given fd 1 back to the client's pipe. The client then reads the text as a JSON-RPC message.
Reproduction
mcp 2.3.0, Python 3.13.16, macOS 26.3.
import asyncio
import sys
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.server.mcpserver import MCPServer
if sys.argv[1:] == ["server"]:
server = MCPServer("repro")
@server.tool()
def noisy() -> str:
print("stray output from a tool") # e.g. a library that prints
return "done"
server.run("stdio")
else:
async def main():
params = StdioServerParameters(command=sys.executable, args=[__file__, "server"])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("noisy", {})
print("result:", result.content[0].text)
asyncio.run(main())
Running python repro.py, the call succeeds, and the client also logs:
pydantic_core._pydantic_core.ValidationError: 1 validation error for union[JSONRPCRequest,JSONRPCNotification,JSONRPCResponse,JSONRPCError]
Invalid JSON: expected value at line 1 column 1 [type=json_invalid, input_value='stray output from a tool', input_type=str]
Cause
While fd 1 is diverted, the printed text never reaches it: it sits in sys.stdout's buffer. The buffer is written out at interpreter exit, when fd 1 is the protocol pipe again. Adding sys.stdout.flush() right after the print() in the tool removes the error, and the text appears on stderr, which confirms this.
Suggestion
Flush sys.stdout before restoring fd 1, so that pending text goes to the diversion. Making sys.stdout line-buffered while serving (sys.stdout.reconfigure(line_buffering=True)) would also send printed text to stderr when it is printed, rather than at exit.
As a workaround we call sys.stdout.reconfigure(line_buffering=True) before server.run("stdio") and flush after each tool.
- Dominant language
- Python
- Stars
- 24.7k
- Forks
- 4.1k
- Avg merge
- 9h 45m
- Merged PRs (30d)
- 16
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/python-sdk
-
bug v2
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
modelcontextprotocol/python-sdk#3811 · 1 comment ·
Maintainers usually reply within 1 day
-
bug v2
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
modelcontextprotocol/python-sdk#3810 · 1 comment ·
Maintainers usually reply within 1 day
-
bug v1 v2
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
modelcontextprotocol/python-sdk#3781 · 2 comments ·
Maintainers usually reply within 1 day
-
[Bug]: Inconsistent structured output wrapping and opt-out for typing.Dict and typing sequence generics in func_metadataMay be free again A pull request for this issue was closed without being merged. Openbug v1 v2
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
modelcontextprotocol/python-sdk#3771 · 3 comments ·
Maintainers usually reply within 1 day
-
bug v2
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
modelcontextprotocol/python-sdk#3769 · 2 comments ·
Maintainers usually reply within 1 day
All issues in modelcontextprotocol/python-sdk
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
jejjohnson/gaussx#752 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
521xueweihan/HelloGitHub#4270 ·
-
enhancement owner: den-haag
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
Maintainers usually reply within 6 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ok-oldking/ok-wuthering-waves#1828 ·
Maintainers usually reply within 1 day
-
lesson-submission pending-review
Difficulty 1/5 Under an hour Newbie friendliness 65/100
Ikalus1988/MisakaNet#4512 ·
Maintainers usually reply within 1 day