From 603c398b2fbac6e08009ca9e3d2aca73485133a9 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Wed, 29 Apr 2026 00:25:44 -0700 Subject: [PATCH] Simplify TUI MCP startup state --- codex-rs/tui/src/chatwidget.rs | 9 +- codex-rs/tui/src/chatwidget/mcp_startup.rs | 20 +- codex-rs/tui/src/chatwidget/test_events.rs | 22 - codex-rs/tui/src/chatwidget/tests.rs | 3 - .../tui/src/chatwidget/tests/mcp_startup.rs | 444 +++++------------- 5 files changed, 119 insertions(+), 379 deletions(-) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index a4413e8975..d42d7f6cee 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -878,9 +878,10 @@ pub(crate) struct ChatWidget { agent_turn_running: bool, /// Tracks per-server MCP startup state while startup is in progress. /// - /// The map is `Some(_)` from the first `McpStartupUpdate` until `McpStartupComplete`, and the - /// bottom pane is treated as "running" while this is populated, even if no agent turn is - /// currently executing. + /// The map is `Some(_)` from the first startup status update until the + /// app-server-backed startup round settles, and the bottom pane is treated + /// as "running" while this is populated, even if no agent turn is currently + /// executing. mcp_startup_status: Option>, /// Expected MCP servers for the current startup round, seeded from enabled local config. mcp_startup_expected_servers: Option>, @@ -7129,8 +7130,6 @@ impl ChatWidget { self.on_error(message); } } - EventMsg::McpStartupUpdate(ev) => self.on_mcp_startup_update(ev), - EventMsg::McpStartupComplete(ev) => self.on_mcp_startup_complete(ev), EventMsg::TurnAborted(ev) => match ev.reason { TurnAbortReason::Interrupted => { let reason = if ev diff --git a/codex-rs/tui/src/chatwidget/mcp_startup.rs b/codex-rs/tui/src/chatwidget/mcp_startup.rs index e5ed9f2939..cf499a455b 100644 --- a/codex-rs/tui/src/chatwidget/mcp_startup.rs +++ b/codex-rs/tui/src/chatwidget/mcp_startup.rs @@ -8,17 +8,10 @@ use std::collections::BTreeSet; use codex_app_server_protocol::McpServerStartupState; use codex_app_server_protocol::McpServerStatusUpdatedNotification; -use serde::Deserialize; -use serde::Serialize; use super::ChatWidget; -#[cfg(test)] -use super::test_events::McpStartupCompleteEvent; -#[cfg(test)] -use super::test_events::McpStartupUpdateEvent; -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "snake_case", tag = "state")] +#[derive(Debug, Clone)] pub(crate) enum McpStartupStatus { Starting, Ready, @@ -179,11 +172,6 @@ impl ChatWidget { self.mcp_startup_expected_servers = Some(server_names.into_iter().collect()); } - #[cfg(test)] - pub(super) fn on_mcp_startup_update(&mut self, ev: McpStartupUpdateEvent) { - self.update_mcp_startup_status(ev.server, ev.status, /*complete_when_settled*/ false); - } - pub(super) fn finish_mcp_startup(&mut self, failed: Vec, cancelled: Vec) { if !cancelled.is_empty() { self.on_warning(format!( @@ -246,12 +234,6 @@ impl ChatWidget { self.finish_mcp_startup(failed, cancelled); } - #[cfg(test)] - pub(super) fn on_mcp_startup_complete(&mut self, ev: McpStartupCompleteEvent) { - let failed = ev.failed.into_iter().map(|f| f.server).collect(); - self.finish_mcp_startup(failed, ev.cancelled); - } - pub(super) fn on_mcp_server_status_updated( &mut self, notification: McpServerStatusUpdatedNotification, diff --git a/codex-rs/tui/src/chatwidget/test_events.rs b/codex-rs/tui/src/chatwidget/test_events.rs index c30aa372ca..1e3e06b30d 100644 --- a/codex-rs/tui/src/chatwidget/test_events.rs +++ b/codex-rs/tui/src/chatwidget/test_events.rs @@ -49,7 +49,6 @@ use codex_protocol::request_user_input::RequestUserInputEvent; use codex_utils_absolute_path::AbsolutePathBuf; use serde::Deserialize; -use super::mcp_startup::McpStartupStatus; use super::user_messages::UserMessageEvent; #[derive(Debug, Clone)] @@ -126,8 +125,6 @@ pub(crate) enum EventMsg { AgentReasoningRawContentDelta(AgentReasoningRawContentDeltaEvent), SessionConfigured(SessionConfiguredEvent), ThreadNameUpdated(ThreadNameUpdatedEvent), - McpStartupUpdate(McpStartupUpdateEvent), - McpStartupComplete(McpStartupCompleteEvent), McpToolCallBegin(McpToolCallBeginEvent), McpToolCallEnd(McpToolCallEndEvent), WebSearchBegin(WebSearchBeginEvent), @@ -279,25 +276,6 @@ pub(crate) struct McpListToolsResponseEvent { pub(crate) auth_statuses: HashMap, } -#[derive(Debug, Clone)] -pub(crate) struct McpStartupUpdateEvent { - pub(crate) server: String, - pub(crate) status: McpStartupStatus, -} - -#[derive(Debug, Clone, Default)] -pub(crate) struct McpStartupCompleteEvent { - pub(crate) ready: Vec, - pub(crate) failed: Vec, - pub(crate) cancelled: Vec, -} - -#[derive(Debug, Clone)] -pub(crate) struct McpStartupFailure { - pub(crate) server: String, - pub(crate) error: String, -} - #[derive(Debug, Clone)] pub(crate) struct ThreadNameUpdatedEvent { pub(crate) thread_id: ThreadId, diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index f5a266ea97..a1f91ea2d2 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -4,7 +4,6 @@ //! snapshot-based so that layout regressions and status/header changes show up as stable, //! reviewable diffs. -pub(super) use super::mcp_startup::McpStartupStatus; pub(super) use super::*; pub(super) use crate::app_command::AppCommand as Op; pub(super) use crate::app_event::AppEvent; @@ -28,8 +27,6 @@ pub(super) use crate::chatwidget::test_events::Event; pub(super) use crate::chatwidget::test_events::EventMsg; pub(super) use crate::chatwidget::test_events::ExitedReviewModeEvent; pub(super) use crate::chatwidget::test_events::ItemCompletedEvent; -pub(super) use crate::chatwidget::test_events::McpStartupCompleteEvent; -pub(super) use crate::chatwidget::test_events::McpStartupUpdateEvent; pub(super) use crate::chatwidget::test_events::ModelVerificationEvent; pub(super) use crate::chatwidget::test_events::SessionConfiguredEvent; pub(super) use crate::chatwidget::test_events::StreamErrorEvent; diff --git a/codex-rs/tui/src/chatwidget/tests/mcp_startup.rs b/codex-rs/tui/src/chatwidget/tests/mcp_startup.rs index 8b1180cfa3..511c49b4b0 100644 --- a/codex-rs/tui/src/chatwidget/tests/mcp_startup.rs +++ b/codex-rs/tui/src/chatwidget/tests/mcp_startup.rs @@ -1,18 +1,28 @@ use super::*; use pretty_assertions::assert_eq; +fn notify_mcp_status( + chat: &mut ChatWidget, + name: &str, + status: McpServerStartupState, + error: Option<&str>, +) { + chat.handle_server_notification( + ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { + name: name.to_string(), + status, + error: error.map(str::to_string), + }), + /*replay_kind*/ None, + ); +} + #[tokio::test] async fn mcp_startup_header_booting_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.show_welcome_banner = false; - chat.handle_codex_event(Event { - id: "mcp-1".into(), - msg: EventMsg::McpStartupUpdate(McpStartupUpdateEvent { - server: "alpha".into(), - status: McpStartupStatus::Starting, - }), - }); + notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting, None); let height = chat.desired_height(/*width*/ 80); let mut terminal = ratatui::Terminal::new(ratatui::backend::TestBackend::new(80, height)) @@ -43,13 +53,14 @@ async fn mcp_startup_complete_does_not_clear_running_task() { assert!(chat.bottom_pane.is_task_running()); assert!(chat.bottom_pane.status_indicator_visible()); - chat.handle_codex_event(Event { - id: "mcp-1".into(), - msg: EventMsg::McpStartupComplete(McpStartupCompleteEvent { - ready: vec!["schaltwerk".into()], - ..Default::default() - }), - }); + chat.set_mcp_startup_expected_servers(["schaltwerk".to_string()]); + notify_mcp_status( + &mut chat, + "schaltwerk", + McpServerStartupState::Starting, + None, + ); + notify_mcp_status(&mut chat, "schaltwerk", McpServerStartupState::Ready, None); assert!(chat.bottom_pane.is_task_running()); assert!(chat.bottom_pane.status_indicator_visible()); @@ -61,25 +72,16 @@ async fn app_server_mcp_startup_failure_renders_warning_history() { chat.show_welcome_banner = false; chat.set_mcp_startup_expected_servers(["alpha".to_string(), "beta".to_string()]); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting, None); assert!(drain_insert_history(&mut rx).is_empty()); assert!(chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); let failure_cells = drain_insert_history(&mut rx); @@ -91,26 +93,12 @@ async fn app_server_mcp_startup_failure_renders_warning_history() { assert!(!failure_text.contains("MCP startup incomplete")); assert!(chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting, None); assert!(drain_insert_history(&mut rx).is_empty()); assert!(chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Ready, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Ready, None); let summary_cells = drain_insert_history(&mut rx); let summary_text = summary_cells @@ -151,30 +139,14 @@ async fn app_server_mcp_startup_lag_settles_startup_and_ignores_late_updates() { chat.show_welcome_banner = false; chat.set_mcp_startup_expected_servers(["alpha".to_string(), "beta".to_string()]); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, - ); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, + notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting, None); + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting, None); let _ = drain_insert_history(&mut rx); assert!(chat.bottom_pane.is_task_running()); @@ -190,26 +162,12 @@ async fn app_server_mcp_startup_lag_settles_startup_and_ignores_late_updates() { assert!(summary_text.contains("MCP startup incomplete (failed: alpha)")); assert!(!chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting, None); assert!(drain_insert_history(&mut rx).is_empty()); assert!(!chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Ready, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Ready, None); assert!(drain_insert_history(&mut rx).is_empty()); assert!(!chat.bottom_pane.is_task_running()); @@ -223,13 +181,11 @@ async fn app_server_mcp_startup_after_lag_can_settle_without_starting_updates() chat.finish_mcp_startup_after_lag(); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); let failure_text = drain_insert_history(&mut rx) @@ -239,14 +195,7 @@ async fn app_server_mcp_startup_after_lag_can_settle_without_starting_updates() assert!(failure_text.contains("MCP client for `alpha` failed to start: handshake failed")); assert!(chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Ready, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Ready, None); let summary_text = drain_insert_history(&mut rx) .iter() @@ -262,43 +211,25 @@ async fn app_server_mcp_startup_after_lag_preserves_partial_terminal_only_round( chat.show_welcome_banner = false; chat.set_mcp_startup_expected_servers(["alpha".to_string(), "beta".to_string()]); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, - ); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, + notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting, None); + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting, None); let _ = drain_insert_history(&mut rx); chat.finish_mcp_startup_after_lag(); let _ = drain_insert_history(&mut rx); assert!(!chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); assert!(drain_insert_history(&mut rx).is_empty()); @@ -306,14 +237,7 @@ async fn app_server_mcp_startup_after_lag_preserves_partial_terminal_only_round( chat.finish_mcp_startup_after_lag(); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Ready, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Ready, None); let summary_text = drain_insert_history(&mut rx) .iter() @@ -330,78 +254,37 @@ async fn app_server_mcp_startup_next_round_discards_stale_terminal_updates() { chat.show_welcome_banner = false; chat.set_mcp_startup_expected_servers(["alpha".to_string(), "beta".to_string()]); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, - ); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, + notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting, None); + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting, None); let _ = drain_insert_history(&mut rx); chat.finish_mcp_startup_after_lag(); let _ = drain_insert_history(&mut rx); assert!(!chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some( - "MCP client for `alpha` failed to start: stale handshake failed".to_string(), - ), - }), - /*replay_kind*/ None, + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: stale handshake failed"), ); assert!(drain_insert_history(&mut rx).is_empty()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting, None); assert!(drain_insert_history(&mut rx).is_empty()); assert!(!chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Ready, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Ready, None); assert!(drain_insert_history(&mut rx).is_empty()); assert!(chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Ready, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Ready, None); let summary_text = drain_insert_history(&mut rx) .iter() @@ -419,23 +302,14 @@ async fn app_server_mcp_startup_next_round_keeps_terminal_statuses_after_startin chat.finish_mcp_startup_after_lag(); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting, None); assert!(drain_insert_history(&mut rx).is_empty()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); let failure_text = drain_insert_history(&mut rx) @@ -444,25 +318,11 @@ async fn app_server_mcp_startup_next_round_keeps_terminal_statuses_after_startin .collect::(); assert!(failure_text.contains("MCP client for `alpha` failed to start: handshake failed")); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting, None); assert!(drain_insert_history(&mut rx).is_empty()); assert!(chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Ready, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Ready, None); let summary_text = drain_insert_history(&mut rx) .iter() @@ -479,24 +339,15 @@ async fn app_server_mcp_startup_next_round_with_empty_expected_servers_reactivat 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, - ); + notify_mcp_status(&mut chat, "runtime", McpServerStartupState::Starting, 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, + notify_mcp_status( + &mut chat, + "runtime", + McpServerStartupState::Failed, + Some("MCP client for `runtime` failed to start: handshake failed"), ); let summary_text = drain_insert_history(&mut rx) @@ -508,56 +359,18 @@ async fn app_server_mcp_startup_next_round_with_empty_expected_servers_reactivat assert!(!chat.bottom_pane.is_task_running()); } -#[tokio::test] -async fn app_server_mcp_startup_after_lag_with_empty_expected_servers_preserves_failures() { - 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.on_mcp_startup_update(McpStartupUpdateEvent { - server: "runtime".to_string(), - status: McpStartupStatus::Starting, - }); - chat.on_mcp_startup_update(McpStartupUpdateEvent { - server: "runtime".to_string(), - status: McpStartupStatus::Failed { - error: "MCP client for `runtime` failed to start: handshake failed".to_string(), - }, - }); - - let warning_text = drain_insert_history(&mut rx) - .iter() - .map(|lines| lines_to_single_string(lines)) - .collect::(); - assert!(warning_text.contains("MCP client for `runtime` failed to start: handshake failed")); - assert!(chat.bottom_pane.is_task_running()); - - chat.finish_mcp_startup_after_lag(); - - let summary_text = drain_insert_history(&mut rx) - .iter() - .map(|lines| lines_to_single_string(lines)) - .collect::(); - assert!(summary_text.contains("MCP startup incomplete (failed: runtime)")); - assert!(!chat.bottom_pane.is_task_running()); -} - #[tokio::test] async fn app_server_mcp_startup_after_lag_includes_runtime_servers_with_expected_set() { 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(["alpha".to_string()]); - chat.on_mcp_startup_update(McpStartupUpdateEvent { - server: "alpha".to_string(), - status: McpStartupStatus::Ready, - }); - chat.on_mcp_startup_update(McpStartupUpdateEvent { - server: "runtime".to_string(), - status: McpStartupStatus::Failed { - error: "MCP client for `runtime` failed to start: handshake failed".to_string(), - }, - }); + notify_mcp_status( + &mut chat, + "runtime", + McpServerStartupState::Failed, + Some("MCP client for `runtime` failed to start: handshake failed"), + ); let warning_text = drain_insert_history(&mut rx) .iter() @@ -582,57 +395,35 @@ async fn app_server_mcp_startup_next_round_after_lag_can_settle_without_starting chat.show_welcome_banner = false; chat.set_mcp_startup_expected_servers(["alpha".to_string(), "beta".to_string()]); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, - ); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, - ); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Starting, - error: None, - }), - /*replay_kind*/ None, + notify_mcp_status(&mut chat, "alpha", McpServerStartupState::Starting, None); + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Starting, None); let _ = drain_insert_history(&mut rx); chat.finish_mcp_startup_after_lag(); let _ = drain_insert_history(&mut rx); assert!(!chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some( - "MCP client for `alpha` failed to start: stale handshake failed".to_string(), - ), - }), - /*replay_kind*/ None, + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: stale handshake failed"), ); assert!(drain_insert_history(&mut rx).is_empty()); chat.finish_mcp_startup_after_lag(); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "alpha".to_string(), - status: McpServerStartupState::Failed, - error: Some("MCP client for `alpha` failed to start: handshake failed".to_string()), - }), - /*replay_kind*/ None, + notify_mcp_status( + &mut chat, + "alpha", + McpServerStartupState::Failed, + Some("MCP client for `alpha` failed to start: handshake failed"), ); let failure_text = drain_insert_history(&mut rx) @@ -642,14 +433,7 @@ async fn app_server_mcp_startup_next_round_after_lag_can_settle_without_starting assert!(failure_text.is_empty()); assert!(!chat.bottom_pane.is_task_running()); - chat.handle_server_notification( - ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification { - name: "beta".to_string(), - status: McpServerStartupState::Ready, - error: None, - }), - /*replay_kind*/ None, - ); + notify_mcp_status(&mut chat, "beta", McpServerStartupState::Ready, None); let summary_text = drain_insert_history(&mut rx) .iter()