@@ -133,6 +133,78 @@ describe("OAuthConformanceTest", () => {
133133 ] ) ;
134134 } ) ;
135135
136+ it ( "fails early when CIMD is requested but AS does not advertise client_id_metadata_document_supported" , async ( ) => {
137+ const serverUrl = "https://mcp.example.com/mcp" ;
138+ const resourceMetadataUrl =
139+ "https://mcp.example.com/.well-known/oauth-protected-resource/mcp" ;
140+ const authServerUrl = "https://auth.example.com" ;
141+
142+ const fetchFn : typeof fetch = jest . fn ( async ( input , init ) => {
143+ const url = String ( input ) ;
144+ const headers = new Headers ( init ?. headers ) ;
145+
146+ if ( url === serverUrl && ! headers . get ( "Authorization" ) ) {
147+ return new Response ( null , {
148+ status : 401 ,
149+ headers : {
150+ "WWW-Authenticate" : `Bearer resource_metadata="${ resourceMetadataUrl } "` ,
151+ } ,
152+ } ) ;
153+ }
154+
155+ if ( url === resourceMetadataUrl ) {
156+ return jsonResponse ( {
157+ resource : serverUrl ,
158+ authorization_servers : [ authServerUrl ] ,
159+ } ) ;
160+ }
161+
162+ if ( url === `${ authServerUrl } /.well-known/oauth-authorization-server` ) {
163+ return jsonResponse ( {
164+ issuer : authServerUrl ,
165+ authorization_endpoint : `${ authServerUrl } /authorize` ,
166+ token_endpoint : `${ authServerUrl } /token` ,
167+ registration_endpoint : `${ authServerUrl } /register` ,
168+ response_types_supported : [ "code" ] ,
169+ grant_types_supported : [ "authorization_code" , "refresh_token" ] ,
170+ code_challenge_methods_supported : [ "S256" ] ,
171+ // NOTE: client_id_metadata_document_supported is NOT set
172+ } ) ;
173+ }
174+
175+ return jsonResponse ( { error : "not found" } , 404 ) ;
176+ } ) as typeof fetch ;
177+
178+ const test = new OAuthConformanceTest (
179+ {
180+ serverUrl,
181+ protocolVersion : "2025-11-25" ,
182+ registrationStrategy : "cimd" ,
183+ auth : { mode : "headless" } ,
184+ fetchFn,
185+ } ,
186+ {
187+ completeHeadlessAuthorization : jest . fn ( ) ,
188+ } ,
189+ ) ;
190+
191+ const result = await test . run ( ) ;
192+
193+ expect ( result . passed ) . toBe ( false ) ;
194+ const failedStep = result . steps . find ( ( step ) => step . status === "failed" ) ;
195+ expect ( failedStep ) . toMatchObject ( {
196+ status : "failed" ,
197+ error : {
198+ message : expect . stringContaining ( "client_id_metadata_document_supported" ) ,
199+ } ,
200+ } ) ;
201+ // Should NOT reach cimd_prepare or authorization_request
202+ const stepNames = result . steps . map ( ( step ) => step . step ) ;
203+ expect ( stepNames ) . toContain ( "received_authorization_server_metadata" ) ;
204+ expect ( stepNames ) . not . toContain ( "cimd_prepare" ) ;
205+ expect ( stepNames ) . not . toContain ( "authorization_request" ) ;
206+ } ) ;
207+
136208 it ( "captures multiple authorization server metadata attempts for the 2025-03-26 fallback flow" , async ( ) => {
137209 const serverUrl = "https://legacy.example.com/mcp" ;
138210 const rootMetadataUrl =
0 commit comments