Simplify TUI MCP startup state

This commit is contained in:
Eric Traut
2026-04-29 01:09:28 -07:00
parent a7d5a99caf
commit 603c398b2f
5 changed files with 119 additions and 379 deletions
+4 -5
View File
@@ -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<HashMap<String, McpStartupStatus>>,
/// Expected MCP servers for the current startup round, seeded from enabled local config.
mcp_startup_expected_servers: Option<HashSet<String>>,
@@ -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
+1 -19
View File
@@ -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<String>, cancelled: Vec<String>) {
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,
@@ -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<String, McpAuthStatus>,
}
#[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<String>,
pub(crate) failed: Vec<McpStartupFailure>,
pub(crate) cancelled: Vec<String>,
}
#[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,
-3
View File
@@ -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;
+114 -330
View File
@@ -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::<String>();
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::<String>());
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::<String>());
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::<String>();
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::<String>();
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()