diff --git a/codex-rs/tui_app_server/src/chatwidget.rs b/codex-rs/tui_app_server/src/chatwidget.rs index 7bbf8186d5..617fa019b5 100644 --- a/codex-rs/tui_app_server/src/chatwidget.rs +++ b/codex-rs/tui_app_server/src/chatwidget.rs @@ -2812,8 +2812,8 @@ impl ChatWidget { let Some(expected_servers) = &self.mcp_startup_expected_servers else { return; }; - let saw_full_round = !expected_servers.is_empty() - && expected_servers + let saw_full_round = expected_servers.is_empty() + || expected_servers .iter() .all(|name| self.mcp_startup_pending_next_round.contains_key(name)); let saw_starting = self diff --git a/codex-rs/tui_app_server/src/chatwidget/tests.rs b/codex-rs/tui_app_server/src/chatwidget/tests.rs index 05be68493b..255c389160 100644 --- a/codex-rs/tui_app_server/src/chatwidget/tests.rs +++ b/codex-rs/tui_app_server/src/chatwidget/tests.rs @@ -11950,6 +11950,42 @@ async fn app_server_mcp_startup_next_round_keeps_terminal_statuses_after_startin assert!(!chat.bottom_pane.is_task_running()); } +#[tokio::test] +async fn app_server_mcp_startup_next_round_with_empty_expected_servers_reactivates() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; + chat.show_welcome_banner = false; + chat.set_mcp_startup_expected_servers(std::iter::empty::()); + chat.finish_mcp_startup(Vec::new(), Vec::new()); + + chat.handle_server_notification( + ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { + name: "runtime".to_string(), + status: McpServerStartupState::Starting, + error: None, + }), + /*replay_kind*/ None, + ); + assert!(drain_insert_history(&mut rx).is_empty()); + assert!(chat.bottom_pane.is_task_running()); + + chat.handle_server_notification( + ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { + name: "runtime".to_string(), + status: McpServerStartupState::Failed, + error: Some("MCP client for `runtime` failed to start: handshake failed".to_string()), + }), + /*replay_kind*/ None, + ); + + let summary_text = drain_insert_history(&mut rx) + .iter() + .map(|lines| lines_to_single_string(lines)) + .collect::(); + assert!(summary_text.contains("MCP client for `runtime` failed to start: handshake failed")); + assert!(summary_text.contains("MCP startup incomplete (failed: runtime)")); + assert!(!chat.bottom_pane.is_task_running()); +} + #[tokio::test] async fn app_server_mcp_startup_next_round_after_lag_can_settle_without_starting_updates() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;