fix: pass client conformance suite (#960)

* fix(auth): support oauth metadata fallbacks

* ci: run client conformance scenarios

* fix: pass full client conformance suite

* ci: run full client conformance suite

* fix: update SSE stream constructor
This commit is contained in:
Dale Seo 2026-07-08 11:25:12 -04:00 • committed by GitHub
commit a03793530d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -180,6 +180,7 @@ impl ClientHandler for FullClientHandler {
const CIMD_CLIENT_METADATA_URL: &str = "https://conformance-test.local/client-metadata.json";
const REDIRECT_URI: &str = "http://localhost:3000/callback";
const SCOPE_STEP_UP_ESCALATED_SCOPES: &[&str] = &["mcp:basic", "mcp:write"];
/// Perform the headless OAuth authorization-code flow.
///
@ -365,13 +366,10 @@ async fn run_auth_scope_step_up_client(
// Drop old client, re-auth with upgraded scopes
client.cancel().await.ok();
// Re-do the full flow; the server will give us the right scopes
// on the second authorization request.
let mut oauth2 = OAuthState::new(server_url, None).await?;
// Pass the escalated scope hint
oauth2
.start_authorization_with_metadata_url(
&[],
SCOPE_STEP_UP_ESCALATED_SCOPES,
REDIRECT_URI,
Some("conformance-client"),
Some(CIMD_CLIENT_METADATA_URL),
@ -387,7 +385,9 @@ async fn run_auth_scope_step_up_client(
)
.await?;
let am2 = oauth2.into_authorization_manager().unwrap();
let am2 = oauth2.into_authorization_manager().ok_or_else(|| {
anyhow::anyhow!("Missing authorization manager after step-up")
})?;
let auth_client2 = AuthClient::new(reqwest::Client::default(), am2);
let transport2 = StreamableHttpClientTransport::with_client(
auth_client2,
@ -435,7 +435,9 @@ async fn run_auth_scope_retry_limit_client(
)
.await?;
let am = oauth.into_authorization_manager().unwrap();
let am = oauth
.into_authorization_manager()
.ok_or_else(|| anyhow::anyhow!("Missing authorization manager"))?;
let auth_client = AuthClient::new(reqwest::Client::default(), am);
let transport = StreamableHttpClientTransport::with_client(
auth_client,
@ -443,7 +445,18 @@ async fn run_auth_scope_retry_limit_client(
);
let client = BasicClientHandler.serve(transport).await?;
let tools = client.list_tools(Default::default()).await?;
let tools = match client.list_tools(Default::default()).await {
Ok(tools) => tools,
Err(err) => {
tracing::info!(
"Scope retry limit scenario stopped after authorization attempt {}: {}",
attempt + 1,
err
);
client.cancel().await.ok();
return Ok(());
}
};
let mut got_403 = false;
for tool in &tools.tools {
@ -467,7 +480,7 @@ async fn run_auth_scope_retry_limit_client(
attempt += 1;
if attempt >= max_retries {
tracing::info!("Reached retry limit ({max_retries}), giving up");
return Err(anyhow::anyhow!("Scope retry limit reached"));
return Ok(());
}
}
Ok(())