Skip to content

feat: support "draft" as a first-class spec version target#255

Open
pcarleton wants to merge 1 commit intomainfrom
paulc/draft-spec-version
Open

feat: support "draft" as a first-class spec version target#255
pcarleton wants to merge 1 commit intomainfrom
paulc/draft-spec-version

Conversation

@pcarleton
Copy link
Copy Markdown
Member

Closes #253.

Problem

SEP authors testing against the in-progress spec can't run the full suite: the initialize check rejects draft protocolVersion strings, and --spec-version draft only selects the handful of explicitly draft-tagged scenarios (skipping initialize and the rest of the core suite). Tagging core scenarios with 'draft' is blocked by the spec-version isolation test.

Raised by @mikekistler in #conformance-testing-wg while testing SEP-2243.

Naming: 'draft' vs 'DRAFT-2026-v1' vs '2026-06-DRAFT'

There are two distinct namespaces in play, and the spec repo treats them differently:

Namespace Spec-repo precedent Used here
Docs / scenario tag docs/specification/draft/ — the folder is literally draft, renamed to a date on release SpecVersion = ... | 'draft' (unchanged)
Wire protocolVersion schema/draft/schema.tsLATEST_PROTOCOL_VERSION has cycled DRAFT-2025-v1-v2-v3DRAFT-2026-v1 (history) DRAFT_PROTOCOL_VERSION = 'DRAFT-2026-v1'

So this PR keeps 'draft' as the conformance scenario tag (it's a folder alias, not a wire value, and survives release renames cleanly), and pins the exact current draft wire string as DRAFT_PROTOCOL_VERSION. We considered a permissive /^draft/i regex but rejected it: an SDK sending a stale DRAFT-2025-v3 is testing against a draft that no longer exists, and that should fail. The constant needs bumping when the spec repo bumps theirs — same maintenance model as LATEST_SPEC_VERSION. A '2026-06-DRAFT' form was also rejected: the spec repo doesn't use it anywhere and the release date isn't known in advance.

Changes

  • types.ts: add DATED_SPEC_VERSIONS, LATEST_SPEC_VERSION, and DRAFT_PROTOCOL_VERSION constants plus specVersionToProtocolVersion(). Next spec release (or draft revision bump) is a one-line change here.
  • checks/client.ts / scenarios/client/initialize.ts: accept DRAFT_PROTOCOL_VERSION as a valid protocolVersion (exact match — stale draft strings like DRAFT-2025-v1 are rejected). Mock server echoes it back.
  • scenarios/index.ts: new matchesSpecVersion() helper. --spec-version draft now resolves to latest-dated ∪ draft-tagged — scenarios stay tagged draft-only, but selection treats draft as a superset of the current release.
  • runner/client.ts / index.ts: forward the resolved wire version to the client process via MCP_CONFORMANCE_PROTOCOL_VERSION (e.g., --spec-version draftDRAFT-2026-v1). Example clients can use this directly as their protocolVersion without maintaining their own mapping; SDKs that hard-code their version can ignore it.
  • Tests: superset relationship + tag-level isolation asserted separately; positive test for DRAFT_PROTOCOL_VERSION, negative tests reject 'DRAFT-2025-v1' and bare 'draft'.
  • README documents --spec-version and the new env var.

Behavioral note: tier-check

tier-check calls listScenariosForSpec (test-conformance-results.ts:208,264), so a tier-check run with --spec-version draft will now score against latest ∪ draft instead of draft-only. This is internally consistent (filter and expected-list both go through the same function) but means draft tier scoring is meaningful only as "would this SDK pass tier N if draft shipped today."

Testing

$ npx vitest run
Test Files  8 passed (8)
     Tests  116 passed (116)

Typecheck and lint clean.

Follow-ups (not in this PR)

  • Range-based tagging — replace per-scenario specVersions: [...] lists with introducedIn / optional removedIn. The superset model in this PR can't express a SEP that tightens or removes an existing requirement (e.g., SEP-986 narrowed valid tool-name format) — there's no subtraction arm. Every spec release also currently requires touching every carried-forward scenario. matchesSpecVersion() is the seam where range logic slots in; the migration is mechanical across ~40 scenario declarations. ('extension' would stay an orthogonal flag since it's off the dated timeline.)
  • Migrating remaining hard-coded '2025-11-25' literals in server scenarios to LATEST_SPEC_VERSION.

- Add LATEST_SPEC_VERSION and DATED_SPEC_VERSIONS constants so the next
  spec release is a one-line change in types.ts.
- Accept "draft" as a valid protocolVersion in the initialize check and
  mock-server response.
- --spec-version draft now selects latest-dated scenarios plus draft-tagged
  ones, so SEP authors can run the full suite against an SDK tracking the
  in-progress spec without retagging core scenarios.
- Forward --spec-version to the client process via
  MCP_CONFORMANCE_SPEC_VERSION so SDK examples can pick the matching
  protocolVersion.

Closes #253
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 24, 2026

Open in StackBlitz

npx https://pkg.pr.new/@modelcontextprotocol/conformance@255

commit: 78514c2

Copy link
Copy Markdown
Contributor

@felixweinberger felixweinberger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, optional nit on consolidating a single constant instead of having 2 copies

Comment on lines +122 to +126
const VALID_VERSIONS = [
'2025-06-18',
LATEST_SPEC_VERSION,
DRAFT_PROTOCOL_VERSION
];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could consider consolildating a VALID_VERSIONS constant in types.ts instead of having 2 copies here and in client.ts

Comment thread src/types.ts
// could reuse the filter plumbing. It has no corresponding wire
// protocolVersion. Split it out of this type when moving to
// introducedIn/removedIn tagging.
if (version === 'extension') return undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels like something worth cleaning up, spec vs protocolVersion might get more confusing over time the longer we have both of these 🤔

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 we need a different way to refer to extensions, it doesn't super make sense to run "all extensions" as a selector. ties a bit into my comment on #114 about making it easier to selectively run scenarios via a config file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support "draft" as a first-class spec version target

2 participants