Skip to content

Commit 6ff84da

Browse files
test(conformance): port auth/pre-registration scenario from v1.x
Forward-ports PR #1545 from v1.x. The SDK already skips DCR when clientInformation() returns pre-populated credentials; only the conformance adapter wiring was missing. - withOAuthRetry: accept optional existingProvider - everythingClient: register auth/pre-registration handler - expected-failures: drop now-passing scenario
1 parent 62ff4c2 commit 6ff84da

3 files changed

Lines changed: 55 additions & 10 deletions

File tree

test/conformance/expected-failures.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22
# CI exits 0 if only these fail, exits 1 on unexpected failures or stale entries.
33

44
client:
5-
- auth/pre-registration
65
- auth/cross-app-access-complete-flow

test/conformance/src/everythingClient.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import { Client, ClientCredentialsProvider, PrivateKeyJwtProvider, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
1616
import * as z from 'zod/v4';
1717

18+
import { ConformanceOAuthProvider } from './helpers/conformanceOAuthProvider.js';
1819
import { logger } from './helpers/logger.js';
1920
import { handle401, withOAuthRetry } from './helpers/withOAuthRetry.js';
2021

@@ -42,6 +43,11 @@ const ClientConformanceContextSchema = z.discriminatedUnion('name', [
4243
name: z.literal('auth/client-credentials-basic'),
4344
client_id: z.string(),
4445
client_secret: z.string()
46+
}),
47+
z.object({
48+
name: z.literal('auth/pre-registration'),
49+
client_id: z.string(),
50+
client_secret: z.string()
4551
})
4652
]);
4753

@@ -240,6 +246,43 @@ async function runClientCredentialsBasic(serverUrl: string): Promise<void> {
240246

241247
registerScenario('auth/client-credentials-basic', runClientCredentialsBasic);
242248

249+
// ============================================================================
250+
// Pre-registration scenario (no dynamic client registration)
251+
// ============================================================================
252+
253+
async function runPreRegistrationClient(serverUrl: string): Promise<void> {
254+
const ctx = parseContext();
255+
if (ctx.name !== 'auth/pre-registration') {
256+
throw new Error(`Expected pre-registration context, got ${ctx.name}`);
257+
}
258+
259+
// Create a provider pre-populated with registered credentials,
260+
// so the SDK skips dynamic client registration.
261+
const provider = new ConformanceOAuthProvider('http://localhost:3000/callback', {
262+
client_name: 'conformance-pre-registration',
263+
redirect_uris: ['http://localhost:3000/callback']
264+
});
265+
provider.saveClientInformation({
266+
client_id: ctx.client_id,
267+
client_secret: ctx.client_secret,
268+
redirect_uris: ['http://localhost:3000/callback']
269+
});
270+
271+
const oauthFetch = withOAuthRetry('conformance-pre-registration', new URL(serverUrl), handle401, undefined, provider)(fetch);
272+
273+
const client = new Client({ name: 'conformance-pre-registration', version: '1.0.0' }, { capabilities: {} });
274+
const transport = new StreamableHTTPClientTransport(new URL(serverUrl), {
275+
fetch: oauthFetch
276+
});
277+
278+
await client.connect(transport);
279+
await client.listTools();
280+
await client.callTool({ name: 'test-tool', arguments: {} });
281+
await transport.close();
282+
}
283+
284+
registerScenario('auth/pre-registration', runPreRegistrationClient);
285+
243286
// ============================================================================
244287
// Elicitation defaults scenario
245288
// ============================================================================

test/conformance/src/helpers/withOAuthRetry.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,19 @@ export const withOAuthRetry = (
5555
clientName: string,
5656
baseUrl?: string | URL,
5757
handle401Fn: typeof handle401 = handle401,
58-
clientMetadataUrl?: string
58+
clientMetadataUrl?: string,
59+
existingProvider?: ConformanceOAuthProvider
5960
): Middleware => {
60-
const provider = new ConformanceOAuthProvider(
61-
'http://localhost:3000/callback',
62-
{
63-
client_name: clientName,
64-
redirect_uris: ['http://localhost:3000/callback']
65-
},
66-
clientMetadataUrl
67-
);
61+
const provider =
62+
existingProvider ??
63+
new ConformanceOAuthProvider(
64+
'http://localhost:3000/callback',
65+
{
66+
client_name: clientName,
67+
redirect_uris: ['http://localhost:3000/callback']
68+
},
69+
clientMetadataUrl
70+
);
6871
return (next: FetchLike) => {
6972
return async (input: string | URL, init?: RequestInit): Promise<Response> => {
7073
const makeRequest = async (): Promise<Response> => {

0 commit comments

Comments
 (0)