From 164790d2bb7835801189551d8430d8a2a647124c Mon Sep 17 00:00:00 2001 From: starr-openai Date: Fri, 22 May 2026 11:26:56 +0200 Subject: [PATCH] test remote MCP OAuth callback flow --- .../tests/suite/v2/mcp_server_status.rs | 37 +++++++++++++++++++ .../src/bin/test_streamable_http_server.rs | 26 +++++++++++++ 2 files changed, 63 insertions(+) diff --git a/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs b/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs index 567ff0e6aa..af48b4ffd8 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs @@ -18,6 +18,7 @@ use axum::Router; use codex_app_server_protocol::ListMcpServerStatusParams; use codex_app_server_protocol::ListMcpServerStatusResponse; use codex_app_server_protocol::McpAuthStatus; +use codex_app_server_protocol::McpServerOauthLoginCompletedNotification; use codex_app_server_protocol::McpServerOauthLoginResponse; use codex_app_server_protocol::McpServerStatusDetail; use codex_app_server_protocol::RequestId; @@ -448,6 +449,42 @@ client_id = "codex-app-server-test" .authorization_url .starts_with(&format!("{}/oauth/authorize?", remote_mcp.server_url())) ); + let browser_response = reqwest::Client::new() + .get(&response.authorization_url) + .send() + .await?; + assert_eq!(browser_response.status(), reqwest::StatusCode::OK); + + let notification = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_notification_message("mcpServer/oauthLogin/completed"), + ) + .await??; + let notification: McpServerOauthLoginCompletedNotification = + serde_json::from_value(notification.params.context("oauth completion params")?)?; + assert_eq!( + notification, + McpServerOauthLoginCompletedNotification { + name: "remote-oauth".to_string(), + success: true, + error: None, + } + ); + + let request_id = mcp + .send_list_mcp_server_status_request(ListMcpServerStatusParams { + cursor: None, + limit: None, + detail: Some(McpServerStatusDetail::ToolsAndAuthOnly), + }) + .await?; + let response = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(request_id)), + ) + .await??; + let response: ListMcpServerStatusResponse = to_response(response)?; + assert_eq!(response.data[0].auth_status, McpAuthStatus::OAuth); Ok(()) } diff --git a/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs b/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs index d1c22f430c..413e82e47d 100644 --- a/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs +++ b/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs @@ -9,6 +9,7 @@ use std::time::Duration; use axum::Router; use axum::body::Body; use axum::extract::Json; +use axum::extract::Query; use axum::extract::State; use axum::http::HeaderMap; use axum::http::Method; @@ -19,6 +20,7 @@ use axum::http::header::CONTENT_TYPE; use axum::http::header::HOST; use axum::middleware; use axum::middleware::Next; +use axum::response::Redirect; use axum::response::Response; use axum::routing::get; use axum::routing::post; @@ -146,6 +148,8 @@ async fn main() -> Result<(), Box> { } }), ) + .route("/oauth/authorize", get(fake_oauth_authorize)) + .route("/oauth/token", post(fake_oauth_token)) .nest_service( "/mcp", StreamableHttpService::new( @@ -172,6 +176,28 @@ async fn main() -> Result<(), Box> { Ok(()) } +async fn fake_oauth_authorize( + Query(params): Query>, +) -> Result { + let redirect_uri = params.get("redirect_uri").ok_or(StatusCode::BAD_REQUEST)?; + let state = params.get("state").ok_or(StatusCode::BAD_REQUEST)?; + let mut callback_url = + reqwest::Url::parse(redirect_uri).map_err(|_| StatusCode::BAD_REQUEST)?; + callback_url + .query_pairs_mut() + .append_pair("code", "test-authorization-code") + .append_pair("state", state); + Ok(Redirect::temporary(callback_url.as_str())) +} + +async fn fake_oauth_token() -> Json { + Json(json!({ + "access_token": "test-access-token", + "refresh_token": "test-refresh-token", + "token_type": "bearer", + })) +} + impl ServerHandler for TestToolServer { fn get_info(&self) -> ServerInfo { ServerInfo {