From 4b91a7b3916ab631043f26f24c92aa4ac2577348 Mon Sep 17 00:00:00 2001 From: canvrno-oai Date: Tue, 24 Mar 2026 14:46:21 -0700 Subject: [PATCH] Suppress plugin-install MCP OAuth URL console spam (#15666) Switch plugin-install background MCP OAuth to a silent login path so the raw authorization URL is no longer printed in normal success cases. OAuth behavior is otherwise unchanged, with fallback URL output via stderr still shown only if browser launch fails. Before: https://github.com/user-attachments/assets/4bf387af-afa8-4b83-bcd6-4ca6b55da8db --- .../plugin_mcp_oauth.rs | 6 +- codex-rs/rmcp-client/src/lib.rs | 1 + .../rmcp-client/src/perform_oauth_login.rs | 76 +++++++++++++++++-- 3 files changed, 73 insertions(+), 10 deletions(-) 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 0c13f5ed4..fb9973e8c 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 0edd0f152..627b9f2e7 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 938f51b05..821fbf275 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!(