diff --git a/codex-rs/app-server/src/codex_message_processor/plugin_mcp_oauth.rs b/codex-rs/app-server/src/codex_message_processor/plugin_mcp_oauth.rs index 0c13f5ed4c1e..fb9973e8c509 100644 --- a/codex-rs/app-server/src/codex_message_processor/plugin_mcp_oauth.rs +++ b/codex-rs/app-server/src/codex_message_processor/plugin_mcp_oauth.rs @@ -9,7 +9,7 @@ use codex_core::mcp::auth::McpOAuthLoginSupport; use codex_core::mcp::auth::oauth_login_support; use codex_core::mcp::auth::resolve_oauth_scopes; use codex_core::mcp::auth::should_retry_without_scopes; -use codex_rmcp_client::perform_oauth_login; +use codex_rmcp_client::perform_oauth_login_silent; use tracing::warn; use super::CodexMessageProcessor; @@ -45,7 +45,7 @@ impl CodexMessageProcessor { let notification_name = name.clone(); tokio::spawn(async move { - let first_attempt = perform_oauth_login( + let first_attempt = perform_oauth_login_silent( &name, &oauth_config.url, store_mode, @@ -60,7 +60,7 @@ impl CodexMessageProcessor { let final_result = match first_attempt { Err(err) if should_retry_without_scopes(&resolved_scopes, &err) => { - perform_oauth_login( + perform_oauth_login_silent( &name, &oauth_config.url, store_mode, diff --git a/codex-rs/rmcp-client/src/lib.rs b/codex-rs/rmcp-client/src/lib.rs index 0edd0f15274d..627b9f2e714c 100644 --- a/codex-rs/rmcp-client/src/lib.rs +++ b/codex-rs/rmcp-client/src/lib.rs @@ -21,6 +21,7 @@ pub use perform_oauth_login::OAuthProviderError; pub use perform_oauth_login::OauthLoginHandle; pub use perform_oauth_login::perform_oauth_login; pub use perform_oauth_login::perform_oauth_login_return_url; +pub use perform_oauth_login::perform_oauth_login_silent; pub use rmcp::model::ElicitationAction; pub use rmcp_client::Elicitation; pub use rmcp_client::ElicitationResponse; diff --git a/codex-rs/rmcp-client/src/perform_oauth_login.rs b/codex-rs/rmcp-client/src/perform_oauth_login.rs index 938f51b053e7..821fbf275087 100644 --- a/codex-rs/rmcp-client/src/perform_oauth_login.rs +++ b/codex-rs/rmcp-client/src/perform_oauth_login.rs @@ -80,6 +80,61 @@ pub async fn perform_oauth_login( oauth_resource: Option<&str>, callback_port: Option, callback_url: Option<&str>, +) -> Result<()> { + perform_oauth_login_with_browser_output( + server_name, + server_url, + store_mode, + http_headers, + env_http_headers, + scopes, + oauth_resource, + callback_port, + callback_url, + /*emit_browser_url*/ true, + ) + .await +} + +#[allow(clippy::too_many_arguments)] +pub async fn perform_oauth_login_silent( + server_name: &str, + server_url: &str, + store_mode: OAuthCredentialsStoreMode, + http_headers: Option>, + env_http_headers: Option>, + scopes: &[String], + oauth_resource: Option<&str>, + callback_port: Option, + callback_url: Option<&str>, +) -> Result<()> { + perform_oauth_login_with_browser_output( + server_name, + server_url, + store_mode, + http_headers, + env_http_headers, + scopes, + oauth_resource, + callback_port, + callback_url, + /*emit_browser_url*/ false, + ) + .await +} + +#[allow(clippy::too_many_arguments)] +async fn perform_oauth_login_with_browser_output( + server_name: &str, + server_url: &str, + store_mode: OAuthCredentialsStoreMode, + http_headers: Option>, + env_http_headers: Option>, + scopes: &[String], + oauth_resource: Option<&str>, + callback_port: Option, + callback_url: Option<&str>, + emit_browser_url: bool, ) -> Result<()> { let headers = OauthHeaders { http_headers, @@ -98,7 +153,7 @@ pub async fn perform_oauth_login( /*timeout_secs*/ None, ) .await? - .finish() + .finish(emit_browser_url) .await } @@ -415,16 +470,23 @@ impl OauthLoginFlow { self.auth_url.clone() } - async fn finish(mut self) -> Result<()> { + async fn finish(mut self, emit_browser_url: bool) -> Result<()> { if self.launch_browser { let server_name = &self.server_name; let auth_url = &self.auth_url; - println!( - "Authorize `{server_name}` by opening this URL in your browser:\n{auth_url}\n" - ); + if emit_browser_url { + println!( + "Authorize `{server_name}` by opening this URL in your browser:\n{auth_url}\n" + ); + } if webbrowser::open(auth_url).is_err() { - println!("(Browser launch failed; please copy the URL above manually.)"); + if !emit_browser_url { + eprintln!( + "Authorize `{server_name}` by opening this URL in your browser:\n{auth_url}\n" + ); + } + eprintln!("(Browser launch failed; please copy the URL above manually.)"); } } @@ -477,7 +539,7 @@ impl OauthLoginFlow { let (tx, rx) = oneshot::channel(); tokio::spawn(async move { - let result = self.finish().await; + let result = self.finish(/*emit_browser_url*/ false).await; if let Err(err) = &result { eprintln!(