refactor: range-based spec version tagging (introducedIn/removedIn) (#265)
* refactor: replace specVersions list with introducedIn/removedIn range tagging Replaces per-scenario `specVersions: SpecVersion[]` arrays with `introducedIn` and optional `removedIn` range fields on all three scenario interfaces. Extension scenarios get an orthogonal `extension?: boolean` flag instead of the former `'extension'` tag in the versions list. matchesSpecVersion() is rewritten as a range comparison (introducedIn <= target && (!removedIn || target < removedIn)), with draft sorting after all dated versions. getScenarioSpecVersions() reconstructs the effective version list from the range for backward-compat with tier-check. Closes #256. * refactor: nest spec-version range under `source: ScenarioSource` union The flat `introducedIn`/`removedIn`/`extension?` shape forced extension scenarios to set a placeholder `introducedIn` that was never read. Nesting the version-range fields inside a `source` property typed as a union lets the compiler enforce introducedIn XOR extensionId — no runtime invariant test needed. type ScenarioSource = | { introducedIn: SpecVersion; removedIn?: SpecVersion } | { extensionId: ExtensionId }; A class can't `implements` a union type directly, so the union lives inside a property and `Scenario` stays a plain interface — `class implements Scenario` keeps working everywhere. `EXTENSION_IDS` is a const array mirroring the `DATED_SPEC_VERSIONS` pattern. Also renames the cross-app-access scenario to enterprise-managed-auth to match the extension's canonical name: file, class, slug, context schema literal, and the everything-client runner. * refactor: use canonical SEP-2133 extension identifiers EXTENSION_IDS now holds the wire identifiers from the spec's extension docs (client-matrix.mdx, docs/extensions/auth/*.mdx) so extensionId values can be cross-referenced against capabilities.extensions keys directly: io.modelcontextprotocol/oauth-client-credentials io.modelcontextprotocol/enterprise-managed-authorization The previous commit used a shortened 'enterprise-managed-auth' which doesn't match the canonical '-authorization'. Re-renames the scenario file/class/slug and downstream references to align. --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
04dd6db7ae
commit
acc70de0f6
11 changed files with 193 additions and 208 deletions
|
|
@ -5,7 +5,7 @@
|
|||
* to prevent DNS rebinding attacks. See GHSA-w48q-cv73-mx4w for details.
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types';
|
||||
import { request } from 'undici';
|
||||
|
||||
const SPEC_REFERENCES = [
|
||||
|
|
@ -85,7 +85,7 @@ async function sendRequestWithHostAndOrigin(
|
|||
|
||||
export class DNSRebindingProtectionScenario implements ClientScenario {
|
||||
name = 'dns-rebinding-protection';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-11-25' } as const;
|
||||
description = `Test DNS rebinding protection for localhost servers.
|
||||
|
||||
**Scope:** This test applies to localhost MCP servers running without HTTPS and without
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
* SEP-1034: Elicitation default values test scenarios for MCP servers
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types';
|
||||
import { connectToServer } from './client-helper';
|
||||
import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
export class ElicitationDefaultsScenario implements ClientScenario {
|
||||
name = 'elicitation-sep1034-defaults';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-11-25' } as const;
|
||||
description = `Test elicitation with default values for all primitive types (SEP-1034).
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
* SEP-1330: Elicitation enum schema improvements test scenarios for MCP servers
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types';
|
||||
import { connectToServer } from './client-helper';
|
||||
import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
export class ElicitationEnumsScenario implements ClientScenario {
|
||||
name = 'elicitation-sep1330-enums';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-11-25' } as const;
|
||||
description = `Test elicitation with enum schema improvements (SEP-1330).
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* or additionalProperties fields.
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types.js';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types.js';
|
||||
import { connectToServer } from './client-helper.js';
|
||||
|
||||
const EXPECTED_TOOL_NAME = 'json_schema_2020_12_tool';
|
||||
|
|
@ -14,7 +14,7 @@ const EXPECTED_SCHEMA_DIALECT = 'https://json-schema.org/draft/2020-12/schema';
|
|||
|
||||
export class JsonSchema2020_12Scenario implements ClientScenario {
|
||||
name = 'json-schema-2020-12';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-11-25' } as const;
|
||||
description = `Validates JSON Schema 2020-12 keyword preservation (SEP-1613).
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Lifecycle test scenarios for MCP servers
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types';
|
||||
import { connectToServer } from './client-helper';
|
||||
|
||||
const VISIBLE_ASCII_REGEX = /^[\x21-\x7E]+$/;
|
||||
|
|
@ -16,7 +16,7 @@ const SESSION_SPEC_REFERENCES = [
|
|||
|
||||
export class ServerInitializeScenario implements ClientScenario {
|
||||
name = 'server-initialize';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test basic server initialization handshake.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
* Prompts test scenarios for MCP servers
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types';
|
||||
import { connectToServer } from './client-helper';
|
||||
|
||||
export class PromptsListScenario implements ClientScenario {
|
||||
name = 'prompts-list';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test listing available prompts.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -88,7 +88,7 @@ export class PromptsListScenario implements ClientScenario {
|
|||
|
||||
export class PromptsGetSimpleScenario implements ClientScenario {
|
||||
name = 'prompts-get-simple';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test getting a simple prompt without arguments.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -173,7 +173,7 @@ Implement a prompt named \`test_simple_prompt\` with no arguments that returns:
|
|||
|
||||
export class PromptsGetWithArgsScenario implements ClientScenario {
|
||||
name = 'prompts-get-with-args';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test parameterized prompt.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -269,7 +269,7 @@ Returns (with args \`{arg1: "hello", arg2: "world"}\`):
|
|||
|
||||
export class PromptsGetEmbeddedResourceScenario implements ClientScenario {
|
||||
name = 'prompts-get-embedded-resource';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test prompt with embedded resource content.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -375,7 +375,7 @@ Returns:
|
|||
|
||||
export class PromptsGetWithImageScenario implements ClientScenario {
|
||||
name = 'prompts-get-with-image';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test prompt with image content.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
import {
|
||||
ClientScenario,
|
||||
ConformanceCheck,
|
||||
SpecVersion,
|
||||
DRAFT_PROTOCOL_VERSION
|
||||
} from '../../types';
|
||||
import { connectToServer } from './client-helper';
|
||||
|
|
@ -17,7 +16,7 @@ import {
|
|||
|
||||
export class ResourcesListScenario implements ClientScenario {
|
||||
name = 'resources-list';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test listing available resources.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -98,7 +97,7 @@ export class ResourcesListScenario implements ClientScenario {
|
|||
|
||||
export class ResourcesReadTextScenario implements ClientScenario {
|
||||
name = 'resources-read-text';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test reading text resource.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -185,7 +184,7 @@ Implement resource \`test://static-text\` that returns:
|
|||
|
||||
export class ResourcesReadBinaryScenario implements ClientScenario {
|
||||
name = 'resources-read-binary';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test reading binary resource.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -270,7 +269,7 @@ Implement resource \`test://static-binary\` that returns:
|
|||
|
||||
export class ResourcesTemplateReadScenario implements ClientScenario {
|
||||
name = 'resources-templates-read';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test reading resource from template.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -372,7 +371,7 @@ Returns (for \`uri: "test://template/123/data"\`):
|
|||
|
||||
export class ResourcesSubscribeScenario implements ClientScenario {
|
||||
name = 'resources-subscribe';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test subscribing to resource updates.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -443,7 +442,7 @@ Example request:
|
|||
|
||||
export class ResourcesNotFoundErrorScenario implements ClientScenario {
|
||||
name = 'sep-2164-resource-not-found';
|
||||
specVersions: SpecVersion[] = [DRAFT_PROTOCOL_VERSION];
|
||||
readonly source = { introducedIn: DRAFT_PROTOCOL_VERSION } as const;
|
||||
description = `Test error handling for non-existent resources (SEP-2164).
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -602,7 +601,7 @@ This scenario does not require the server to register any specific resource —
|
|||
|
||||
export class ResourcesUnsubscribeScenario implements ClientScenario {
|
||||
name = 'resources-unsubscribe';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test unsubscribing from resource.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@
|
|||
* Multiple concurrent streams are achieved via POST requests, each getting their own stream.
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types.js';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types.js';
|
||||
import { EventSourceParserStream } from 'eventsource-parser/stream';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
||||
|
||||
export class ServerSSEMultipleStreamsScenario implements ClientScenario {
|
||||
name = 'server-sse-multiple-streams';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-11-25' } as const;
|
||||
description =
|
||||
'Test server supports multiple concurrent POST SSE streams (SEP-1699)';
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* - Replaying events when client reconnects with Last-Event-ID
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types.js';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types.js';
|
||||
import { EventSourceParserStream } from 'eventsource-parser/stream';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
||||
|
|
@ -67,7 +67,7 @@ function createLoggingFetch(checks: ConformanceCheck[]) {
|
|||
|
||||
export class ServerSSEPollingScenario implements ClientScenario {
|
||||
name = 'server-sse-polling';
|
||||
specVersions: SpecVersion[] = ['2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-11-25' } as const;
|
||||
description =
|
||||
'Test server SSE polling via test_reconnection tool that closes stream mid-call (SEP-1699)';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Tools test scenarios for MCP servers
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types';
|
||||
import { connectToServer, NotificationCollector } from './client-helper';
|
||||
import {
|
||||
CallToolResultSchema,
|
||||
|
|
@ -90,7 +90,7 @@ export function buildToolsNameFormatCheck(
|
|||
|
||||
export class ToolsListScenario implements ClientScenario {
|
||||
name = 'tools-list';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test listing available tools.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -177,7 +177,7 @@ export class ToolsListScenario implements ClientScenario {
|
|||
|
||||
export class ToolsCallSimpleTextScenario implements ClientScenario {
|
||||
name = 'tools-call-simple-text';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test calling a tool that returns simple text.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -262,7 +262,7 @@ Implement tool \`test_simple_text\` with no arguments that returns:
|
|||
|
||||
export class ToolsCallImageScenario implements ClientScenario {
|
||||
name = 'tools-call-image';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test calling a tool that returns image content.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -350,7 +350,7 @@ Implement tool \`test_image_content\` with no arguments that returns:
|
|||
|
||||
export class ToolsCallMultipleContentTypesScenario implements ClientScenario {
|
||||
name = 'tools-call-mixed-content';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test tool returning multiple content types.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -451,7 +451,7 @@ Implement tool \`test_multiple_content_types\` with no arguments that returns:
|
|||
|
||||
export class ToolsCallWithLoggingScenario implements ClientScenario {
|
||||
name = 'tools-call-with-logging';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test tool that sends log messages during execution.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -540,7 +540,7 @@ Implement tool \`test_tool_with_logging\` with no arguments.
|
|||
|
||||
export class ToolsCallErrorScenario implements ClientScenario {
|
||||
name = 'tools-call-error';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test tool error reporting.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -625,7 +625,7 @@ Implement tool \`test_error_handling\` with no arguments.
|
|||
|
||||
export class ToolsCallWithProgressScenario implements ClientScenario {
|
||||
name = 'tools-call-with-progress';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test tool that reports progress notifications.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -745,7 +745,7 @@ If no progress token provided, just execute with delays.
|
|||
|
||||
export class ToolsCallSamplingScenario implements ClientScenario {
|
||||
name = 'tools-call-sampling';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test tool that requests LLM sampling from client.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -873,7 +873,7 @@ Implement tool \`test_sampling\` with argument:
|
|||
|
||||
export class ToolsCallElicitationScenario implements ClientScenario {
|
||||
name = 'tools-call-elicitation';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test tool that requests user input (elicitation) from client.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -1004,7 +1004,7 @@ Implement tool \`test_elicitation\` with argument:
|
|||
|
||||
export class ToolsCallAudioScenario implements ClientScenario {
|
||||
name = 'tools-call-audio';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test calling a tool that returns audio content.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -1099,7 +1099,7 @@ Implement tool \`test_audio_content\` with no arguments that returns:
|
|||
|
||||
export class ToolsCallEmbeddedResourceScenario implements ClientScenario {
|
||||
name = 'tools-call-embedded-resource';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test calling a tool that returns embedded resource content.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
* Utilities test scenarios for MCP servers
|
||||
*/
|
||||
|
||||
import { ClientScenario, ConformanceCheck, SpecVersion } from '../../types';
|
||||
import { ClientScenario, ConformanceCheck } from '../../types';
|
||||
import { connectToServer } from './client-helper';
|
||||
|
||||
export class LoggingSetLevelScenario implements ClientScenario {
|
||||
name = 'logging-set-level';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test setting logging level.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -86,7 +86,7 @@ export class LoggingSetLevelScenario implements ClientScenario {
|
|||
|
||||
export class PingScenario implements ClientScenario {
|
||||
name = 'ping';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test ping utility for connection health check.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
@ -176,7 +176,7 @@ export class PingScenario implements ClientScenario {
|
|||
|
||||
export class CompletionCompleteScenario implements ClientScenario {
|
||||
name = 'completion-complete';
|
||||
specVersions: SpecVersion[] = ['2025-06-18', '2025-11-25'];
|
||||
readonly source = { introducedIn: '2025-06-18' } as const;
|
||||
description = `Test completion endpoint.
|
||||
|
||||
**Server Implementation Requirements:**
|
||||
|
|
|
|||
Loading…
Reference in a new issue