Skip to content

Commit 03a4c0f

Browse files
Merge main (CIMD conformance test) and resolve conflict
2 parents 6c2646b + d303013 commit 03a4c0f

34 files changed

+304
-113
lines changed

examples/clients/typescript/auth-test-bad-prm.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
UnauthorizedError
88
} from '@modelcontextprotocol/sdk/client/auth.js';
99
import type { FetchLike } from '@modelcontextprotocol/sdk/shared/transport.js';
10-
import { withOAuthRetry } from './helpers/withOAuthRetry.js';
11-
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js';
12-
import { runAsCli } from './helpers/cliRunner.js';
13-
import { logger } from './helpers/logger.js';
10+
import { withOAuthRetry } from './helpers/withOAuthRetry';
11+
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider';
12+
import { runAsCli } from './helpers/cliRunner';
13+
import { logger } from './helpers/logger';
1414

1515
/**
1616
* Broken client that always uses root-based PRM discovery.

examples/clients/typescript/auth-test-ignore-403.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import {
88
UnauthorizedError
99
} from '@modelcontextprotocol/sdk/client/auth.js';
1010
import type { FetchLike } from '@modelcontextprotocol/sdk/shared/transport.js';
11-
import { withOAuthRetry } from './helpers/withOAuthRetry.js';
12-
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js';
13-
import { runAsCli } from './helpers/cliRunner.js';
14-
import { logger } from './helpers/logger.js';
11+
import { withOAuthRetry } from './helpers/withOAuthRetry';
12+
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider';
13+
import { runAsCli } from './helpers/cliRunner';
14+
import { logger } from './helpers/logger';
1515

1616
/**
1717
* Broken client that only responds to 401, not 403.

examples/clients/typescript/auth-test-ignore-scope.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import {
88
UnauthorizedError
99
} from '@modelcontextprotocol/sdk/client/auth.js';
1010
import type { FetchLike } from '@modelcontextprotocol/sdk/shared/transport.js';
11-
import { withOAuthRetry } from './helpers/withOAuthRetry.js';
12-
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js';
13-
import { runAsCli } from './helpers/cliRunner.js';
14-
import { logger } from './helpers/logger.js';
11+
import { withOAuthRetry } from './helpers/withOAuthRetry';
12+
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider';
13+
import { runAsCli } from './helpers/cliRunner';
14+
import { logger } from './helpers/logger';
1515

1616
/**
1717
* Broken client that ignores the scope from WWW-Authenticate header.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env node
2+
3+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
4+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
5+
import { withOAuthRetry } from './helpers/withOAuthRetry';
6+
import { runAsCli } from './helpers/cliRunner';
7+
import { logger } from './helpers/logger';
8+
9+
/**
10+
* Non-compliant client that doesn't use CIMD (Client ID Metadata Document).
11+
*
12+
* This client intentionally omits the clientMetadataUrl parameter when the server
13+
* advertises client_id_metadata_document_supported=true. A compliant client should
14+
* use CIMD when the server supports it, but this client falls back to DCR (Dynamic
15+
* Client Registration) instead.
16+
*
17+
* Used to test that conformance checks detect clients that don't properly
18+
* implement CIMD support.
19+
*/
20+
export async function runClient(serverUrl: string): Promise<void> {
21+
const client = new Client(
22+
{ name: 'test-auth-client-no-cimd', version: '1.0.0' },
23+
{ capabilities: {} }
24+
);
25+
26+
// Non-compliant: omitting clientMetadataUrl causes fallback to DCR
27+
// A compliant client would pass a clientMetadataUrl here when the server
28+
// advertises client_id_metadata_document_supported=true
29+
const oauthFetch = withOAuthRetry(
30+
'test-auth-client-no-cimd',
31+
new URL(serverUrl)
32+
)(fetch);
33+
34+
const transport = new StreamableHTTPClientTransport(new URL(serverUrl), {
35+
fetch: oauthFetch
36+
});
37+
38+
await client.connect(transport);
39+
logger.debug('Connected to MCP server (without CIMD)');
40+
41+
await client.listTools();
42+
logger.debug('Successfully listed tools');
43+
44+
await client.callTool({ name: 'test-tool', arguments: {} });
45+
logger.debug('Successfully called tool');
46+
47+
await transport.close();
48+
logger.debug('Connection closed successfully');
49+
}
50+
51+
runAsCli(runClient, import.meta.url, 'auth-test-no-cimd <server-url>');

