You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/run/index.md
+24Lines changed: 24 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,6 +41,30 @@ Nothing prints, and it doesn't return. It is waiting on stdin for a host to spea
41
41
42
42
That also means stdout **is the wire**. A stray `print()` corrupts the stream; the `logging` module writes to stderr and is the right tool. That story is in **[Logging](../handlers/logging.md)**.
43
43
44
+
On Windows, the same rule applies to child processes your tools start. A child
45
+
that inherits the stdio server's stdin can block behind the server's protocol
46
+
reader. If your tool starts a subprocess and you do not intend to feed it input,
47
+
redirect the child's stdin:
48
+
49
+
```python
50
+
import asyncio
51
+
import subprocess
52
+
import sys
53
+
54
+
55
+
asyncdefrun_script() -> tuple[bytes, bytes]:
56
+
process =await asyncio.create_subprocess_exec(
57
+
sys.executable,
58
+
"script.py",
59
+
stdin=subprocess.DEVNULL,
60
+
stdout=subprocess.PIPE,
61
+
stderr=subprocess.PIPE,
62
+
)
63
+
returnawait process.communicate()
64
+
```
65
+
66
+
The matching troubleshooting entry is **[My stdio tool hangs when it starts a subprocess on Windows](../troubleshooting.md#my-stdio-tool-hangs-when-it-starts-a-subprocess-on-windows)**.
Copy file name to clipboardExpand all lines: docs/troubleshooting.md
+35Lines changed: 35 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -141,6 +141,41 @@ There is no error string for this, which is exactly why it is hard to search. Th
141
141
142
142
An "invalid" tool name is *not* on that list: a non-conforming name logs a warning but the tool is registered and listed anyway.
143
143
144
+
## My stdio tool hangs when it starts a subprocess on Windows
145
+
146
+
Your server is running over `stdio`, and a tool starts another process with
147
+
`asyncio.create_subprocess_exec`, `asyncio.create_subprocess_shell`, or
148
+
`subprocess.Popen`. The tool call never returns on Windows, while the same code
149
+
works over an HTTP transport.
150
+
151
+
The child inherited the server's stdin. In a stdio server, stdin is the protocol
152
+
pipe and the server is already waiting on it for the next JSON-RPC message. A
153
+
Python child process on Windows can block during startup when it inherits that
154
+
same pipe.
155
+
156
+
If you do not intend to send input to the child, redirect its stdin:
157
+
158
+
```python
159
+
import asyncio
160
+
import subprocess
161
+
import sys
162
+
163
+
164
+
asyncdefrun_script() -> tuple[bytes, bytes]:
165
+
process =await asyncio.create_subprocess_exec(
166
+
sys.executable,
167
+
"script.py",
168
+
stdin=subprocess.DEVNULL,
169
+
stdout=subprocess.PIPE,
170
+
stderr=subprocess.PIPE,
171
+
)
172
+
returnawait process.communicate()
173
+
```
174
+
175
+
Use the same idea with `subprocess.Popen(..., stdin=subprocess.DEVNULL)`. Also
176
+
capture or redirect the child's stdout. The stdio server's stdout is the MCP
177
+
wire, so a child that writes there can corrupt the connection.
178
+
144
179
## `MCPError: Server returned an error response`
145
180
146
181
The server refused the HTTP request outright, with a body that is not JSON-RPC, so the python `Client` has nothing better to show you than this stand-in.
0 commit comments