mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
[codex] Surface MCP reauthentication-required startup failures (#29877)
## Summary - distinguish expired, non-refreshable stored MCP OAuth credentials from first-time missing credentials - carry a typed `failureReason: "reauthenticationRequired"` on the existing `mcpServer/startupStatus/updated` notification only when user action is required - keep the public MCP auth-status API unchanged and regenerate the app-server protocol schemas and documentation ## Why An MCP server with an expired access token and no usable refresh token currently fails startup without giving clients a reliable, typed recovery signal. The existing startup-status notification is the natural place to carry this state. Its nullable `failureReason` keeps the recovery reason attached to the failed startup transition without adding a one-off notification. Internally, Codex distinguishes first-time login from reauthentication and emits the reason only when the startup error itself requires authentication. ## User impact App clients can prompt an existing user to reconnect an MCP server when automatic recovery is impossible by handling a failed `mcpServer/startupStatus/updated` notification whose `failureReason` is `reauthenticationRequired`. Starting, ready, cancelled, unrelated failures, and first-time setup carry no reauthentication reason. ## Companion app PR - openai/openai#1069582 ## Validation - `just test -p codex-app-server-protocol` — 248 passed; schema fixture tests passed - `cargo check -p codex-app-server -p codex-tui` - `just test -p codex-rmcp-client -p codex-mcp` — 184 passed, 2 skipped - `just test -p codex-protocol -p codex-app-server-protocol -p codex-mcp` — 579 passed - `just write-app-server-schema` - `just fmt`
This commit is contained in:
1 parent
b80fbb70cd
commit
a6d20ed297
27 files changed
+350
-55
No files matched your search
@@ -9,7 +9,7 @@ use codex_config::types::AuthKeyringBackendKind;
|
||||
use codex_config::types::OAuthCredentialsStoreMode;
|
||||
use codex_exec_server::HttpClient;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_protocol::protocol::McpAuthStatus;
|
||||
use codex_rmcp_client::McpAuthState;
|
||||
use codex_rmcp_client::OAuthProviderError;
|
||||
use codex_rmcp_client::determine_streamable_http_auth_status;
|
||||
use codex_rmcp_client::determine_streamable_http_auth_status_with_http_client;
|
||||
@@ -54,7 +54,7 @@ pub struct ResolvedMcpOAuthScopes {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct McpAuthStatusEntry {
|
||||
pub config: Option<McpServerConfig>,
|
||||
pub auth_status: McpAuthStatus,
|
||||
pub auth_state: McpAuthState,
|
||||
}
|
||||
|
||||
pub async fn oauth_login_support(transport: &McpServerTransportConfig) -> McpOAuthLoginSupport {
|
||||
@@ -208,7 +208,7 @@ where
|
||||
)
|
||||
});
|
||||
async move {
|
||||
let auth_status = match config.as_ref() {
|
||||
let auth_state = match config.as_ref() {
|
||||
Some(config) => {
|
||||
match compute_auth_status(
|
||||
&name,
|
||||
@@ -225,16 +225,13 @@ where
|
||||
warn!(
|
||||
"failed to determine auth status for MCP server `{name}`: {error:?}"
|
||||
);
|
||||
McpAuthStatus::Unsupported
|
||||
McpAuthState::Unsupported
|
||||
}
|
||||
}
|
||||
}
|
||||
None => McpAuthStatus::Unsupported,
|
||||
};
|
||||
let entry = McpAuthStatusEntry {
|
||||
config,
|
||||
auth_status,
|
||||
None => McpAuthState::Unsupported,
|
||||
};
|
||||
let entry = McpAuthStatusEntry { config, auth_state };
|
||||
(name, entry)
|
||||
}
|
||||
});
|
||||
@@ -249,17 +246,17 @@ async fn compute_auth_status(
|
||||
keyring_backend_kind: AuthKeyringBackendKind,
|
||||
has_runtime_auth: bool,
|
||||
runtime_context: &McpRuntimeContext,
|
||||
) -> Result<McpAuthStatus> {
|
||||
) -> Result<McpAuthState> {
|
||||
if !config.enabled {
|
||||
return Ok(McpAuthStatus::Unsupported);
|
||||
return Ok(McpAuthState::Unsupported);
|
||||
}
|
||||
|
||||
if has_runtime_auth {
|
||||
return Ok(McpAuthStatus::BearerToken);
|
||||
return Ok(McpAuthState::BearerToken);
|
||||
}
|
||||
|
||||
match &config.transport {
|
||||
McpServerTransportConfig::Stdio { .. } => Ok(McpAuthStatus::Unsupported),
|
||||
McpServerTransportConfig::Stdio { .. } => Ok(McpAuthState::Unsupported),
|
||||
McpServerTransportConfig::StreamableHttp {
|
||||
url,
|
||||
bearer_token_env_var,
|
||||
|
||||
@@ -567,7 +567,7 @@ fn auth_statuses_from_entries(
|
||||
) -> HashMap<String, McpAuthStatus> {
|
||||
auth_status_entries
|
||||
.iter()
|
||||
.map(|(name, entry)| (name.clone(), entry.auth_status))
|
||||
.map(|(name, entry)| (name.clone(), McpAuthStatus::from(entry.auth_state)))
|
||||
.collect::<HashMap<_, _>>()
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user