examples/clients/typescript/auth-test-partial-scopes.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import {
88
UnauthorizedError
99
} from '@modelcontextprotocol/sdk/client/auth.js';
1010
import type { FetchLike } from '@modelcontextprotocol/sdk/shared/transport.js';
11-
import { withOAuthRetry } from './helpers/withOAuthRetry.js';
12-
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js';
13-
import { runAsCli } from './helpers/cliRunner.js';
14-
import { logger } from './helpers/logger.js';
11+
import { withOAuthRetry } from './helpers/withOAuthRetry';
12+
import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider';
13+
import { runAsCli } from './helpers/cliRunner';
14+
import { logger } from './helpers/logger';
1515

1616
/**
1717
* Broken client that only requests a subset of scopes.

examples/clients/typescript/auth-test.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
44
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
5-
import { withOAuthRetry } from './helpers/withOAuthRetry.js';
6-
import { runAsCli } from './helpers/cliRunner.js';
7-
import { logger } from './helpers/logger.js';
5+
import { withOAuthRetry, handle401 } from './helpers/withOAuthRetry';
6+
import { runAsCli } from './helpers/cliRunner';
7+
import { logger } from './helpers/logger';
8+
9+
/**
10+
* Fixed client metadata URL for CIMD conformance tests.
11+
* When server supports client_id_metadata_document_supported, this URL
12+
* will be used as the client_id instead of doing dynamic registration.
13+
*/
14+
const CIMD_CLIENT_METADATA_URL =
15+
'https://conformance-test.local/client-metadata.json';
816

917
/**
1018
* Well-behaved auth client that follows all OAuth protocols correctly.
@@ -17,7 +25,9 @@ export async function runClient(serverUrl: string): Promise<void> {
1725

1826
const oauthFetch = withOAuthRetry(
1927
'test-auth-client',
20-
new URL(serverUrl)
28+
new URL(serverUrl),
29+
handle401,
30+
CIMD_CLIENT_METADATA_URL
2131
)(fetch);
2232

2333
const transport = new StreamableHTTPClientTransport(new URL(serverUrl), {

examples/clients/typescript/helpers/ConformanceOAuthProvider.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ export class ConformanceOAuthProvider implements OAuthClientProvider {
2727
return this._clientMetadata;
2828
}
2929

30+
get clientMetadataUrl(): string | undefined {
31+
return this._clientMetadataUrl?.toString();
32+
}
33+
3034
clientInformation(): OAuthClientInformation | undefined {
31-
if (this._clientMetadataUrl) {
32-
console.log('Using client ID metadata URL');
33-
return {
34-
client_id: this._clientMetadataUrl.toString()
35-
};
36-
}
3735
return this._clientInformation;
3836
}
3937

examples/clients/typescript/helpers/withOAuthRetry.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,16 @@ export const handle401 = async (
6060
export const withOAuthRetry = (
6161
clientName: string,
6262
baseUrl?: string | URL,
63-
handle401Fn: typeof handle401 = handle401
63+
handle401Fn: typeof handle401 = handle401,
64+
clientMetadataUrl?: string
6465
): Middleware => {
6566
const provider = new ConformanceOAuthProvider(
6667
'http://localhost:3000/callback',
6768
{
6869
client_name: clientName,
6970
redirect_uris: ['http://localhost:3000/callback']
70-
}
71+
},
72+
clientMetadataUrl
7173
);
7274
return (next: FetchLike) => {
7375
return async (

src/checks/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ConformanceCheck, CheckStatus } from '../types.js';
1+
import { ConformanceCheck, CheckStatus } from '../types';
22

33
export function createServerInfoCheck(serverInfo: {
44
name: string;

src/checks/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// Namespaced exports for client checks
2-
import * as client from './client.js';
2+
import * as client from './client';
33

44
export const clientChecks = client;

0 commit comments

Comments
 (0)