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
+17Lines changed: 17 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,6 +41,23 @@ 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
+
process =await asyncio.create_subprocess_exec(
51
+
sys.executable,
52
+
"script.py",
53
+
stdin=subprocess.DEVNULL,
54
+
stdout=subprocess.PIPE,
55
+
stderr=subprocess.PIPE,
56
+
)
57
+
```
58
+
59
+
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
+28Lines changed: 28 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -141,6 +141,34 @@ 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
+
process =await asyncio.create_subprocess_exec(
160
+
sys.executable,
161
+
"script.py",
162
+
stdin=subprocess.DEVNULL,
163
+
stdout=subprocess.PIPE,
164
+
stderr=subprocess.PIPE,
165
+
)
166
+
```
167
+
168
+
Use the same idea with `subprocess.Popen(..., stdin=subprocess.DEVNULL)`. Also
169
+
capture or redirect the child's stdout. The stdio server's stdout is the MCP
170
+
wire, so a child that writes there can corrupt the connection.
171
+
144
172
## `MCPError: Server returned an error response`
145
173
146
174
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