PR approval for 'Transport Closed' bug for MCP servers
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 30/100
Research direction
Start with issue #16899 and inspect the codex-rmcp-client package, its existing tests, and the tested commit linked in this issue. Run just test -p codex-rmcp-client and review the reported recovery regressions; done means the transport recovery, concurrency, no-replay, cleanup, and HTTP 404 behaviors pass without breaking existing tests.
Written by the indexing model from the issue text.
Description
What version of Codex CLI is running?
0.125.0
What subscription do you have?
ChatGPT Pro 20x
Which model were you using?
all
What platform is your computer?
This bug is reproducible on all platforms
What terminal emulator and version are you using (if applicable)?
This bug is reproducible in all terminals
Codex doctor report
What issue are you seeing?
I investigated the https://github.com/openai/codex/issues/16899 issue and have a tested fix ready.
TLDR
Root cause: RmcpClient retains a closed RunningService in ClientState::Ready, so subsequent operations reuse the dead transport indefinitely. Fresh Codex processes work because they construct a new client and transport.
The proposed fix recreates and initializes the transport behind the existing recovery semaphore, shares one recovery across concurrent callers, never replays an interrupted tool call, preserves HTTP 404 retry behavior, and tracks replacement stdio processes for cleanup.
Validation: all 119 codex-rmcp-client tests pass, including new preclosed, concurrent recovery, no-replay, and process-cleanup regressions.
Tested commit: https://github.com/welf/codex/commit/bba585c393a0de5e96448707b06248a729aa2cef
Would a Codex maintainer be willing to invite a PR for this approach?
Here is the full PR description:
Fixes #16899
Root cause
RmcpClient stores a successfully initialized service in ClientState::Ready. When the RMCP service task or underlying
transport closes, the stored state is not changed. Normal operations retrieve that Ready service without checking
whether it is still alive.
The RMCP dependency returns rmcp::service::ServiceError::TransportClosed, whose display text is Transport closed.
Codex returns that error but leaves the same closed service cached.
Prepared tool calls and resource clients retain the same Arc<RmcpClient>. Since transport closure does not mark the
MCP runtime dirty, the ordinary per-step dirty check does not rebuild the connection. Every subsequent operation
therefore calls the same closed service.
A new Codex process constructs a new McpRuntime, McpConnectionSet, ManagedClient, and RmcpClient, which explains
why fresh codex exec processes continue to work.
What
Recover an MCP client when its current transport has closed instead of leaving a long-lived Codex session permanently
attached to a dead RunningService.
- Recreate and initialize the transport before the next operation when closure is already observable.
- Serialize concurrent recovery attempts so callers share one replacement connection.
- Recover eagerly after an in-flight operation reports
TransportClosed, but return the original error and never replay
the operation. - Track the active stdio process alongside the active service so shutdown cleans up a replacement server too.
- Preserve the existing streamable HTTP 404 session-expiry behavior and its single operation retry.
Why
A persistent CLI session kept the original closed MCP service after a stdio server or transport exited. Every later tool
call therefore failed with Transport closed, while a fresh codex exec worked because it constructed a new client and
transport.
How
The client now uses its retained transport recipe and initialization context to rebuild the service behind the existing
recovery semaphore. Recovery atomically installs the replacement service, OAuth persistor, and stdio process handle.
Callers that arrive concurrently reuse a healthy replacement installed by the first recovery attempt.
Regression coverage verifies pre-closed recovery, single recovery under concurrency, no replay after an in-flight
tool-call closure, replacement stdio cleanup, and unchanged HTTP 404 retry semantics. The new recovery regressions were
confirmed to fail before the implementation.
Validation
just test -p codex-rmcp-client— 119 passed, 5 skippedjust argument-comment-lint -p codex-rmcp-clientjust fix -p codex-rmcp-clientjust fmt
Issue: https://github.com/openai/codex/issues/16899
What steps can reproduce the bug?
See implemented tests
What is the expected behavior?
After Transport Closed error MCP servers should continue to work
Additional information
No response
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- Avg merge
- 1m
- Merged PRs (30d)
- 1k
Contributor 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 openai/codex
-
enhancement remote
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
bug CLI windows-os
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
macOS sandbox blocks hw.optional.arm64 sysctl, causing Flutter to misdetect Apple Silicon as x64 Openbug CLI sandbox
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
bug CLI TUI
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
CLI config enhancement skills
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
kwakseongjae/auto-hwp#319 ·
-
area:cli bug filter-quality good first issue priority:medium
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
Difficulty 1/5 Under an hour Newbie friendliness 72/100
bevyengine/bevy#25861 ·
-
comp-datalake
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
ClickHouse/ClickHouse#121222 ·
-
A-linter
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
oxc-project/oxc#26863 ·