feat: add specVersion classification to conformance scenarios (#147)
* feat: add specVersions classification to conformance scenarios
Each scenario declares which spec versions it applies to as a list.
Scenarios that carry forward (e.g. initialize) list all applicable versions
['2025-06-18', '2025-11-25']. Scenarios removed from newer specs (e.g.
backcompat auth) only list their original version ['2025-03-26'].
- specVersions list on Scenario and ClientScenario interfaces
- --spec-version CLI filter uses simple .includes()
- Tier-check conformance matrix (Server / Client: Core / Client: Auth)
with per-version columns and unique All* count
- 7 unit tests for specVersions helpers
- Updated tier-audit skill docs with matrix format
* fix: fix console output template for tier audit skill
The template had an orphan table header (Check | Value | T2 | T1)
with no rows above the conformance matrix, causing an empty table
to render. The scorecard rows below the matrix also lacked their
own header.
Fix: two self-contained tables with clear labels — 'Conformance:'
for the per-version matrix, 'Scorecard:' for the check rows.
* fix: align skill console template with tier-check script output
Add asterisk footnote ('unique scenarios — a scenario may apply to
multiple spec versions') and rename 'Scorecard' to 'Repository Health'
to match the labels used by the tier-check CLI output.
* fix: add specVersions to CrossAppAccessCompleteFlowScenario
Added in 83c446d on main after this branch diverged.
This commit is contained in:
parent
dd14862ee4
commit
3f9048a019
13 changed files with 521 additions and 59 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import type { Scenario, ConformanceCheck } from '../../../types';
|
||||
import { ScenarioUrls } from '../../../types';
|
||||
import { ScenarioUrls, SpecVersion } from '../../../types';
|
||||
import { createAuthServer } from './helpers/createAuthServer';
|
||||
import { createServer } from './helpers/createServer';
|
||||
import { ServerLifecycle } from './helpers/serverLifecycle';
|
||||
|
|
@ -22,6 +22,7 @@ export const CIMD_CLIENT_METADATA_URL =
|
|||
*/
|
||||
export class AuthBasicCIMDScenario implements Scenario {
|
||||
name = 'auth/basic-cimd';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import * as jose from 'jose';
|
||||
import type { CryptoKey } from 'jose';
|
||||
import type { Scenario, ConformanceCheck, ScenarioUrls } from '../../../types';
|
||||
import type {
|
||||
Scenario,
|
||||
ConformanceCheck,
|
||||
ScenarioUrls,
|
||||
SpecVersion
|
||||
} from '../../../types';
|
||||
import { createAuthServer } from './helpers/createAuthServer';
|
||||
import { createServer } from './helpers/createServer';
|
||||
import { ServerLifecycle } from './helpers/serverLifecycle';
|
||||
|
|
@ -32,6 +37,7 @@ async function generateTestKeypair(): Promise<{
|
|||
*/
|
||||
export class ClientCredentialsJwtScenario implements Scenario {
|
||||
name = 'auth/client-credentials-jwt';
|
||||
specVersions: SpecVersion[] = ['extension'];
|
||||
description =
|
||||
'Tests OAuth client_credentials flow with private_key_jwt authentication (SEP-1046)';
|
||||
|
||||
|
|
@ -250,6 +256,7 @@ export class ClientCredentialsJwtScenario implements Scenario {
|
|||
*/
|
||||
export class ClientCredentialsBasicScenario implements Scenario {
|
||||
name = 'auth/client-credentials-basic';
|
||||
specVersions: SpecVersion[] = ['extension'];
|
||||
description =
|
||||
'Tests OAuth client_credentials flow with client_secret_basic authentication';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import * as jose from 'jose';
|
||||
import type { CryptoKey } from 'jose';
|
||||
import express, { type Request, type Response } from 'express';
|
||||
import type { Scenario, ConformanceCheck, ScenarioUrls } from '../../../types';
|
||||
import type {
|
||||
Scenario,
|
||||
ConformanceCheck,
|
||||
ScenarioUrls,
|
||||
SpecVersion
|
||||
} from '../../../types';
|
||||
import { createAuthServer } from './helpers/createAuthServer';
|
||||
import { createServer } from './helpers/createServer';
|
||||
import { MockTokenVerifier } from './helpers/mockTokenVerifier';
|
||||
|
|
@ -55,6 +60,7 @@ async function createIdpIdToken(
|
|||
*/
|
||||
export class CrossAppAccessCompleteFlowScenario implements Scenario {
|
||||
name = 'auth/cross-app-access-complete-flow';
|
||||
specVersions: SpecVersion[] = ['extension'];
|
||||
description =
|
||||
'Tests complete SEP-990 flow: token exchange + JWT bearer grant (Enterprise Managed OAuth)';
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ function createMetadataScenario(config: MetadataScenarioConfig): Scenario {
|
|||
|
||||
return {
|
||||
name: `auth/${config.name}`,
|
||||
specVersions: ['2025-11-25'],
|
||||
description: `Tests Basic OAuth metadata discovery flow.
|
||||
|
||||
**PRM:** ${config.prmLocation}${config.inWwwAuth ? '' : ' (not in WWW-Authenticate)'}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Scenario, ConformanceCheck } from '../../../types';
|
||||
import { ScenarioUrls } from '../../../types';
|
||||
import { ScenarioUrls, SpecVersion } from '../../../types';
|
||||
import { createAuthServer } from './helpers/createAuthServer';
|
||||
import { createServer } from './helpers/createServer';
|
||||
import { ServerLifecycle } from './helpers/serverLifecycle';
|
||||
|
|
@ -8,6 +8,7 @@ import { SpecReferences } from './spec-references';
|
|||
|
||||
export class Auth20250326OAuthMetadataBackcompatScenario implements Scenario {
|
||||
name = 'auth/2025-03-26-oauth-metadata-backcompat';
|
||||
specVersions: SpecVersion[] = ['2025-03-26'];
|
||||
description =
|
||||
'Tests 2025-03-26 spec OAuth flow: no PRM (Protected Resource Metadata), OAuth metadata at root location';
|
||||
private server = new ServerLifecycle();
|
||||
|
|
@ -68,6 +69,7 @@ export class Auth20250326OAuthMetadataBackcompatScenario implements Scenario {
|
|||
|
||||
export class Auth20250326OEndpointFallbackScenario implements Scenario {
|
||||
name = 'auth/2025-03-26-oauth-endpoint-fallback';
|
||||
specVersions: SpecVersion[] = ['2025-03-26'];
|
||||
description =
|
||||
'Tests OAuth flow with no metadata endpoints, relying on fallback to standard OAuth endpoints at server root (2025-03-26 spec behavior)';
|
||||
private server = new ServerLifecycle();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import type { Scenario, ConformanceCheck, ScenarioUrls } from '../../../types';
|
||||
import type {
|
||||
Scenario,
|
||||
ConformanceCheck,
|
||||
ScenarioUrls,
|
||||
SpecVersion
|
||||
} from '../../../types';
|
||||
import { createAuthServer } from './helpers/createAuthServer';
|
||||
import { createServer } from './helpers/createServer';
|
||||
import { ServerLifecycle } from './helpers/serverLifecycle';
|
||||
|
|
@ -19,6 +24,7 @@ const PRE_REGISTERED_CLIENT_SECRET = 'pre-registered-secret';
|
|||
*/
|
||||
export class PreRegistrationScenario implements Scenario {
|
||||
name = 'auth/pre-registration';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
description =
|
||||
'Tests OAuth flow with pre-registered client credentials. Server does not support DCR.';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Scenario, ConformanceCheck } from '../../../types.js';
|
||||
import { ScenarioUrls } from '../../../types.js';
|
||||
import { ScenarioUrls, SpecVersion } from '../../../types.js';
|
||||
import { createAuthServer } from './helpers/createAuthServer.js';
|
||||
import { createServer } from './helpers/createServer.js';
|
||||
import { ServerLifecycle } from './helpers/serverLifecycle.js';
|
||||
|
|
@ -27,6 +27,7 @@ import { MockTokenVerifier } from './helpers/mockTokenVerifier.js';
|
|||
*/
|
||||
export class ResourceMismatchScenario implements Scenario {
|
||||
name = 'auth/resource-mismatch';
|
||||
specVersions: SpecVersion[] = ['draft'];
|
||||
description =
|
||||
'Tests that client rejects when PRM resource does not match server URL';
|
||||
allowClientError = true;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Scenario, ConformanceCheck } from '../../../types';
|
||||
import { ScenarioUrls } from '../../../types';
|
||||
import { ScenarioUrls, SpecVersion } from '../../../types';
|
||||
import { createAuthServer } from './helpers/createAuthServer';
|
||||
import { createServer } from './helpers/createServer';
|
||||
import { ServerLifecycle } from './helpers/serverLifecycle';
|
||||
|
|
@ -15,6 +15,7 @@ import type { Request, Response, NextFunction } from 'express';
|
|||
*/
|
||||
export class ScopeFromWwwAuthenticateScenario implements Scenario {
|
||||
name = 'auth/scope-from-www-authenticate';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
description =
|
||||
'Tests that client uses scope parameter from WWW-Authenticate header when provided';
|
||||
private authServer = new ServerLifecycle();
|
||||
|
|
@ -100,6 +101,7 @@ export class ScopeFromWwwAuthenticateScenario implements Scenario {
|
|||
*/
|
||||
export class ScopeFromScopesSupportedScenario implements Scenario {
|
||||
name = 'auth/scope-from-scopes-supported';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
description =
|
||||
'Tests that client uses all scopes from scopes_supported when scope not in WWW-Authenticate header';
|
||||
private authServer = new ServerLifecycle();
|
||||
|
|
@ -195,6 +197,7 @@ export class ScopeFromScopesSupportedScenario implements Scenario {
|
|||
*/
|
||||
export class ScopeOmittedWhenUndefinedScenario implements Scenario {
|
||||
name = 'auth/scope-omitted-when-undefined';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
description =
|
||||
'Tests that client omits scope parameter when scopes_supported is undefined';
|
||||
private authServer = new ServerLifecycle();
|
||||
|
|
@ -281,6 +284,7 @@ export class ScopeOmittedWhenUndefinedScenario implements Scenario {
|
|||
*/
|
||||
export class ScopeStepUpAuthScenario implements Scenario {
|
||||
name = 'auth/scope-step-up';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
description =
|
||||
'Tests that client handles step-up authentication with different scope requirements per operation';
|
||||
private authServer = new ServerLifecycle();
|
||||
|
|
@ -477,6 +481,7 @@ export class ScopeStepUpAuthScenario implements Scenario {
|
|||
*/
|
||||
export class ScopeRetryLimitScenario implements Scenario {
|
||||
name = 'auth/scope-retry-limit';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
description =
|
||||
'Tests that client implements retry limits to prevent infinite authorization loops on repeated 403 responses';
|
||||
allowClientError = true;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Scenario, ConformanceCheck } from '../../../types.js';
|
||||
import { ScenarioUrls } from '../../../types.js';
|
||||
import { ScenarioUrls, SpecVersion } from '../../../types.js';
|
||||
import { createAuthServer } from './helpers/createAuthServer.js';
|
||||
import { createServer } from './helpers/createServer.js';
|
||||
import { ServerLifecycle } from './helpers/serverLifecycle.js';
|
||||
|
|
@ -45,6 +45,7 @@ const AUTH_METHOD_NAMES: Record<AuthMethod, string> = {
|
|||
|
||||
class TokenEndpointAuthScenario implements Scenario {
|
||||
name: string;
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
description: string;
|
||||
private expectedAuthMethod: AuthMethod;
|
||||
private authServer = new ServerLifecycle();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
ListToolsRequestSchema,
|
||||
ElicitResultSchema
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import type { Scenario, ConformanceCheck } from '../../types';
|
||||
import type { Scenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import express, { Request, Response } from 'express';
|
||||
import { ScenarioUrls } from '../../types';
|
||||
import { createRequestLogger } from '../request-logger';
|
||||
|
|
@ -474,6 +474,7 @@ function createServer(checks: ConformanceCheck[]): {
|
|||
|
||||
export class ElicitationClientDefaultsScenario implements Scenario {
|
||||
name = 'elicitation-sep1034-client-defaults';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
description =
|
||||
'Tests client applies default values for omitted elicitation fields (SEP-1034)';
|
||||
private app: express.Application | null = null;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
import http from 'http';
|
||||
import { Scenario, ScenarioUrls, ConformanceCheck } from '../../types';
|
||||
import {
|
||||
Scenario,
|
||||
ScenarioUrls,
|
||||
ConformanceCheck,
|
||||
SpecVersion
|
||||
} from '../../types';
|
||||
import { clientChecks } from '../../checks/index';
|
||||
|
||||
export class InitializeScenario implements Scenario {
|
||||
name = 'initialize';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
description = 'Tests MCP client initialization handshake';
|
||||
|
||||
private server: http.Server | null = null;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,16 @@
|
|||
*/
|
||||
|
||||
import http from 'http';
|
||||
import { Scenario, ScenarioUrls, ConformanceCheck } from '../../types.js';
|
||||
import {
|
||||
Scenario,
|
||||
ScenarioUrls,
|
||||
ConformanceCheck,
|
||||
SpecVersion
|
||||
} from '../../types.js';
|
||||
|
||||
export class SSERetryScenario implements Scenario {
|
||||
name = 'sse-retry';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
description =
|
||||
'Tests that client respects SSE retry field timing and reconnects properly (SEP-1699)';
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import type { Scenario, ConformanceCheck } from '../../types';
|
||||
import type { Scenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import express, { Request, Response } from 'express';
|
||||
import { ScenarioUrls } from '../../types';
|
||||
import { createRequestLogger } from '../request-logger';
|
||||
|
|
@ -115,6 +115,7 @@ function createServerApp(checks: ConformanceCheck[]): express.Application {
|
|||
|
||||
export class ToolsCallScenario implements Scenario {
|
||||
name = 'tools_call';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
description = 'Tests calling tools with various parameter types';
|
||||
private app: express.Application | null = null;
|
||||
private httpServer: any = null;
|
||||
|
|
|
|||
Loading…
Reference in a new issue