From a1d6d5dd29ee059aeb7b9d039b15f6407226a1c9 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Tue, 18 Nov 2025 17:56:13 +0000 Subject: [PATCH 1/5] add cimd test and negative test --- .../clients/typescript/auth-test-no-cimd.ts | 44 +++++++++ examples/clients/typescript/auth-test.ts | 14 ++- .../helpers/ConformanceOAuthProvider.ts | 10 +- .../typescript/helpers/withOAuthRetry.ts | 6 +- src/scenarios/client/auth/basic-cimd.ts | 99 +++++++++++++++++++ .../client/auth/helpers/createAuthServer.ts | 10 ++ src/scenarios/client/auth/index.test.ts | 8 ++ src/scenarios/client/auth/index.ts | 2 + src/scenarios/client/auth/spec-references.ts | 8 ++ 9 files changed, 191 insertions(+), 10 deletions(-) create mode 100644 examples/clients/typescript/auth-test-no-cimd.ts create mode 100644 src/scenarios/client/auth/basic-cimd.ts diff --git a/examples/clients/typescript/auth-test-no-cimd.ts b/examples/clients/typescript/auth-test-no-cimd.ts new file mode 100644 index 0000000..7956efb --- /dev/null +++ b/examples/clients/typescript/auth-test-no-cimd.ts @@ -0,0 +1,44 @@ +#!/usr/bin/env node + +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import { withOAuthRetry } from './helpers/withOAuthRetry.js'; +import { runAsCli } from './helpers/cliRunner.js'; +import { logger } from './helpers/logger.js'; + +/** + * Client that doesn't use CIMD even when server supports it. + * BUG: Doesn't provide clientMetadataUrl, so falls back to DCR. + */ +export async function runClient(serverUrl: string): Promise { + const client = new Client( + { name: 'test-auth-client-no-cimd', version: '1.0.0' }, + { capabilities: {} } + ); + + // BUG: Not passing clientMetadataUrl, so client will use DCR + // even when server supports client_id_metadata_document_supported + const oauthFetch = withOAuthRetry( + 'test-auth-client-no-cimd', + new URL(serverUrl) + // Missing: handle401, clientMetadataUrl + )(fetch); + + const transport = new StreamableHTTPClientTransport(new URL(serverUrl), { + fetch: oauthFetch + }); + + await client.connect(transport); + logger.debug('Connected to MCP server (without CIMD)'); + + await client.listTools(); + logger.debug('Successfully listed tools'); + + await client.callTool({ name: 'test-tool', arguments: {} }); + logger.debug('Successfully called tool'); + + await transport.close(); + logger.debug('Connection closed successfully'); +} + +runAsCli(runClient, import.meta.url, 'auth-test-no-cimd '); diff --git a/examples/clients/typescript/auth-test.ts b/examples/clients/typescript/auth-test.ts index 29e62a5..df6c71e 100644 --- a/examples/clients/typescript/auth-test.ts +++ b/examples/clients/typescript/auth-test.ts @@ -2,10 +2,18 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; -import { withOAuthRetry } from './helpers/withOAuthRetry.js'; +import { withOAuthRetry, handle401 } from './helpers/withOAuthRetry.js'; import { runAsCli } from './helpers/cliRunner.js'; import { logger } from './helpers/logger.js'; +/** + * Fixed client metadata URL for CIMD conformance tests. + * When server supports client_id_metadata_document_supported, this URL + * will be used as the client_id instead of doing dynamic registration. + */ +const CIMD_CLIENT_METADATA_URL = + 'https://conformance-test.local/client-metadata.json'; + /** * Well-behaved auth client that follows all OAuth protocols correctly. */ @@ -17,7 +25,9 @@ export async function runClient(serverUrl: string): Promise { const oauthFetch = withOAuthRetry( 'test-auth-client', - new URL(serverUrl) + new URL(serverUrl), + handle401, + CIMD_CLIENT_METADATA_URL )(fetch); const transport = new StreamableHTTPClientTransport(new URL(serverUrl), { diff --git a/examples/clients/typescript/helpers/ConformanceOAuthProvider.ts b/examples/clients/typescript/helpers/ConformanceOAuthProvider.ts index 2db99d9..aa3aef5 100644 --- a/examples/clients/typescript/helpers/ConformanceOAuthProvider.ts +++ b/examples/clients/typescript/helpers/ConformanceOAuthProvider.ts @@ -27,13 +27,11 @@ export class ConformanceOAuthProvider implements OAuthClientProvider { return this._clientMetadata; } + get clientMetadataUrl(): string | undefined { + return this._clientMetadataUrl?.toString(); + } + clientInformation(): OAuthClientInformation | undefined { - if (this._clientMetadataUrl) { - console.log('Using client ID metadata URL'); - return { - client_id: this._clientMetadataUrl.toString() - }; - } return this._clientInformation; } diff --git a/examples/clients/typescript/helpers/withOAuthRetry.ts b/examples/clients/typescript/helpers/withOAuthRetry.ts index c95ab96..497f4a4 100644 --- a/examples/clients/typescript/helpers/withOAuthRetry.ts +++ b/examples/clients/typescript/helpers/withOAuthRetry.ts @@ -60,14 +60,16 @@ export const handle401 = async ( export const withOAuthRetry = ( clientName: string, baseUrl?: string | URL, - handle401Fn: typeof handle401 = handle401 + handle401Fn: typeof handle401 = handle401, + clientMetadataUrl?: string ): Middleware => { const provider = new ConformanceOAuthProvider( 'http://localhost:3000/callback', { client_name: clientName, redirect_uris: ['http://localhost:3000/callback'] - } + }, + clientMetadataUrl ); return (next: FetchLike) => { return async ( diff --git a/src/scenarios/client/auth/basic-cimd.ts b/src/scenarios/client/auth/basic-cimd.ts new file mode 100644 index 0000000..e1fb82e --- /dev/null +++ b/src/scenarios/client/auth/basic-cimd.ts @@ -0,0 +1,99 @@ +import type { Scenario, ConformanceCheck } from '../../../types.js'; +import { ScenarioUrls } from '../../../types.js'; +import { createAuthServer } from './helpers/createAuthServer.js'; +import { createServer } from './helpers/createServer.js'; +import { ServerLifecycle } from './helpers/serverLifecycle.js'; +import { SpecReferences } from './spec-references.js'; + +/** + * Fixed client metadata URL that clients should use for CIMD tests. + * This URL doesn't need to resolve - the server will accept it as-is + * and use hardcoded metadata. + */ +export const CIMD_CLIENT_METADATA_URL = + 'https://conformance-test.local/client-metadata.json'; + +/** + * Scenario: Client ID Metadata Documents (SEP-991/URL-based client IDs) + * + * Tests that when a server advertises client_id_metadata_document_supported=true, + * clients SHOULD use a URL as their client_id instead of using dynamic client + * registration. + */ +export class AuthBasicCIMDScenario implements Scenario { + name = 'auth/basic-cimd'; + description = + 'Tests OAuth flow with Client ID Metadata Documents (SEP-991/URL-based client IDs). Server advertises client_id_metadata_document_supported=true and client should use URL as client_id instead of DCR.'; + private authServer = new ServerLifecycle(); + private server = new ServerLifecycle(); + private checks: ConformanceCheck[] = []; + + async start(): Promise { + this.checks = []; + + const authApp = createAuthServer(this.checks, this.authServer.getUrl, { + clientIdMetadataDocumentSupported: true, + onAuthorizationRequest: (data) => { + // Check if client used URL-based client ID + const usedUrlClientId = data.clientId === CIMD_CLIENT_METADATA_URL; + this.checks.push({ + id: 'cimd-client-id-used', + name: 'Client ID Metadata Document Usage', + description: usedUrlClientId + ? 'Client correctly used URL-based client ID when server supports client_id_metadata_document_supported' + : 'Client SHOULD use URL-based client ID when server advertises client_id_metadata_document_supported=true', + status: usedUrlClientId ? 'SUCCESS' : 'WARNING', + timestamp: data.timestamp, + specReferences: [ + SpecReferences.MCP_CLIENT_ID_METADATA_DOCUMENTS, + SpecReferences.IETF_CIMD + ], + details: { + expectedClientId: CIMD_CLIENT_METADATA_URL, + actualClientId: data.clientId || 'none' + } + }); + } + }); + + await this.authServer.start(authApp); + + const app = createServer( + this.checks, + this.server.getUrl, + this.authServer.getUrl + ); + + await this.server.start(app); + + return { serverUrl: `${this.server.getUrl()}/mcp` }; + } + + async stop() { + await this.authServer.stop(); + await this.server.stop(); + } + + getChecks(): ConformanceCheck[] { + // Ensure we have the CIMD check - if not, the client didn't make an auth request + const hasCimdCheck = this.checks.some( + (c) => c.id === 'cimd-client-id-used' + ); + if (!hasCimdCheck) { + this.checks.push({ + id: 'cimd-client-id-used', + name: 'Client ID Metadata Document Usage', + description: + 'Client did not make an authorization request to test CIMD support', + status: 'FAILURE', + timestamp: new Date().toISOString(), + specReferences: [ + SpecReferences.MCP_CLIENT_ID_METADATA_DOCUMENTS, + SpecReferences.IETF_CIMD + ] + }); + } + + return this.checks; + } +} diff --git a/src/scenarios/client/auth/helpers/createAuthServer.ts b/src/scenarios/client/auth/helpers/createAuthServer.ts index 7828990..40527b8 100644 --- a/src/scenarios/client/auth/helpers/createAuthServer.ts +++ b/src/scenarios/client/auth/helpers/createAuthServer.ts @@ -10,6 +10,7 @@ export interface AuthServerOptions { loggingEnabled?: boolean; routePrefix?: string; scopesSupported?: string[]; + clientIdMetadataDocumentSupported?: boolean; tokenVerifier?: MockTokenVerifier; onTokenRequest?: (requestData: { scope?: string; @@ -17,6 +18,7 @@ export interface AuthServerOptions { timestamp: string; }) => { token: string; scopes: string[] }; onAuthorizationRequest?: (requestData: { + clientId?: string; scope?: string; timestamp: string; }) => void; @@ -33,6 +35,7 @@ export function createAuthServer( loggingEnabled = true, routePrefix = '', scopesSupported, + clientIdMetadataDocumentSupported, tokenVerifier, onTokenRequest, onAuthorizationRequest @@ -93,6 +96,12 @@ export function createAuthServer( metadata.scopes_supported = scopesSupported; } + // Add client_id_metadata_document_supported if provided + if (clientIdMetadataDocumentSupported !== undefined) { + metadata.client_id_metadata_document_supported = + clientIdMetadataDocumentSupported; + } + // Add OpenID Configuration specific fields if (isOpenIdConfiguration) { metadata.jwks_uri = `${getAuthBaseUrl()}/.well-known/jwks.json`; @@ -123,6 +132,7 @@ export function createAuthServer( if (onAuthorizationRequest) { onAuthorizationRequest({ + clientId: req.query.client_id as string | undefined, scope: scopeParam, timestamp }); diff --git a/src/scenarios/client/auth/index.test.ts b/src/scenarios/client/auth/index.test.ts index 7ac4beb..b733f00 100644 --- a/src/scenarios/client/auth/index.test.ts +++ b/src/scenarios/client/auth/index.test.ts @@ -5,6 +5,7 @@ import { } from './test_helpers/testClient.js'; import { runClient as goodClient } from '../../../../examples/clients/typescript/auth-test.js'; import { runClient as badPrmClient } from '../../../../examples/clients/typescript/auth-test-bad-prm.js'; +import { runClient as noCimdClient } from '../../../../examples/clients/typescript/auth-test-no-cimd.js'; import { runClient as ignoreScopeClient } from '../../../../examples/clients/typescript/auth-test-ignore-scope.js'; import { runClient as partialScopesClient } from '../../../../examples/clients/typescript/auth-test-partial-scopes.js'; import { runClient as ignore403Client } from '../../../../examples/clients/typescript/auth-test-ignore-403.js'; @@ -76,4 +77,11 @@ describe('Negative tests', () => { 'scope-step-up-escalation' ]); }); + + test('client uses DCR instead of CIMD when server supports it', async () => { + const runner = new InlineClientRunner(noCimdClient); + await runClientAgainstScenario(runner, 'auth/basic-cimd', [ + 'cimd-client-id-used' + ]); + }); }); diff --git a/src/scenarios/client/auth/index.ts b/src/scenarios/client/auth/index.ts index 24bce85..6d4d400 100644 --- a/src/scenarios/client/auth/index.ts +++ b/src/scenarios/client/auth/index.ts @@ -1,5 +1,6 @@ import { Scenario } from '../../../types'; import { AuthBasicDCRScenario } from './basic-dcr.js'; +import { AuthBasicCIMDScenario } from './basic-cimd.js'; import { AuthBasicMetadataVar1Scenario, AuthBasicMetadataVar2Scenario, @@ -18,6 +19,7 @@ import { export const authScenariosList: Scenario[] = [ new AuthBasicDCRScenario(), + new AuthBasicCIMDScenario(), new AuthBasicMetadataVar1Scenario(), new AuthBasicMetadataVar2Scenario(), new AuthBasicMetadataVar3Scenario(), diff --git a/src/scenarios/client/auth/spec-references.ts b/src/scenarios/client/auth/spec-references.ts index 3642396..6f6b824 100644 --- a/src/scenarios/client/auth/spec-references.ts +++ b/src/scenarios/client/auth/spec-references.ts @@ -52,5 +52,13 @@ export const SpecReferences: { [key: string]: SpecReference } = { MCP_AUTH_ERROR_HANDLING: { id: 'MCP-Auth-error-handling', url: 'https://modelcontextprotocol.io/specification/draft/basic/authorization#error-handling' + }, + MCP_CLIENT_ID_METADATA_DOCUMENTS: { + id: 'MCP-Client-ID-Metadata-Documents', + url: 'https://modelcontextprotocol.io/specification/draft/basic/authorization#client-id-metadata-documents' + }, + IETF_CIMD: { + id: 'IETF-OAuth-Client-ID-Metadata-Document', + url: 'https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00' } }; From 1048f6a4934f65f0a421ed1a6b8fbbbcb91fd00f Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Thu, 20 Nov 2025 14:49:17 +0000 Subject: [PATCH 2/5] Improve auth-test-no-cimd.ts documentation for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback: expanded comments to better explain what CIMD is, why this client is non-compliant, and what behavior it's testing. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../clients/typescript/auth-test-no-cimd.ts | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/examples/clients/typescript/auth-test-no-cimd.ts b/examples/clients/typescript/auth-test-no-cimd.ts index 7956efb..ad46c9f 100644 --- a/examples/clients/typescript/auth-test-no-cimd.ts +++ b/examples/clients/typescript/auth-test-no-cimd.ts @@ -2,13 +2,20 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; -import { withOAuthRetry } from './helpers/withOAuthRetry.js'; -import { runAsCli } from './helpers/cliRunner.js'; -import { logger } from './helpers/logger.js'; +import { withOAuthRetry } from './helpers/withOAuthRetry'; +import { runAsCli } from './helpers/cliRunner'; +import { logger } from './helpers/logger'; /** - * Client that doesn't use CIMD even when server supports it. - * BUG: Doesn't provide clientMetadataUrl, so falls back to DCR. + * Non-compliant client that doesn't use CIMD (Client ID Metadata Document). + * + * This client intentionally omits the clientMetadataUrl parameter when the server + * advertises client_id_metadata_document_supported=true. A compliant client should + * use CIMD when the server supports it, but this client falls back to DCR (Dynamic + * Client Registration) instead. + * + * Used to test that conformance checks detect clients that don't properly + * implement CIMD support. */ export async function runClient(serverUrl: string): Promise { const client = new Client( @@ -16,12 +23,12 @@ export async function runClient(serverUrl: string): Promise { { capabilities: {} } ); - // BUG: Not passing clientMetadataUrl, so client will use DCR - // even when server supports client_id_metadata_document_supported + // Non-compliant: omitting clientMetadataUrl causes fallback to DCR + // A compliant client would pass a clientMetadataUrl here when the server + // advertises client_id_metadata_document_supported=true const oauthFetch = withOAuthRetry( 'test-auth-client-no-cimd', new URL(serverUrl) - // Missing: handle401, clientMetadataUrl )(fetch); const transport = new StreamableHTTPClientTransport(new URL(serverUrl), { From c0ac704b918ca53d7f3d8fb592cb6ed14515ca30 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Thu, 20 Nov 2025 14:54:28 +0000 Subject: [PATCH 3/5] Remove .js extensions from relative imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tsx doesn't require .js extensions for TypeScript imports. Keeps .js for @modelcontextprotocol/sdk imports as those are external packages. Also improves auth-test-no-cimd.ts documentation for clarity. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../clients/typescript/auth-test-bad-prm.ts | 8 +++---- .../typescript/auth-test-ignore-403.ts | 8 +++---- .../typescript/auth-test-ignore-scope.ts | 8 +++---- .../typescript/auth-test-partial-scopes.ts | 8 +++---- examples/clients/typescript/auth-test.ts | 6 ++--- src/checks/client.ts | 2 +- src/checks/index.ts | 2 +- src/scenarios/client/auth/basic-cimd.ts | 12 +++++----- src/scenarios/client/auth/basic-dcr.ts | 12 +++++----- src/scenarios/client/auth/basic-metadata.ts | 12 +++++----- .../client/auth/helpers/createAuthServer.ts | 8 +++---- .../client/auth/helpers/createServer.ts | 8 +++---- .../client/auth/helpers/mockTokenVerifier.ts | 4 ++-- src/scenarios/client/auth/index.test.ts | 18 +++++++------- src/scenarios/client/auth/index.ts | 10 ++++---- .../client/auth/march-spec-backcompat.ts | 12 +++++----- src/scenarios/client/auth/scope-handling.ts | 14 +++++------ .../client/auth/test_helpers/testClient.ts | 2 +- src/scenarios/client/elicitation-defaults.ts | 6 ++--- src/scenarios/client/initialize.ts | 4 ++-- src/scenarios/client/tools_call.ts | 6 ++--- src/scenarios/index.ts | 24 +++++++++---------- src/scenarios/request-logger.ts | 2 +- src/scenarios/server/all-scenarios.test.ts | 2 +- src/scenarios/server/elicitation-defaults.ts | 4 ++-- src/scenarios/server/elicitation-enums.ts | 4 ++-- src/scenarios/server/lifecycle.ts | 4 ++-- src/scenarios/server/prompts.ts | 4 ++-- src/scenarios/server/resources.ts | 4 ++-- src/scenarios/server/tools.ts | 4 ++-- src/scenarios/server/utils.ts | 4 ++-- 31 files changed, 113 insertions(+), 113 deletions(-) diff --git a/examples/clients/typescript/auth-test-bad-prm.ts b/examples/clients/typescript/auth-test-bad-prm.ts index 9ef7e49..4f65f32 100644 --- a/examples/clients/typescript/auth-test-bad-prm.ts +++ b/examples/clients/typescript/auth-test-bad-prm.ts @@ -7,10 +7,10 @@ import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'; import type { FetchLike } from '@modelcontextprotocol/sdk/shared/transport.js'; -import { withOAuthRetry } from './helpers/withOAuthRetry.js'; -import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js'; -import { runAsCli } from './helpers/cliRunner.js'; -import { logger } from './helpers/logger.js'; +import { withOAuthRetry } from './helpers/withOAuthRetry'; +import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider'; +import { runAsCli } from './helpers/cliRunner'; +import { logger } from './helpers/logger'; /** * Broken client that always uses root-based PRM discovery. diff --git a/examples/clients/typescript/auth-test-ignore-403.ts b/examples/clients/typescript/auth-test-ignore-403.ts index e78ab28..08aaa82 100644 --- a/examples/clients/typescript/auth-test-ignore-403.ts +++ b/examples/clients/typescript/auth-test-ignore-403.ts @@ -8,10 +8,10 @@ import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'; import type { FetchLike } from '@modelcontextprotocol/sdk/shared/transport.js'; -import { withOAuthRetry } from './helpers/withOAuthRetry.js'; -import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js'; -import { runAsCli } from './helpers/cliRunner.js'; -import { logger } from './helpers/logger.js'; +import { withOAuthRetry } from './helpers/withOAuthRetry'; +import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider'; +import { runAsCli } from './helpers/cliRunner'; +import { logger } from './helpers/logger'; /** * Broken client that only responds to 401, not 403. diff --git a/examples/clients/typescript/auth-test-ignore-scope.ts b/examples/clients/typescript/auth-test-ignore-scope.ts index e516625..c934da4 100644 --- a/examples/clients/typescript/auth-test-ignore-scope.ts +++ b/examples/clients/typescript/auth-test-ignore-scope.ts @@ -8,10 +8,10 @@ import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'; import type { FetchLike } from '@modelcontextprotocol/sdk/shared/transport.js'; -import { withOAuthRetry } from './helpers/withOAuthRetry.js'; -import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js'; -import { runAsCli } from './helpers/cliRunner.js'; -import { logger } from './helpers/logger.js'; +import { withOAuthRetry } from './helpers/withOAuthRetry'; +import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider'; +import { runAsCli } from './helpers/cliRunner'; +import { logger } from './helpers/logger'; /** * Broken client that ignores the scope from WWW-Authenticate header. diff --git a/examples/clients/typescript/auth-test-partial-scopes.ts b/examples/clients/typescript/auth-test-partial-scopes.ts index fc3b3ab..654ea29 100644 --- a/examples/clients/typescript/auth-test-partial-scopes.ts +++ b/examples/clients/typescript/auth-test-partial-scopes.ts @@ -8,10 +8,10 @@ import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'; import type { FetchLike } from '@modelcontextprotocol/sdk/shared/transport.js'; -import { withOAuthRetry } from './helpers/withOAuthRetry.js'; -import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider.js'; -import { runAsCli } from './helpers/cliRunner.js'; -import { logger } from './helpers/logger.js'; +import { withOAuthRetry } from './helpers/withOAuthRetry'; +import { ConformanceOAuthProvider } from './helpers/ConformanceOAuthProvider'; +import { runAsCli } from './helpers/cliRunner'; +import { logger } from './helpers/logger'; /** * Broken client that only requests a subset of scopes. diff --git a/examples/clients/typescript/auth-test.ts b/examples/clients/typescript/auth-test.ts index df6c71e..3efe71e 100644 --- a/examples/clients/typescript/auth-test.ts +++ b/examples/clients/typescript/auth-test.ts @@ -2,9 +2,9 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; -import { withOAuthRetry, handle401 } from './helpers/withOAuthRetry.js'; -import { runAsCli } from './helpers/cliRunner.js'; -import { logger } from './helpers/logger.js'; +import { withOAuthRetry, handle401 } from './helpers/withOAuthRetry'; +import { runAsCli } from './helpers/cliRunner'; +import { logger } from './helpers/logger'; /** * Fixed client metadata URL for CIMD conformance tests. diff --git a/src/checks/client.ts b/src/checks/client.ts index cde349d..7a14589 100644 --- a/src/checks/client.ts +++ b/src/checks/client.ts @@ -1,4 +1,4 @@ -import { ConformanceCheck, CheckStatus } from '../types.js'; +import { ConformanceCheck, CheckStatus } from '../types'; export function createServerInfoCheck(serverInfo: { name: string; diff --git a/src/checks/index.ts b/src/checks/index.ts index cb807fe..ed443ce 100644 --- a/src/checks/index.ts +++ b/src/checks/index.ts @@ -1,4 +1,4 @@ // Namespaced exports for client checks -import * as client from './client.js'; +import * as client from './client'; export const clientChecks = client; diff --git a/src/scenarios/client/auth/basic-cimd.ts b/src/scenarios/client/auth/basic-cimd.ts index e1fb82e..64c87d5 100644 --- a/src/scenarios/client/auth/basic-cimd.ts +++ b/src/scenarios/client/auth/basic-cimd.ts @@ -1,9 +1,9 @@ -import type { Scenario, ConformanceCheck } from '../../../types.js'; -import { ScenarioUrls } from '../../../types.js'; -import { createAuthServer } from './helpers/createAuthServer.js'; -import { createServer } from './helpers/createServer.js'; -import { ServerLifecycle } from './helpers/serverLifecycle.js'; -import { SpecReferences } from './spec-references.js'; +import type { Scenario, ConformanceCheck } from '../../../types'; +import { ScenarioUrls } from '../../../types'; +import { createAuthServer } from './helpers/createAuthServer'; +import { createServer } from './helpers/createServer'; +import { ServerLifecycle } from './helpers/serverLifecycle'; +import { SpecReferences } from './spec-references'; /** * Fixed client metadata URL that clients should use for CIMD tests. diff --git a/src/scenarios/client/auth/basic-dcr.ts b/src/scenarios/client/auth/basic-dcr.ts index 902fbdb..cfd8658 100644 --- a/src/scenarios/client/auth/basic-dcr.ts +++ b/src/scenarios/client/auth/basic-dcr.ts @@ -1,10 +1,10 @@ -import type { Scenario, ConformanceCheck } from '../../../types.js'; -import { ScenarioUrls } from '../../../types.js'; -import { createAuthServer } from './helpers/createAuthServer.js'; -import { createServer } from './helpers/createServer.js'; -import { ServerLifecycle } from './helpers/serverLifecycle.js'; +import type { Scenario, ConformanceCheck } from '../../../types'; +import { ScenarioUrls } from '../../../types'; +import { createAuthServer } from './helpers/createAuthServer'; +import { createServer } from './helpers/createServer'; +import { ServerLifecycle } from './helpers/serverLifecycle'; import { Request, Response } from 'express'; -import { SpecReferences } from './spec-references.js'; +import { SpecReferences } from './spec-references'; export class AuthBasicDCRScenario implements Scenario { name = 'auth/basic-dcr'; diff --git a/src/scenarios/client/auth/basic-metadata.ts b/src/scenarios/client/auth/basic-metadata.ts index 828f756..9282e5d 100644 --- a/src/scenarios/client/auth/basic-metadata.ts +++ b/src/scenarios/client/auth/basic-metadata.ts @@ -1,9 +1,9 @@ -import type { Scenario, ConformanceCheck } from '../../../types.js'; -import { ScenarioUrls } from '../../../types.js'; -import { createAuthServer } from './helpers/createAuthServer.js'; -import { createServer } from './helpers/createServer.js'; -import { ServerLifecycle } from './helpers/serverLifecycle.js'; -import { SpecReferences } from './spec-references.js'; +import type { Scenario, ConformanceCheck } from '../../../types'; +import { ScenarioUrls } from '../../../types'; +import { createAuthServer } from './helpers/createAuthServer'; +import { createServer } from './helpers/createServer'; +import { ServerLifecycle } from './helpers/serverLifecycle'; +import { SpecReferences } from './spec-references'; export class AuthBasicMetadataVar1Scenario implements Scenario { name = 'auth/basic-metadata-var1'; diff --git a/src/scenarios/client/auth/helpers/createAuthServer.ts b/src/scenarios/client/auth/helpers/createAuthServer.ts index 40527b8..c4a2094 100644 --- a/src/scenarios/client/auth/helpers/createAuthServer.ts +++ b/src/scenarios/client/auth/helpers/createAuthServer.ts @@ -1,8 +1,8 @@ import express, { Request, Response } from 'express'; -import type { ConformanceCheck } from '../../../../types.js'; -import { createRequestLogger } from '../../../request-logger.js'; -import { SpecReferences } from '../spec-references.js'; -import { MockTokenVerifier } from './mockTokenVerifier.js'; +import type { ConformanceCheck } from '../../../../types'; +import { createRequestLogger } from '../../../request-logger'; +import { SpecReferences } from '../spec-references'; +import { MockTokenVerifier } from './mockTokenVerifier'; export interface AuthServerOptions { metadataPath?: string; diff --git a/src/scenarios/client/auth/helpers/createServer.ts b/src/scenarios/client/auth/helpers/createServer.ts index 8e26944..a40a828 100644 --- a/src/scenarios/client/auth/helpers/createServer.ts +++ b/src/scenarios/client/auth/helpers/createServer.ts @@ -9,10 +9,10 @@ import { } from '@modelcontextprotocol/sdk/types.js'; import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js'; import express, { Request, Response, NextFunction } from 'express'; -import type { ConformanceCheck } from '../../../../types.js'; -import { createRequestLogger } from '../../../request-logger.js'; -import { MockTokenVerifier } from './mockTokenVerifier.js'; -import { SpecReferences } from '../spec-references.js'; +import type { ConformanceCheck } from '../../../../types'; +import { createRequestLogger } from '../../../request-logger'; +import { MockTokenVerifier } from './mockTokenVerifier'; +import { SpecReferences } from '../spec-references'; export interface ServerOptions { prmPath?: string | null; diff --git a/src/scenarios/client/auth/helpers/mockTokenVerifier.ts b/src/scenarios/client/auth/helpers/mockTokenVerifier.ts index 2ce5a0e..f49f101 100644 --- a/src/scenarios/client/auth/helpers/mockTokenVerifier.ts +++ b/src/scenarios/client/auth/helpers/mockTokenVerifier.ts @@ -1,7 +1,7 @@ import { OAuthTokenVerifier } from '@modelcontextprotocol/sdk/server/auth/provider.js'; import { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js'; -import type { ConformanceCheck } from '../../../../types.js'; -import { SpecReferences } from '../spec-references.js'; +import type { ConformanceCheck } from '../../../../types'; +import { SpecReferences } from '../spec-references'; export class MockTokenVerifier implements OAuthTokenVerifier { private tokenScopes: Map = new Map(); diff --git a/src/scenarios/client/auth/index.test.ts b/src/scenarios/client/auth/index.test.ts index b733f00..86ed76d 100644 --- a/src/scenarios/client/auth/index.test.ts +++ b/src/scenarios/client/auth/index.test.ts @@ -1,15 +1,15 @@ -import { authScenariosList } from './index.js'; +import { authScenariosList } from './index'; import { runClientAgainstScenario, InlineClientRunner -} from './test_helpers/testClient.js'; -import { runClient as goodClient } from '../../../../examples/clients/typescript/auth-test.js'; -import { runClient as badPrmClient } from '../../../../examples/clients/typescript/auth-test-bad-prm.js'; -import { runClient as noCimdClient } from '../../../../examples/clients/typescript/auth-test-no-cimd.js'; -import { runClient as ignoreScopeClient } from '../../../../examples/clients/typescript/auth-test-ignore-scope.js'; -import { runClient as partialScopesClient } from '../../../../examples/clients/typescript/auth-test-partial-scopes.js'; -import { runClient as ignore403Client } from '../../../../examples/clients/typescript/auth-test-ignore-403.js'; -import { setLogLevel } from '../../../../examples/clients/typescript/helpers/logger.js'; +} from './test_helpers/testClient'; +import { runClient as goodClient } from '../../../../examples/clients/typescript/auth-test'; +import { runClient as badPrmClient } from '../../../../examples/clients/typescript/auth-test-bad-prm'; +import { runClient as noCimdClient } from '../../../../examples/clients/typescript/auth-test-no-cimd'; +import { runClient as ignoreScopeClient } from '../../../../examples/clients/typescript/auth-test-ignore-scope'; +import { runClient as partialScopesClient } from '../../../../examples/clients/typescript/auth-test-partial-scopes'; +import { runClient as ignore403Client } from '../../../../examples/clients/typescript/auth-test-ignore-403'; +import { setLogLevel } from '../../../../examples/clients/typescript/helpers/logger'; beforeAll(() => { setLogLevel('error'); diff --git a/src/scenarios/client/auth/index.ts b/src/scenarios/client/auth/index.ts index 6d4d400..f051819 100644 --- a/src/scenarios/client/auth/index.ts +++ b/src/scenarios/client/auth/index.ts @@ -1,21 +1,21 @@ import { Scenario } from '../../../types'; -import { AuthBasicDCRScenario } from './basic-dcr.js'; -import { AuthBasicCIMDScenario } from './basic-cimd.js'; +import { AuthBasicDCRScenario } from './basic-dcr'; +import { AuthBasicCIMDScenario } from './basic-cimd'; import { AuthBasicMetadataVar1Scenario, AuthBasicMetadataVar2Scenario, AuthBasicMetadataVar3Scenario -} from './basic-metadata.js'; +} from './basic-metadata'; import { Auth20250326OAuthMetadataBackcompatScenario, Auth20250326OEndpointFallbackScenario -} from './march-spec-backcompat.js'; +} from './march-spec-backcompat'; import { ScopeFromWwwAuthenticateScenario, ScopeFromScopesSupportedScenario, ScopeOmittedWhenUndefinedScenario, ScopeStepUpAuthScenario -} from './scope-handling.js'; +} from './scope-handling'; export const authScenariosList: Scenario[] = [ new AuthBasicDCRScenario(), diff --git a/src/scenarios/client/auth/march-spec-backcompat.ts b/src/scenarios/client/auth/march-spec-backcompat.ts index 0999bdf..4f0a5ae 100644 --- a/src/scenarios/client/auth/march-spec-backcompat.ts +++ b/src/scenarios/client/auth/march-spec-backcompat.ts @@ -1,10 +1,10 @@ -import type { Scenario, ConformanceCheck } from '../../../types.js'; -import { ScenarioUrls } from '../../../types.js'; -import { createAuthServer } from './helpers/createAuthServer.js'; -import { createServer } from './helpers/createServer.js'; -import { ServerLifecycle } from './helpers/serverLifecycle.js'; +import type { Scenario, ConformanceCheck } from '../../../types'; +import { ScenarioUrls } from '../../../types'; +import { createAuthServer } from './helpers/createAuthServer'; +import { createServer } from './helpers/createServer'; +import { ServerLifecycle } from './helpers/serverLifecycle'; import express, { Request, Response } from 'express'; -import { SpecReferences } from './spec-references.js'; +import { SpecReferences } from './spec-references'; export class Auth20250326OAuthMetadataBackcompatScenario implements Scenario { name = 'auth/2025-03-26-oauth-metadata-backcompat'; diff --git a/src/scenarios/client/auth/scope-handling.ts b/src/scenarios/client/auth/scope-handling.ts index 5f97e36..94038f1 100644 --- a/src/scenarios/client/auth/scope-handling.ts +++ b/src/scenarios/client/auth/scope-handling.ts @@ -1,10 +1,10 @@ -import type { Scenario, ConformanceCheck } from '../../../types.js'; -import { ScenarioUrls } from '../../../types.js'; -import { createAuthServer } from './helpers/createAuthServer.js'; -import { createServer } from './helpers/createServer.js'; -import { ServerLifecycle } from './helpers/serverLifecycle.js'; -import { SpecReferences } from './spec-references.js'; -import { MockTokenVerifier } from './helpers/mockTokenVerifier.js'; +import type { Scenario, ConformanceCheck } from '../../../types'; +import { ScenarioUrls } from '../../../types'; +import { createAuthServer } from './helpers/createAuthServer'; +import { createServer } from './helpers/createServer'; +import { ServerLifecycle } from './helpers/serverLifecycle'; +import { SpecReferences } from './spec-references'; +import { MockTokenVerifier } from './helpers/mockTokenVerifier'; import type { Request, Response, NextFunction } from 'express'; /** diff --git a/src/scenarios/client/auth/test_helpers/testClient.ts b/src/scenarios/client/auth/test_helpers/testClient.ts index 6a34c79..df0f70c 100644 --- a/src/scenarios/client/auth/test_helpers/testClient.ts +++ b/src/scenarios/client/auth/test_helpers/testClient.ts @@ -1,4 +1,4 @@ -import { getScenario } from '../../../index.js'; +import { getScenario } from '../../../index'; import { spawn } from 'child_process'; const CLIENT_TIMEOUT = 10000; // 10 seconds for client to complete diff --git a/src/scenarios/client/elicitation-defaults.ts b/src/scenarios/client/elicitation-defaults.ts index 2d4b8ca..88e6bf0 100644 --- a/src/scenarios/client/elicitation-defaults.ts +++ b/src/scenarios/client/elicitation-defaults.ts @@ -11,10 +11,10 @@ import { ListToolsRequestSchema, ElicitResultSchema } from '@modelcontextprotocol/sdk/types.js'; -import type { Scenario, ConformanceCheck } from '../../types.js'; +import type { Scenario, ConformanceCheck } from '../../types'; import express, { Request, Response } from 'express'; -import { ScenarioUrls } from '../../types.js'; -import { createRequestLogger } from '../request-logger.js'; +import { ScenarioUrls } from '../../types'; +import { createRequestLogger } from '../request-logger'; import { randomUUID } from 'crypto'; function isInitializeRequest(body: unknown): boolean { diff --git a/src/scenarios/client/initialize.ts b/src/scenarios/client/initialize.ts index 2d5ac18..770103c 100644 --- a/src/scenarios/client/initialize.ts +++ b/src/scenarios/client/initialize.ts @@ -1,6 +1,6 @@ import http from 'http'; -import { Scenario, ScenarioUrls, ConformanceCheck } from '../../types.js'; -import { clientChecks } from '../../checks/index.js'; +import { Scenario, ScenarioUrls, ConformanceCheck } from '../../types'; +import { clientChecks } from '../../checks/index'; export class InitializeScenario implements Scenario { name = 'initialize'; diff --git a/src/scenarios/client/tools_call.ts b/src/scenarios/client/tools_call.ts index 7e3a298..ab7e312 100644 --- a/src/scenarios/client/tools_call.ts +++ b/src/scenarios/client/tools_call.ts @@ -4,10 +4,10 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; -import type { Scenario, ConformanceCheck } from '../../types.js'; +import type { Scenario, ConformanceCheck } from '../../types'; import express, { Request, Response } from 'express'; -import { ScenarioUrls } from '../../types.js'; -import { createRequestLogger } from '../request-logger.js'; +import { ScenarioUrls } from '../../types'; +import { createRequestLogger } from '../request-logger'; function createServer(checks: ConformanceCheck[]): express.Application { const server = new Server( diff --git a/src/scenarios/index.ts b/src/scenarios/index.ts index 5d50dff..895afc8 100644 --- a/src/scenarios/index.ts +++ b/src/scenarios/index.ts @@ -1,15 +1,15 @@ -import { Scenario, ClientScenario } from '../types.js'; -import { InitializeScenario } from './client/initialize.js'; -import { ToolsCallScenario } from './client/tools_call.js'; -import { ElicitationClientDefaultsScenario } from './client/elicitation-defaults.js'; +import { Scenario, ClientScenario } from '../types'; +import { InitializeScenario } from './client/initialize'; +import { ToolsCallScenario } from './client/tools_call'; +import { ElicitationClientDefaultsScenario } from './client/elicitation-defaults'; // Import all new server test scenarios -import { ServerInitializeScenario } from './server/lifecycle.js'; +import { ServerInitializeScenario } from './server/lifecycle'; import { LoggingSetLevelScenario, CompletionCompleteScenario -} from './server/utils.js'; +} from './server/utils'; import { ToolsListScenario, @@ -23,10 +23,10 @@ import { ToolsCallElicitationScenario, ToolsCallAudioScenario, ToolsCallEmbeddedResourceScenario -} from './server/tools.js'; +} from './server/tools'; -import { ElicitationDefaultsScenario } from './server/elicitation-defaults.js'; -import { ElicitationEnumsScenario } from './server/elicitation-enums.js'; +import { ElicitationDefaultsScenario } from './server/elicitation-defaults'; +import { ElicitationEnumsScenario } from './server/elicitation-enums'; import { ResourcesListScenario, @@ -35,7 +35,7 @@ import { ResourcesTemplateReadScenario, ResourcesSubscribeScenario, ResourcesUnsubscribeScenario -} from './server/resources.js'; +} from './server/resources'; import { PromptsListScenario, @@ -43,9 +43,9 @@ import { PromptsGetWithArgsScenario, PromptsGetEmbeddedResourceScenario, PromptsGetWithImageScenario -} from './server/prompts.js'; +} from './server/prompts'; -import { authScenariosList } from './client/auth/index.js'; +import { authScenariosList } from './client/auth/index'; // Pending client scenarios (not yet fully tested/implemented) const pendingClientScenariosList: ClientScenario[] = [ diff --git a/src/scenarios/request-logger.ts b/src/scenarios/request-logger.ts index 6f63837..a799ad0 100644 --- a/src/scenarios/request-logger.ts +++ b/src/scenarios/request-logger.ts @@ -1,5 +1,5 @@ import { Request, Response, NextFunction } from 'express'; -import { ConformanceCheck } from '../types.js'; +import { ConformanceCheck } from '../types'; export interface LoggerOptions { incomingId: string; diff --git a/src/scenarios/server/all-scenarios.test.ts b/src/scenarios/server/all-scenarios.test.ts index 6eab252..9f9a7e9 100644 --- a/src/scenarios/server/all-scenarios.test.ts +++ b/src/scenarios/server/all-scenarios.test.ts @@ -1,5 +1,5 @@ import { spawn, ChildProcess } from 'child_process'; -import { getClientScenario, listActiveClientScenarios } from '../index.js'; +import { getClientScenario, listActiveClientScenarios } from '../index'; import path from 'path'; describe('Server Scenarios', () => { diff --git a/src/scenarios/server/elicitation-defaults.ts b/src/scenarios/server/elicitation-defaults.ts index bd54475..2be114c 100644 --- a/src/scenarios/server/elicitation-defaults.ts +++ b/src/scenarios/server/elicitation-defaults.ts @@ -2,8 +2,8 @@ * SEP-1034: Elicitation default values test scenarios for MCP servers */ -import { ClientScenario, ConformanceCheck } from '../../types.js'; -import { connectToServer } from './client-helper.js'; +import { ClientScenario, ConformanceCheck } from '../../types'; +import { connectToServer } from './client-helper'; import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js'; export class ElicitationDefaultsScenario implements ClientScenario { diff --git a/src/scenarios/server/elicitation-enums.ts b/src/scenarios/server/elicitation-enums.ts index fa35b08..e5c1fa3 100644 --- a/src/scenarios/server/elicitation-enums.ts +++ b/src/scenarios/server/elicitation-enums.ts @@ -2,8 +2,8 @@ * SEP-1330: Elicitation enum schema improvements test scenarios for MCP servers */ -import { ClientScenario, ConformanceCheck } from '../../types.js'; -import { connectToServer } from './client-helper.js'; +import { ClientScenario, ConformanceCheck } from '../../types'; +import { connectToServer } from './client-helper'; import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js'; export class ElicitationEnumsScenario implements ClientScenario { diff --git a/src/scenarios/server/lifecycle.ts b/src/scenarios/server/lifecycle.ts index 357c5b3..d9b341e 100644 --- a/src/scenarios/server/lifecycle.ts +++ b/src/scenarios/server/lifecycle.ts @@ -2,8 +2,8 @@ * Lifecycle test scenarios for MCP servers */ -import { ClientScenario, ConformanceCheck } from '../../types.js'; -import { connectToServer } from './client-helper.js'; +import { ClientScenario, ConformanceCheck } from '../../types'; +import { connectToServer } from './client-helper'; export class ServerInitializeScenario implements ClientScenario { name = 'server-initialize'; diff --git a/src/scenarios/server/prompts.ts b/src/scenarios/server/prompts.ts index 1bbb378..436564b 100644 --- a/src/scenarios/server/prompts.ts +++ b/src/scenarios/server/prompts.ts @@ -2,8 +2,8 @@ * Prompts test scenarios for MCP servers */ -import { ClientScenario, ConformanceCheck } from '../../types.js'; -import { connectToServer } from './client-helper.js'; +import { ClientScenario, ConformanceCheck } from '../../types'; +import { connectToServer } from './client-helper'; export class PromptsListScenario implements ClientScenario { name = 'prompts-list'; diff --git a/src/scenarios/server/resources.ts b/src/scenarios/server/resources.ts index c66e425..a4ed241 100644 --- a/src/scenarios/server/resources.ts +++ b/src/scenarios/server/resources.ts @@ -2,8 +2,8 @@ * Resources test scenarios for MCP servers */ -import { ClientScenario, ConformanceCheck } from '../../types.js'; -import { connectToServer } from './client-helper.js'; +import { ClientScenario, ConformanceCheck } from '../../types'; +import { connectToServer } from './client-helper'; import { TextResourceContents, BlobResourceContents diff --git a/src/scenarios/server/tools.ts b/src/scenarios/server/tools.ts index 656df0a..64bb547 100644 --- a/src/scenarios/server/tools.ts +++ b/src/scenarios/server/tools.ts @@ -2,8 +2,8 @@ * Tools test scenarios for MCP servers */ -import { ClientScenario, ConformanceCheck } from '../../types.js'; -import { connectToServer, NotificationCollector } from './client-helper.js'; +import { ClientScenario, ConformanceCheck } from '../../types'; +import { connectToServer, NotificationCollector } from './client-helper'; import { CallToolResultSchema, CreateMessageRequestSchema, diff --git a/src/scenarios/server/utils.ts b/src/scenarios/server/utils.ts index 70de452..e65c814 100644 --- a/src/scenarios/server/utils.ts +++ b/src/scenarios/server/utils.ts @@ -2,8 +2,8 @@ * Utilities test scenarios for MCP servers */ -import { ClientScenario, ConformanceCheck } from '../../types.js'; -import { connectToServer } from './client-helper.js'; +import { ClientScenario, ConformanceCheck } from '../../types'; +import { connectToServer } from './client-helper'; export class LoggingSetLevelScenario implements ClientScenario { name = 'logging-set-level'; From cb4cd6163906df25497b1b3a489198cf31a9c83b Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Thu, 20 Nov 2025 19:07:35 +0000 Subject: [PATCH 4/5] Skip auth/basic-cimd test until SDK support lands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Waiting on typescript-sdk PR #1127 for CIMD support. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/scenarios/client/auth/index.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/scenarios/client/auth/index.test.ts b/src/scenarios/client/auth/index.test.ts index 86ed76d..ce6bf98 100644 --- a/src/scenarios/client/auth/index.test.ts +++ b/src/scenarios/client/auth/index.test.ts @@ -23,7 +23,10 @@ const skipScenarios = new Set([ // Waiting on typescript-sdk support for using scopes_supported from PRM // to request scopes. // https://github.com/modelcontextprotocol/typescript-sdk/pull/1133 - 'auth/scope-from-scopes-supported' + 'auth/scope-from-scopes-supported', + // Waiting on typescript-sdk support for CIMD + // https://github.com/modelcontextprotocol/typescript-sdk/pull/1127 + 'auth/basic-cimd' ]); describe('Client Auth Scenarios', () => { From 17cde178bceec92a0727afaea50f27f6cf34b81e Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Thu, 20 Nov 2025 20:37:47 +0000 Subject: [PATCH 5/5] Fix trailing whitespace in auth-test-no-cimd.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- examples/clients/typescript/auth-test-no-cimd.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/typescript/auth-test-no-cimd.ts b/examples/clients/typescript/auth-test-no-cimd.ts index ad46c9f..11b8f05 100644 --- a/examples/clients/typescript/auth-test-no-cimd.ts +++ b/examples/clients/typescript/auth-test-no-cimd.ts @@ -8,12 +8,12 @@ import { logger } from './helpers/logger'; /** * Non-compliant client that doesn't use CIMD (Client ID Metadata Document). - * + * * This client intentionally omits the clientMetadataUrl parameter when the server * advertises client_id_metadata_document_supported=true. A compliant client should * use CIMD when the server supports it, but this client falls back to DCR (Dynamic * Client Registration) instead. - * + * * Used to test that conformance checks detect clients that don't properly * implement CIMD support. */