mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
[codex] Classify nested MCP authentication startup errors (#30257)
## Summary - classify authentication-required RMCP startup failures, including errors nested inside `ClientInitializeError::TransportError` - let `codex-mcp` consume that classification so the existing `reauthenticationRequired` startup failure reason is emitted - add a regression test that performs real startup with an expired persisted OAuth token and no refresh token ## Why Follow-up to #29877. RMCP stores streamable HTTP initialization failures inside a dynamic transport error whose payload is not exposed through the standard Rust error source chain. The original `anyhow::Error::chain()` check therefore missed the nested `AuthError::AuthorizationRequired` seen during real MCP startup and emitted `failureReason: null`. The transport-specific inspection now lives in `codex-rmcp-client`, while `codex-mcp` consumes only the domain-level authentication-required result. This classifier does not distinguish first-time login from reauthentication; the existing auth-state logic remains responsible for that distinction. ## User impact When stored MCP OAuth credentials are expired and cannot be refreshed, app clients now receive `failureReason: "reauthenticationRequired"` on the failed startup update and can show the reconnect action. First-time login and unrelated startup failures remain unchanged. ## Validation - `just test -p codex-rmcp-client --test streamable_http_oauth_startup identifies_expired_unrefreshable_token_startup_error` - `just test -p codex-mcp startup_outcome_error_identifies_authentication_required` - `just test -p codex-mcp mcp_startup_failure_reason_requires_existing_oauth_and_auth_failure` - `cargo build -p codex-cli --bin codex` - local app-server probe emitted `failureReason: "reauthenticationRequired"` - manual end-to-end reconnect flow confirmed - `just fmt`
This commit is contained in:
1 parent
c55ce3b51b
commit
526f495f3a
4 files changed
+128
-11
No files matched your search
@@ -57,6 +57,7 @@ use codex_rmcp_client::LocalStdioServerLauncher;
|
||||
use codex_rmcp_client::RmcpClient;
|
||||
use codex_rmcp_client::StdioServerLauncher;
|
||||
use codex_rmcp_client::ToolWithConnectorId;
|
||||
use codex_rmcp_client::is_authentication_required_error;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::future::FutureExt;
|
||||
use futures::future::Shared;
|
||||
@@ -67,7 +68,6 @@ use rmcp::model::InitializeRequestParams;
|
||||
use rmcp::model::JsonObject;
|
||||
use rmcp::model::ProtocolVersion;
|
||||
use rmcp::model::Tool as RmcpTool;
|
||||
use rmcp::transport::auth::AuthError;
|
||||
use tokio::time::Instant as TokioInstant;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::Instrument;
|
||||
@@ -552,16 +552,7 @@ impl StartupOutcomeError {
|
||||
|
||||
impl From<anyhow::Error> for StartupOutcomeError {
|
||||
fn from(error: anyhow::Error) -> Self {
|
||||
let is_authentication_required = error.chain().any(|source| {
|
||||
source
|
||||
.downcast_ref::<AuthError>()
|
||||
.is_some_and(|auth_error| {
|
||||
matches!(
|
||||
auth_error,
|
||||
AuthError::AuthorizationRequired | AuthError::TokenExpired
|
||||
)
|
||||
})
|
||||
});
|
||||
let is_authentication_required = is_authentication_required_error(&error);
|
||||
Self::Failed {
|
||||
error: error.to_string(),
|
||||
is_authentication_required,
|
||||
|
||||
Reference in new issue
Block a user