mirror of
https://github.com/openai/codex.git
synced 2026-11-12 06:40:35 +00:00
## Why MCP inventory can be cached or collected separately from a thread's live connections, so tool availability alone does not describe the current runtime state. ## What changed - Add a nullable `runtimeStatus` to `mcpServerStatus/list` for thread-scoped requests, covering not-started, starting, connected, authentication-required, failed, cancelled, and disabled connections. - Observe published connection state without starting or reconnecting servers, and return an unknown status when no thread is supplied or the active configuration no longer matches the published registration. - Show connection state and tool counts in the compact `/mcp` view while retaining the detailed inventory in `/mcp verbose` and compatibility with servers that omit `runtimeStatus`. ## Testing - Cover runtime status transitions, deferred and disabled servers, closed transports, configuration changes, protocol compatibility, and TUI rendering. GitOrigin-RevId: e3bb6efe652f0fa8b3c97d5c53e4729b3a87cd91
47 lines
1.6 KiB
Rust
47 lines
1.6 KiB
Rust
//! Observe the latest startup attempt without polling it or initiating a retry.
|
|
|
|
use codex_protocol::mcp::McpServerConnectionStatus as Status;
|
|
|
|
use super::AsyncManagedClient;
|
|
use super::StartupOutcomeError;
|
|
|
|
impl AsyncManagedClient {
|
|
pub(crate) async fn connection_status(&self) -> Status {
|
|
if self.cancel_token.is_cancelled() {
|
|
return Status::Cancelled;
|
|
}
|
|
let reconnect_outcome = if let Some(reconnect) = &self.startup_reconnect {
|
|
let state = reconnect
|
|
.state
|
|
.lock()
|
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
|
if state.reconnect_in_flight {
|
|
return Status::Starting;
|
|
}
|
|
state
|
|
.current_client
|
|
.clone()
|
|
.map(Ok)
|
|
.or_else(|| state.last_error.clone().map(Err))
|
|
} else {
|
|
None
|
|
};
|
|
let outcome = reconnect_outcome.or_else(|| self.client.peek().cloned());
|
|
match outcome {
|
|
Some(Ok(client)) => {
|
|
if client.client.is_closed().await {
|
|
Status::Failed
|
|
} else {
|
|
Status::Connected
|
|
}
|
|
}
|
|
Some(Err(error)) if error.is_authentication_required() => {
|
|
Status::AuthenticationRequired
|
|
}
|
|
Some(Err(StartupOutcomeError::Failed { .. })) => Status::Failed,
|
|
Some(Err(StartupOutcomeError::Cancelled)) => Status::Cancelled,
|
|
None => Status::Starting,
|
|
}
|
|
}
|
|
}
|