Files
codex/codex-rs/rmcp-client/src/service_error.rs
T
yitaoh-oai 3c074bb44a Add model Search discovery to MCP bindings (#59034)
## What changed

Add `McpBinding` APIs to discover the model's Search tool profile through an already authorized, host-owned Apps connection. Discovery sends turn metadata, paginates `tools/list`, and returns a scoped binding with the refreshed declaration and connector-provided invocation context.

Require a visible Search tool and an object-valued invocation context. Preserve the context only for calls using the same client, while allowing normal dispatch to recheck visibility, account selection, and approval policy.

Bound discovery to five seconds and distinguish critical timeout, transport, and server errors from unavailability. Expose the existing retryable transport error classifier for this distinction.

## Testing

Add coverage for opaque invocation context extraction and extend transport error classification coverage to HTTP 503 and 403 responses, exercising the exported classifier through a contextual error.

GitOrigin-RevId: 9848d4adb91e3fb2b9406349c31d1fd5c5c47a33
2026-11-03 01:59:24 +00:00

57 lines
2.0 KiB
Rust

//! Access structured MCP errors without dropping their original protocol data.
use anyhow::Error;
use rmcp::ErrorData;
use rmcp::service::ServiceError;
use std::time::Duration;
use crate::rmcp_client::ClientOperationError;
use crate::rmcp_client::RmcpClient;
/// Whether tools/list would retry this transport error before returning it to the caller.
pub fn is_retryable_transport_error(error: &Error) -> bool {
error
.downcast_ref()
.is_some_and(RmcpClient::is_retryable_transport_error)
}
/// Returns the server's structured protocol error, including its original data.
pub fn mcp_error(error: &Error) -> Option<&ErrorData> {
let ClientOperationError::Service(ServiceError::McpError(error)) = error.downcast_ref()? else {
return None;
};
Some(error)
}
/// Preserves a request deadline as a typed error for caller-side telemetry.
pub fn request_timeout_error(label: &str, duration: Duration) -> Error {
ClientOperationError::Timeout {
label: label.to_string(),
duration,
}
.into()
}
/// Returns a bounded caller-side failure category without parsing error text.
pub fn request_error_code(error: &Error) -> Option<&'static str> {
let error = error.downcast_ref::<ClientOperationError>()?;
Some(match error {
ClientOperationError::Timeout { .. } => "request_timeout",
ClientOperationError::Service(error) => match error {
ServiceError::Timeout { .. } => "request_timeout",
ServiceError::TransportClosed => "transport_closed",
ServiceError::TransportSend(_) => "transport_error",
ServiceError::McpError(_) => "protocol_error",
ServiceError::UnexpectedResponse => "unexpected_response",
ServiceError::Cancelled { .. } => "cancelled",
ServiceError::SubscriptionLagged { .. } => "subscription_lagged",
ServiceError::InputRequiredRoundsExceeded { .. } => "input_required_rounds_exceeded",
_ => return None,
},
})
}
#[cfg(test)]
#[path = "service_error_tests.rs"]
mod tests;