From 1e6ab5aff91caf7304f596854628ed57d3d1b95e Mon Sep 17 00:00:00 2001 From: Gabriel Peal Date: Tue, 14 Oct 2025 17:53:48 -0700 Subject: [PATCH 1/2] Works --- codex-rs/cli/src/mcp_cmd.rs | 31 ++++++++++++++++---- codex-rs/core/src/codex.rs | 39 +++++++++++++++++++++---- codex-rs/rmcp-client/src/auth_status.rs | 2 +- codex-rs/rmcp-client/src/lib.rs | 1 + 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/codex-rs/cli/src/mcp_cmd.rs b/codex-rs/cli/src/mcp_cmd.rs index 0a5be0dc23..8813e2b61a 100644 --- a/codex-rs/cli/src/mcp_cmd.rs +++ b/codex-rs/cli/src/mcp_cmd.rs @@ -17,6 +17,7 @@ use codex_core::mcp::auth::compute_auth_statuses; use codex_core::protocol::McpAuthStatus; use codex_rmcp_client::delete_oauth_tokens; use codex_rmcp_client::perform_oauth_login; +use codex_rmcp_client::supports_oauth_login; /// [experimental] Launch Codex as an MCP server or manage configured MCP servers. /// @@ -189,7 +190,10 @@ impl McpCli { async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Result<()> { // Validate any provided overrides even though they are not currently applied. - config_overrides.parse_overrides().map_err(|e| anyhow!(e))?; + let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?; + let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default()) + .await + .context("failed to load configuration")?; let AddArgs { name, @@ -225,17 +229,21 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re } } AddMcpTransportArgs { - streamable_http: Some(streamable_http), + streamable_http: + Some(AddMcpStreamableHttpArgs { + url, + bearer_token_env_var, + }), .. } => McpServerTransportConfig::StreamableHttp { - url: streamable_http.url, - bearer_token_env_var: streamable_http.bearer_token_env_var, + url, + bearer_token_env_var, }, AddMcpTransportArgs { .. } => bail!("exactly one of --command or --url must be provided"), }; let new_entry = McpServerConfig { - transport, + transport: transport.clone(), enabled: true, startup_timeout_sec: None, tool_timeout_sec: None, @@ -246,7 +254,18 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re write_global_mcp_servers(&codex_home, &servers) .with_context(|| format!("failed to write MCP servers to {}", codex_home.display()))?; - println!("Added global MCP server '{name}'."); + println!("Added MCP server '{name}'."); + + if let McpServerTransportConfig::StreamableHttp { + url, + bearer_token_env_var: None, + } = transport + && matches!(supports_oauth_login(&url).await, Ok(true)) + { + println!("Detected OAuth support. Starting OAuth flow…"); + perform_oauth_login(&name, &url, config.mcp_oauth_credentials_store_mode).await?; + println!("Successfully logged in."); + } Ok(()) } diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 22b34b1cd0..68854aee18 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -17,6 +17,7 @@ use codex_apply_patch::ApplyPatchAction; use codex_protocol::ConversationId; use codex_protocol::protocol::ConversationPathResponseEvent; use codex_protocol::protocol::ExitedReviewModeEvent; +use codex_protocol::protocol::McpAuthStatus; use codex_protocol::protocol::ReviewRequest; use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::SessionSource; @@ -370,10 +371,25 @@ impl Session { ); let default_shell_fut = shell::default_user_shell(); let history_meta_fut = crate::message_history::history_metadata(&config); + let auth_statuses_fut = compute_auth_statuses( + config.mcp_servers.iter(), + config.mcp_oauth_credentials_store_mode, + ); // Join all independent futures. - let (rollout_recorder, mcp_res, default_shell, (history_log_id, history_entry_count)) = - tokio::join!(rollout_fut, mcp_fut, default_shell_fut, history_meta_fut); + let ( + rollout_recorder, + mcp_res, + default_shell, + (history_log_id, history_entry_count), + auth_statuses, + ) = tokio::join!( + rollout_fut, + mcp_fut, + default_shell_fut, + history_meta_fut, + auth_statuses_fut + ); let rollout_recorder = rollout_recorder.map_err(|e| { error!("failed to initialize rollout recorder: {e:#}"); @@ -400,11 +416,24 @@ impl Session { // Surface individual client start-up failures to the user. if !failed_clients.is_empty() { for (server_name, err) in failed_clients { - let message = format!("MCP client for `{server_name}` failed to start: {err:#}"); - error!("{message}"); + let log_message = + format!("MCP client for `{server_name}` failed to start: {err:#}"); + error!("{log_message}"); + let display_message = if matches!( + auth_statuses.get(&server_name), + Some(McpAuthStatus::NotLoggedIn) + ) { + format!( + "The {server_name} MCP server is not logged in. Run `codex mcp login {server_name}` to log in." + ) + } else { + log_message + }; post_session_configured_error_events.push(Event { id: INITIAL_SUBMIT_ID.to_owned(), - msg: EventMsg::Error(ErrorEvent { message }), + msg: EventMsg::Error(ErrorEvent { + message: display_message, + }), }); } } diff --git a/codex-rs/rmcp-client/src/auth_status.rs b/codex-rs/rmcp-client/src/auth_status.rs index 0281c0ffe8..5e32eed485 100644 --- a/codex-rs/rmcp-client/src/auth_status.rs +++ b/codex-rs/rmcp-client/src/auth_status.rs @@ -44,7 +44,7 @@ pub async fn determine_streamable_http_auth_status( } /// Attempt to determine whether a streamable HTTP MCP server advertises OAuth login. -async fn supports_oauth_login(url: &str) -> Result { +pub async fn supports_oauth_login(url: &str) -> Result { let base_url = Url::parse(url)?; let client = Client::builder().timeout(DISCOVERY_TIMEOUT).build()?; diff --git a/codex-rs/rmcp-client/src/lib.rs b/codex-rs/rmcp-client/src/lib.rs index 05412da184..ca99a7bb90 100644 --- a/codex-rs/rmcp-client/src/lib.rs +++ b/codex-rs/rmcp-client/src/lib.rs @@ -7,6 +7,7 @@ mod rmcp_client; mod utils; pub use auth_status::determine_streamable_http_auth_status; +pub use auth_status::supports_oauth_login; pub use codex_protocol::protocol::McpAuthStatus; pub use oauth::OAuthCredentialsStoreMode; pub use oauth::StoredOAuthTokens; From f123bdd16fd88243e9eb917291ba803ce96a1a44 Mon Sep 17 00:00:00 2001 From: Gabriel Peal Date: Tue, 14 Oct 2025 21:51:07 -0700 Subject: [PATCH 2/2] Fix test --- codex-rs/cli/src/mcp_cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codex-rs/cli/src/mcp_cmd.rs b/codex-rs/cli/src/mcp_cmd.rs index 8813e2b61a..b753fbee98 100644 --- a/codex-rs/cli/src/mcp_cmd.rs +++ b/codex-rs/cli/src/mcp_cmd.rs @@ -254,7 +254,7 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re write_global_mcp_servers(&codex_home, &servers) .with_context(|| format!("failed to write MCP servers to {}", codex_home.display()))?; - println!("Added MCP server '{name}'."); + println!("Added global MCP server '{name}'."); if let McpServerTransportConfig::StreamableHttp { url,