Skip to content

feat(cli): --extensions/--exclude-extensions scenario selection - #420

Open
pcarleton wants to merge 1 commit into
mainfrom
paulc/extension-filter
Open

feat(cli): --extensions/--exclude-extensions scenario selection#420
pcarleton wants to merge 1 commit into
mainfrom
paulc/extension-filter

Conversation

@pcarleton

Copy link
Copy Markdown
Member

Problem

Extension scenarios (DPoP, WIF, client-credentials, EMA, tasks) are optional by definition, but --suite all selects them unconditionally. SDKs that don't implement an extension end up baselining its scenarios in expected-failures.yml — a file whose stated semantics ("burns down per milestone", "MUST be removed as they're fixed") misrepresent optional surface as conformance debt. Both typescript-sdk and python-sdk currently carry DPoP/WIF and tasks entries for exactly this reason.

Change

Explicit, flexible runner config for which extensions run:

  • --extensions <ids> — keep only these extensions' scenarios in the selected suite (none deselects them all). Asking for an extension that contributes no scenarios to the suite is an error, so a wrong suite or a --spec-version that already filtered it out can't silently pass as tested.
  • --exclude-extensions <ids> — run everything but these (none deselects nothing; excluding an absent extension is a satisfied no-op). Mutually exclusive with --extensions (commander conflicts).

Semantics:

  • IDs comma-separated, full or short form (auth/dpop, tasks, io.modelcontextprotocol/tasks).
  • Spec-timeline scenarios always pass through; an explicit --scenario runs unfiltered (a note is printed when combined with the flags).
  • Deselected scenarios are logged, so CI output shows exactly what was opted out — no silent exclusions.
  • Empty values error (--extensions "$UNSET_VAR" must not silently change which scenarios run); a selection that deselects every scenario in a non-empty suite fails loudly.
  • list accepts both flags as a dry-run preview of a selection; sdk passes them through to the child client/server invocations (including empty values, so the child's guard fires, and with the same immediate conflict check).
  • authorization is untouched — no extension-tagged AS scenarios exist, so the flags would be a guaranteed error there.

Verification

  • typescript-sdk v2: client --suite all --exclude-extensions auth/dpop,auth/wif439 passed, 0 failed with no expected-failures file (today this requires baselining 3 scenarios).
  • python-sdk: server --suite all --extensions none137 passed, 0 failed with no baseline (today: 9 baselined tasks scenarios).
  • ~20 adversarial CLI probes (empty values, none on both flags, conflicts, unknown IDs, include-with-no-match, spec-version interactions, suite emptied by selection, --scenario combinations).
  • 10 new unit tests in src/scenarios/extension-filter.test.ts; full suite 459/459; typecheck + lint clean.

Follow-up once released: both SDKs can swap their DPoP/WIF/tasks baseline entries for --exclude-extensions legs (the stale-entry enforcement forces the cleanup in the same pin-bump PR).

Extension scenarios (DPoP, WIF, client-credentials, EMA, tasks) are
optional by definition, but --suite all selects them unconditionally,
so SDKs that don't implement an extension end up baselining its
scenarios as expected failures — a file whose semantics ("burns down
per milestone") misrepresent optional surface as conformance debt.

Give the runner explicit extension selection instead:

- --extensions <ids>: keep only the listed extensions' scenarios in
  the selected suite ("none" deselects them all). Asking for an
  extension with no scenarios in the suite is an error, so a wrong
  suite or a --spec-version that already filtered them out cannot
  silently pass as tested.
- --exclude-extensions <ids>: drop the listed extensions' scenarios
  and run the rest ("none" deselects nothing; excluding an absent
  extension is a satisfied no-op). Mutually exclusive with
  --extensions via commander conflicts.

IDs may omit the io.modelcontextprotocol/ prefix (auth/dpop, tasks).
Spec-timeline scenarios always pass through, an explicit --scenario
runs unfiltered (with a note when combined with the flags), deselected
scenarios are logged, an empty value errors (an unset CI variable must
not silently change which scenarios run), and a selection that
deselects every scenario in a non-empty suite fails loudly. The list
command accepts both flags as a silent dry-run preview, and sdk-runner
passes them through to the client/server invocations. The
authorization command is untouched: no extension-tagged AS scenarios
exist, so the flags would be a guaranteed error there.

Verified end-to-end: typescript-sdk client --suite all
--exclude-extensions auth/dpop,auth/wif passes 439/0 with no
expected-failures file, and python-sdk server --suite all
--extensions none passes 137/0 likewise.
@pkg-pr-new

pkg-pr-new Bot commented Jul 27, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: c5a2732

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.

1 participant