Files
codex/codex-rs/rmcp-client/src/startup_error.rs
T
stevenlee-oai a3bce23f3b Update rmcp to 3.1.3 (#39798)
## What changed

- Upgrade `rmcp` and `rmcp-macros` from 3.1.2 to 3.1.3.
- Preserve authentication-required and retryable transport classifications when modern MCP discovery falls back to legacy initialization.
- Prevent unrelated discovery errors from triggering legacy fallback while retaining their diagnostics.

## Testing

- Cover authentication challenges and transient initialization failures during legacy fallback.
- Verify that a failed OAuth issuer check does not consume the pending authorization state.

GitOrigin-RevId: 957c0873fec1148883dd163fbaeb77d7593d8c53
2026-08-20 20:53:17 +00:00

62 lines
2.4 KiB
Rust

use anyhow::Error;
use rmcp::service::ClientInitializeError;
use rmcp::service::ServiceError;
use rmcp::transport::DynamicTransportError;
use rmcp::transport::auth::AuthError;
use rmcp::transport::streamable_http_client::StreamableHttpError;
use crate::http_client_adapter::StreamableHttpClientAdapterError;
use crate::rmcp_client::ClientOperationError;
/// Returns whether an RMCP client error indicates that authentication is required.
///
/// This does not distinguish first-time login from reauthentication.
/// Streamable HTTP initialization errors are stored inside RMCP's dynamic
/// transport error, which is not part of the standard error source chain.
pub fn is_authentication_required_error(error: &Error) -> bool {
error.chain().any(|source| {
source
.downcast_ref::<AuthError>()
.is_some_and(auth_error_requires_authentication)
|| source
.downcast_ref::<ClientInitializeError>()
.is_some_and(|mut error| {
while let ClientInitializeError::LegacyFallbackFailed { fallback, .. } = error {
error = fallback;
}
matches!(
error,
ClientInitializeError::TransportError { error, .. }
if transport_error_requires_authentication(error)
)
})
|| source
.downcast_ref::<ClientOperationError>()
.is_some_and(|error| {
matches!(
error,
ClientOperationError::Service(ServiceError::TransportSend(error))
if transport_error_requires_authentication(error)
)
})
})
}
fn transport_error_requires_authentication(error: &DynamicTransportError) -> bool {
error
.error
.downcast_ref::<StreamableHttpError<StreamableHttpClientAdapterError>>()
.is_some_and(|error| match error {
StreamableHttpError::AuthRequired(_) => true,
StreamableHttpError::Auth(auth_error) => auth_error_requires_authentication(auth_error),
_ => false,
})
}
fn auth_error_requires_authentication(error: &AuthError) -> bool {
matches!(
error,
AuthError::AuthorizationRequired | AuthError::TokenExpired
)
}