macOS: stdio MCP tool calls can fail with raw `Transport closed` even when the underlying operation is healthy

Open 💬 4 comments Opened Apr 22, 2026 by alario-tang

Summary

On macOS, stdio MCP servers can fail with Transport closed even when the server logic itself is healthy and the same operation succeeds via a non-MCP parity path.

What looks wrong from the outside is that Codex's MCP host/transport lifecycle seems able to drop or replace the stdio connection in a way that leaves the tool call with a stale/closed transport handle.

In my case this showed up with local OMX MCP servers (omx_state, etc.), but the evidence suggests the more general problem is on the Codex MCP host/stdio lifecycle side, not in the specific state logic.

What version of Codex is running?

codex-cli 0.122.0

What subscription do you have?

Pro

Which model were you using?

Observed inside Codex App / Codex CLI session; model choice does not seem relevant to the failure.

What platform is your computer?

macOS (Apple Silicon)

What issue are you seeing?

Intermittent MCP tool failures like:

tool call error: tool call failed for `omx_state/state_write`

Caused by:
    Transport closed

Key observations:

  1. omx doctor reports the environment is healthy.
  2. codex mcp list shows the OMX MCP servers are enabled.
  3. The same state operations succeed via CLI parity:
  • omx state write ...
  • omx state read ...
  1. So the state logic itself is healthy; the failure is specific to the MCP tool transport path.

I also locally verified that the OMX stdio MCP server exits immediately when stdin/transport is closed, which means Codex-side transport churn can surface directly as Transport closed.

What steps can reproduce the bug?

I do not yet have a minimal reproduction using a public MCP server, but here is the concrete local evidence path:

  1. Run a Codex session with stdio MCP servers configured.
  2. Use MCP tools successfully for a while.
  3. Later in the same long-running session, a tool call may fail with Transport closed.
  4. The same operation still succeeds immediately via a direct CLI parity command outside the MCP tool path.

Additional local debugging:

  • The OMX MCP server uses StdioServerTransport and shuts down on:
  • stdin_end
  • stdin_close
  • transport_close
  • parent_gone
  • With OMX_MCP_TRANSPORT_DEBUG=1, manually closing stdin reproduces a clean server shutdown with:
[omx-state-server] transport shutdown: stdin_end
[omx-state-server] transport shutdown: exit

This does not prove Codex is always at fault, but it does show that if Codex drops/replaces the stdio transport, the MCP server will terminate and the tool call will surface as Transport closed.

What is the expected behavior?

For stdio MCP servers:

  1. Codex should avoid exposing stale/closed transport handles to tool calls where possible.
  2. If the host has restarted/replaced the MCP client process, Codex should reconnect/retry rather than surfacing a raw Transport closed immediately.
  3. At minimum, the error should distinguish between:
  • server exited intentionally
  • host transport was closed/replaced
  • handshake/startup failure
  • tool execution failed after a healthy connection

Why I think this is a Codex-side issue

Because the exact same logical operation succeeds through CLI parity using the same underlying handler, which strongly suggests:

  • the underlying operation is fine
  • the failure is in the MCP host/client transport lifecycle

Related but not identical issues

  • #7155 — Windows-specific Transport closed with heavy stderr output
  • #6128 — persistent MCP / per-call restart behavior

This report is different: it is about macOS stdio MCP lifecycle robustness and stale transport closure surfacing as raw Transport closed during otherwise healthy local tool usage.

Additional information

If helpful, I can provide a more minimal reproduction, but I wanted to file this first because the pattern seems broader than the specific OMX tool.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