Leader footer can stay stuck on child-session MCP startup (for example `starting omx_state MCP`)

Open 💬 3 comments Opened Apr 16, 2026 by xiangnan0811
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

codex-cli 0.120.0

What subscription do you have?

Redacted for privacy. I can provide it privately if needed.

Which model were you using?

gpt-5.4

What platform is your computer?

Darwin 24.6.0 arm64 arm

What terminal emulator and version are you using (if applicable)?

A macOS terminal emulator inside tmux 3.6a

What issue are you seeing?

The foreground leader footer can keep showing a child-session MCP startup state (for example starting omx_state MCP) for the entire lifetime of background subagent work.

This does not look like raw omx_state startup slowness. I measured omx_state cold-connect locally over stdio at roughly 100-160 ms, which is far shorter than the time the footer can remain in the "starting ... MCP" state.

So the bug appears to be closer to footer status attribution / cleanup for background child sessions than to actual MCP startup duration.

Related but not identical to existing reports such as #16821 and #12976: in my case the key symptom is that the leader footer stays visually stuck on child MCP startup even while child tasks are already running normally.

I am intentionally omitting screenshots, thread IDs, local file paths, usernames, and workspace-specific prompts for privacy.

What steps can reproduce the bug?

  1. Configure a few lightweight stdio MCP servers, including omx_state.
  2. Start a normal interactive Codex CLI leader session.
  3. From that leader session, spawn multiple background child sessions (native subagents; omx team workers can trigger a similar pattern).
  4. Watch the leader footer while the child sessions initialize.
  5. The leader footer can continue to show a child MCP startup indicator such as starting omx_state MCP until all background tasks finish.

Important detail: the background work itself often proceeds normally, so the footer state appears stale or incorrectly attributed rather than accurately representing a still-pending MCP startup.

What is the expected behavior?

  • The leader footer should only show MCP startup state for the foreground leader session itself, or clearly scope any child-session state to the child.
  • Once a child session finishes its MCP handshake/startup, the leader footer should clear that startup indicator promptly.
  • A lightweight MCP such as omx_state should not appear to be starting for the entire duration of unrelated background task execution.

Additional information

A few observations that may help narrow the issue:

  • Child sessions appear to initialize their own MCP managers rather than reusing the leader's live MCP connections.
  • The persistent footer state seems decoupled from the actual cold-connect time of omx_state.
  • This looks like a TUI/session-status scoping problem on top of normal child-session MCP lifecycle behavior.
  • I intentionally redacted any identifiers that could expose local paths, repo names, usernames, or private session metadata.

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 4 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #16821
  • #16779

Powered by Codex Action

flexiondotorg · 3 months ago

Sub-agent McpConnectionManager startup events leak into the leader TUI's status header because McpServerStatusUpdated notifications carry no thread identity and are routed as Global, which falls through to the leader's chat_widget and overwrites its mcp_startup_status map. Reproducible on codex-cli 0.128.0; same root cause as #16821 and #19542.

Causal chain (verified against main)

  1. Each sub-agent constructs its own McpConnectionManager. codex-rs/core/src/session/session.rs:986-1006 - sub-agent sessions build their own manager and emit a fresh Starting -> Ready sequence for every configured server, every time a sub-agent spawns.
  1. McpServerStatusUpdated carries no thread_id. codex-rs/tui/src/app/app_server_event_targets.rs:144 returns None for this notification variant, so the router classifies it as Global.
  1. Global notifications fall through to the leader's chat_widget. codex-rs/tui/src/app/app_server_events.rs:135-139 - the Global match arm is a no-op with no return, so execution continues into self.chat_widget.handle_server_notification(...) regardless of which session emitted the event.
  1. The leader's mcp_startup_status map is overwritten by child events. codex-rs/tui/src/chatwidget/mcp_startup.rs:81-85 does startup_status.insert(server, status) unconditionally, so a sub-agent's Starting for a server the leader already has as Ready flips the leader's view back to Starting.
  1. The settle check requires every server in a non-Starting state. codex-rs/tui/src/chatwidget/mcp_startup.rs:102-127 - while sub-agents keep re-emitting Starting, the settle condition never passes, and the spinner remains up indefinitely.

The leader has no way to filter these events because they carry no session or thread identity to filter by.

Reproduction

  • codex-cli 0.128.0 on Linux (NixOS)
  • At least one HTTP MCP server configured with bearer_token_env_var (e.g. context7 against https://mcp.context7.com/mcp)
  • Run any task that triggers spawn_agent
  • Leader header shows Starting MCP servers (X/N): <name> indefinitely while sub-agents are active
  • codex exec is unaffected - no TUI, no spinner to wedge
  • The displayed server name is whichever sorts alphabetically first among the still-Starting set; the underlying mechanism is server-agnostic, which is why #16821 reports omx_state and #19542 reports "the last MCP" on different setups

Cross-references

  • #16821 - Windows, codex 0.118, surfaces as omx_state
  • #19542 - Windows, codex 0.125, surfaces as "the last MCP"

Both are the same root cause as this issue. The reporter here framed it correctly as a "TUI/session-status scoping problem".

Suggested fix direction

The minimal patch lives in the routing layer, not the TUI:

  • Add a thread_id field to ServerNotification::McpServerStatusUpdated, carried through from the emitting McpConnectionManager.
  • In app_server_event_targets.rs, classify McpServerStatusUpdated with a non-leader thread_id as Thread(thread_id) rather than Global.
  • Scope the leader's mcp_startup_status accounting in chatwidget/mcp_startup.rs to events from primary_thread_id only.

This preserves the leader spinner for the leader's own MCP lifecycle while preventing sub-agent lifecycle events from cross-contaminating it. Sub-agent MCP status can still be surfaced separately if desired, but it should not mutate the leader's map.