Windows: MCP servers with heavy stderr output fail with "Transport closed" error
What version of Codex is running?
codex-cli 0.63.0
What subscription do you have?
Pro
Which model were you using?
gpt-5.1-codex-max
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64
What issue are you seeing?
MCP servers that produce heavy stderr output during tool execution fail with "Transport closed" errors on Windows. This affects servers like bc-code-intelligence-mcp but not servers with minimal stderr like @modelcontextprotocol/server-filesystem.
Error Message:
[WARN] MCP tool call error: "tool call error: tool call failed for `bc-code-intel/set_workspace_info`
Caused by: tools/call failed: Transport closed"
Key Evidence:
- bc-code-intel works perfectly when tested manually via stdio
- bc-code-intel works in Claude Code, GitHub Copilot, and Cursor
- filesystem MCP works perfectly in Codex (minimal stderr)
- bc-code-intel ONLY fails in Codex on Windows with heavy stderr (189-237ms of output)
What steps can reproduce the bug?
- Configure bc-code-intelligence-mcp in
config.toml:
[mcp_servers.bc-code-intel]
command = "node"
args = ["C:\\path\\to\\bc-code-intelligence-mcp\\dist\\index.js"]
startup_timeout_sec = 60
- Start Codex and call
set_workspace_infotool - Server processes successfully (logs show completion: "📊 Total: 123 topics, 16 specialists")
- Error occurs ~7ms after server completes: "Transport closed"
Manual Test (WORKS):
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"set_workspace_info","arguments":{"workspace_root":"C:/test","available_mcps":["bc-code-intel"]}}}' | node dist/index.js
# Returns valid JSON-RPC response successfully
What is the expected behavior?
MCP servers should work regardless of stderr output volume. The server should complete tool execution and return results without "Transport closed" errors.
Root Cause Analysis
Location: codex-rs/rmcp-client/src/rmcp_client.rs lines 133-149
The Bug: The stderr reading task blocks indefinitely on lines().next_line().await without timeout handling. When MCP servers produce heavy stderr output (like bc-code-intel's 189-237ms of loading logs), this interferes with the stdio transport on Windows.
Code:
if let Some(stderr) = stderr {
tokio::spawn(async move {
let mut reader = BufReader::new(stderr).lines();
loop {
match reader.next_line().await { // ← NO TIMEOUT
Ok(Some(line)) => {
info!("MCP server stderr ({program_name}): {line}");
}
Ok(None) => break,
Err(error) => {
warn!("Failed to read MCP server stderr ({program_name}): {error}");
break;
}
}
}
});
}
Smoking Gun: This exact pattern was already fixed in commit 73ed30d7e ("Avoid hang when tool's process spawns grandchild that shares stderr/stdout") for shell tool execution in codex-rs/core/src/exec.rs lines 584-625, but was never applied to MCP client code.
Proposed Fix (UNTESTED)
Note: This fix is based on code analysis and the existing pattern in exec.rs, but has NOT been tested locally due to build environment requirements. The Codex team should validate this approach.
Apply the same timeout pattern from exec.rs to prevent stderr blocking:
if let Some(stderr) = stderr {
tokio::spawn(async move {
use tokio::time::{timeout, Duration};
let mut reader = BufReader::new(stderr).lines();
loop {
match timeout(Duration::from_secs(5), reader.next_line()).await {
Ok(Ok(Some(line))) => {
info!("MCP server stderr ({program_name}): {line}");
}
Ok(Ok(None)) => break,
Ok(Err(error)) => {
warn!("Failed to read MCP server stderr ({program_name}): {error}");
break;
}
Err(_) => {
warn!("MCP server stderr ({program_name}): read timeout after 5s, continuing...");
}
}
}
});
}
Additional Information
Testing Done:
- ✅ Verified bc-code-intel works with manual stdio testing
- ✅ Tested with SDK 0.4.0 and 1.22.0 - same issue
- ✅ Confirmed filesystem MCP works in Codex (low stderr)
- ✅ Isolated issue to Codex's stderr handling, not bc-code-intel
- ❌ Proposed fix NOT tested locally (build environment setup incomplete)
Files Involved:
codex-rs/rmcp-client/src/rmcp_client.rs- needs fixcodex-rs/core/src/exec.rs- reference implementation
Related Commits:
73ed30d7e- Fixed similar issue for shell toolsf828cd289- Windows MCP server execution fix
This is a Windows-specific race condition in MCP client stderr handling that should be fixed to support MCP servers with verbose diagnostic output.
5 Comments
I've also encountered this problem. Codex is truly disappointing, a bunch of idiots!
Hopefully Codex will fix this problem ASAP, this is truely disappointing.
The problem still exist in Codex VS Extension v0.4.71 and Codex CLI v0.98.0
cc @gpeal
Based on the symptoms described, this looks like a Windows named pipe buffer deadlock — a well-documented issue with child process I/O on Windows.
Likely root cause (hypothesis, not confirmed from source):
When Codex CLI spawns an MCP server via stdio, it reads stdout for JSON-RPC messages. If stderr is not actively drained by the parent process, and the MCP server writes heavy stderr output (as bc-code-intelligence-mcp does with 189-237ms of logs), the Windows pipe buffer (~4KB default) fills up. Unlike Unix, Windows pipe buffer exhaustion can stall the entire child process or cause the OS to tear down associated pipes — which would explain the "Transport closed" error appearing ~7ms after the server finishes processing.
This would also explain why filesystem MCP (minimal stderr) works fine, and why the same server works in Claude Code, Copilot, and Cursor — those clients likely drain stderr independently.
User workaround — redirect stderr to NUL:
This prevents stderr from accumulating in the pipe buffer entirely. The downside is you lose server debug logs — if you need them, redirect to a file instead:
2>C:\\temp\\mcp-stderr.logNote for Codex maintainers: The stdio transport should spawn MCP server child processes with stderr set to
'pipe'and actively consume it (e.g.,child.stderr.resume()) rather than inheriting or ignoring the stream. This is the standard fix for Windows pipe deadlocks in Node.js child_process.---
<sub>Confucius Debug — community knowledge base for AI agent bugs. Free to search via MCP.</sub>
Related, but I do not think this is a duplicate of #7155.
I just filed #18977 for a different
Transport closedfailure mode on macOS:stdin_end/stdin_close/transport_closeSo the overlap is the user-visible symptom (
Transport closed), but the suspected trigger is different:Linking the two in case the maintainers see a shared host-side weakness around MCP process / transport management:
https://github.com/openai/codex/issues/18977