Skip to content

Commit 61b40af

Browse files
authored
fix: enumerate conformance client scenarios (#1008)
fixes #1001. Restores explicit dispatch arms for the ten client scenarios orphaned by #991: standard auth flow: - `auth/iss-*` - `auth/offline-access-*` - `auth/metadata-issuer-mismatch` - `auth/authorization-server-migration` plain connect/list client: `json-schema-ref-no-deref` `run_auth_client` now negotiates via `ClientLifecycleMode::Discover` — `auth/authorization-server-migration` tracked in #879
1 parent 8efc142 commit 61b40af

1 file changed

Lines changed: 52 additions & 6 deletions

File tree

conformance/src/bin/client.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,17 @@ async fn run_auth_client(server_url: &str, ctx: &ConformanceContext) -> anyhow::
286286
StreamableHttpClientTransportConfig::with_uri(server_url),
287287
);
288288

289-
let client = BasicClientHandler.serve(transport).await?;
289+
// The 2026-07-28 auth mocks require the modern per-request lifecycle
290+
// (MCP-Protocol-Version header on every request), so negotiate via the
291+
// discover lifecycle rather than the legacy initialize handshake.
292+
let client = BasicClientHandler
293+
.serve_with_lifecycle(
294+
transport,
295+
ClientLifecycleMode::Discover {
296+
preferred_versions: preferred_protocol_versions(),
297+
},
298+
)
299+
.await?;
290300
tracing::debug!("Connected (authenticated)");
291301

292302
let tools = client.list_tools(Default::default()).await?;
@@ -899,15 +909,22 @@ fn conformance_protocol_version() -> ProtocolVersion {
899909
.unwrap_or(ProtocolVersion::V_2026_07_28)
900910
}
901911

902-
/// Runs draft stateless scenarios through the public discover lifecycle and
903-
/// Streamable HTTP transport.
904-
async fn run_discover_client(server_url: &str) -> anyhow::Result<()> {
912+
/// Preferred protocol versions for discover-lifecycle negotiation: the
913+
/// runner-provided version first, then all other known versions newest-first.
914+
fn preferred_protocol_versions() -> Vec<ProtocolVersion> {
905915
let mut preferred_versions = vec![conformance_protocol_version()];
906916
for version in ProtocolVersion::KNOWN_VERSIONS.iter().rev() {
907917
if !preferred_versions.contains(version) {
908918
preferred_versions.push(version.clone());
909919
}
910920
}
921+
preferred_versions
922+
}
923+
924+
/// Runs draft stateless scenarios through the public discover lifecycle and
925+
/// Streamable HTTP transport.
926+
async fn run_discover_client(server_url: &str) -> anyhow::Result<()> {
927+
let preferred_versions = preferred_protocol_versions();
911928
let transport = StreamableHttpClientTransport::from_uri(server_url);
912929
let client = FullClientHandler
913930
.serve_with_lifecycle(
@@ -968,7 +985,12 @@ async fn main() -> anyhow::Result<()> {
968985
match scenario.as_str() {
969986
// Non-auth scenarios
970987
"initialize" => run_basic_client(&server_url).await?,
971-
"json-schema-ref-no-deref" => run_discover_client(&server_url).await?,
988+
// SEP-2106: the scenario serves a tool whose schema carries a network
989+
// `$ref`; the check passes when the client lists tools without
990+
// dereferencing (fetching) that URL. A plain connect → list_tools →
991+
// close is sufficient; the scenario's mock server does not implement
992+
// the discover lifecycle, so `run_discover_client` hangs against it.
993+
"json-schema-ref-no-deref" => run_basic_client(&server_url).await?,
972994
"tools_call" => run_tools_call_client(&server_url, &ctx).await?,
973995
"elicitation-sep1034-client-defaults" => {
974996
run_elicitation_defaults_client(&server_url).await?
@@ -994,7 +1016,31 @@ async fn main() -> anyhow::Result<()> {
9941016
| "auth/token-endpoint-auth-post"
9951017
| "auth/token-endpoint-auth-none"
9961018
| "auth/2025-03-26-oauth-metadata-backcompat"
997-
| "auth/2025-03-26-oauth-endpoint-fallback" => run_auth_client(&server_url, &ctx).await?,
1019+
| "auth/2025-03-26-oauth-endpoint-fallback"
1020+
// Offline access scope handling: positive/negative variants both run
1021+
// the well-behaved flow; the referee inspects the requested scopes.
1022+
| "auth/offline-access-scope"
1023+
| "auth/offline-access-not-supported"
1024+
// SEP-2468 (RFC 9207 iss / RFC 8414 §3.3 issuer-echo). The client
1025+
// captures `iss` from the authorization redirect and passes it to the
1026+
// callback handler; the SDK validates internally. Positive scenarios
1027+
// proceed to the token endpoint; negative scenarios error out (the
1028+
// referee sets `allowClientError`).
1029+
| "auth/iss-supported"
1030+
| "auth/iss-not-advertised"
1031+
| "auth/iss-supported-missing"
1032+
| "auth/iss-wrong-issuer"
1033+
| "auth/iss-unexpected"
1034+
| "auth/iss-normalized"
1035+
| "auth/metadata-issuer-mismatch"
1036+
| "auth/metadata-issuer-mismatch"
1037+
// SEP-2352: PRM `authorization_servers` switches between calls; a
1038+
// compliant client re-registers at the new AS. Known partial failure:
1039+
// the SDK lacks issuer-stamped credential storage (#879), so the
1040+
// `sep-2352-reregister-on-as-change` check fails. Left on the standard
1041+
// flow rather than fixture-orchestrated re-registration so the
1042+
// conformance result reflects real SDK behavior.
1043+
| "auth/authorization-server-migration" => run_auth_client(&server_url, &ctx).await?,
9981044

9991045
// Auth - scope step-up
10001046
"auth/scope-step-up" => run_auth_scope_step_up_client(&server_url, &ctx).await?,

0 commit comments

Comments
 (0)