Skip to content

feat: add SEP-2567 (sessionless Streamable HTTP) requirement traceability and server scenario#333

Open
felixweinberger wants to merge 2 commits into
mainfrom
fweinberger/sep-2567-suite
Open

feat: add SEP-2567 (sessionless Streamable HTTP) requirement traceability and server scenario#333
felixweinberger wants to merge 2 commits into
mainfrom
fweinberger/sep-2567-suite

Conversation

@felixweinberger

Copy link
Copy Markdown
Collaborator

Adds requirement traceability and a draft-suite server scenario for
SEP-2567 (sessionless Streamable HTTP), following the sep-2575 pattern.

Motivation and Context

Closes #332.

SEP-2567 is merged into the draft spec: it removes protocol-level sessions
and the Mcp-Session-Id header from the Streamable HTTP transport, and
makes the list endpoints (tools/list, resources/list, prompts/list)
connection-invariant (see the draft changelog).
The conformance suite has no coverage for it yet — there is no
src/seps/sep-2567.yaml and no scenario exercises the sessionless
invariants at the draft spec version.

This PR adds both, mirroring how SEP-2575 is covered (sep-2575.yaml +
ServerStatelessScenario).

src/seps/sep-2567.yaml — 7 check rows, 3 excluded rows

Checks:

  • sep-2567-server-accepts-requests-without-session-id — sessions are
    removed at the draft revision; a request without Mcp-Session-Id is
    served normally. (Anchored to the changelog entry — the removal has no
    keyworded sentence by construction.)
  • sep-2567-{tools,resources,prompts}-list-connection-invariant — the
    list "MUST NOT vary per-connection or as a side effect of other requests
    on the connection" (server/tools, server/resources, server/prompts
    #capabilities).
  • sep-2567-server-rejects-get-and-delete,
    sep-2567-server-ignores-session-id,
    sep-2567-server-ignores-last-event-id — the SHOULDs from
    Earlier Streamable HTTP Revisions
    for servers that support only the draft revision.

Excluded (with reasons in the yaml):

  • request-ID reuse scoping (basic/index#requests) — not observable from
    a black-box harness; the harness cannot compel the implementation under
    test to issue two concurrent requests with colliding IDs.
  • the reworded authorization sentence — editorial (SEP-2567 only drops the
    "same logical session" clause); the obligation predates the SEP and is
    exercised by the authorization scenarios.
  • the SEP's re-scoped SSE event-id uniqueness rule — superseded before
    publication; the draft later removed SSE resumability entirely, so the
    sentence no longer appears in the spec.

ServerSessionlessScenario (--suite draft, server-sessionless)

One scenario, seven checks, registered like the other draft scenarios via
source.introducedIn = DRAFT_PROTOCOL_VERSION. Built on the existing
sendStatelessRequest() connection helper.

The three backward-compatibility SHOULDs are scoped by the spec to "a
server that supports only this revision". The scenario probes with a
legacy-shaped initialize request first; dual-era servers (the
everything-server is one) report SKIPPED for those three checks, per the
spec's instruction that dual-era servers implement the legacy revision's
behavior instead.

List invariance is checked by taking two paginated snapshots of each
declared list endpoint on independent connections, with unrelated requests
interleaved, and comparing the identifier sets. (Per-connection state is
not directly addressable from a black-box harness; every harness request
already arrives on an independent connection — same model as the
sep-2575.yaml exclusions.)

How Has This Been Tested?

  • npm test — 278/278 passing, including all-scenarios.test.ts, which
    runs server-sessionless against the everything-server (4 SUCCESS,
    3 SKIPPED via the dual-era probe; no FAILURE).
  • 9 new vitest cases in src/scenarios/server/sessionless.test.ts drive
    the scenario against mocked non-conformant servers and assert each check
    fires with the right severity (FAILURE for MUST rows, WARNING for SHOULD
    rows), plus the SKIPPED paths (undeclared capabilities, dual-era).
  • npm run build, npm run typecheck, npm run lint all pass.

src/seps/traceability.json is not regenerated here; the traceability
workflow refreshes it via PR.

Breaking Changes

None. The scenario is draft-suite only (--suite draft / --suite all);
the default active suite is unchanged.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Severity follows the spec keyword per AGENTS.md (MUST → FAILURE, SHOULD →
WARNING). Requirement texts were verified against SEP-2567's spec diff
(modelcontextprotocol/modelcontextprotocol#2567) and the current draft
pages; the three backcompat SHOULDs come from the draft's
"Earlier Streamable HTTP Revisions" section, which is the live normative
expression of the sessionless model for legacy traffic.

Related prior art: #222 covers the 2025-11-25 "server MAY omit
Mcp-Session-Id" path at the dated spec versions; this PR covers the draft
revision where sessions are removed outright, so the two are complementary.

Map SEP-2567 (remove protocol-level sessions from Streamable HTTP) to
conformance requirements: the no-session-required invariant, list-endpoint
connection invariance for tools/resources/prompts, and the
backward-compatibility handling of legacy session-era traffic. Requirements
that are not observable from a black-box harness (request-id reuse scoping,
the reworded authorization sentence, the superseded SSE event-id rule) are
documented as excluded with reasons.
Add a draft-suite server scenario covering sessionless Streamable HTTP
operation:

- a request without an Mcp-Session-Id header is served normally
- tools/list, resources/list and prompts/list return the same set across
  independent connections, with unrelated requests interleaved between
  snapshots (paginated lists are accumulated before comparing)
- a server that supports only the draft revision handles legacy
  session-era traffic per the backward-compatibility section: 405 for
  GET/DELETE, ignore Mcp-Session-Id without minting or echoing session
  IDs, ignore Last-Event-ID

The backward-compatibility SHOULDs are scoped to servers that support
only the draft revision, so the scenario probes with a legacy-shaped
initialize request first and reports SKIPPED for dual-era servers (the
everything-server is one).

Negative tests drive the scenario against mocked non-conformant servers
and assert each check fires with the right severity (FAILURE for MUST
rows, WARNING for SHOULD rows).
@pkg-pr-new

pkg-pr-new Bot commented Jun 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 29d80ce

@felixweinberger

Copy link
Copy Markdown
Collaborator Author

Short version of what this conformance test checks:

  1. server-accepts-requests-without-session-id — sends draft-version requests with no Mcp-Session-Id header and fails the server if it refuses to serve them.
  2. tools-list-connection-invariant — fetches the full tools/list twice on independent connections (with unrelated requests interleaved) and fails if the tool sets differ.
  3. resources-list-connection-invariant — same two-snapshot comparison for resources/list.
  4. prompts-list-connection-invariant — same two-snapshot comparison for prompts/list.
  5. server-rejects-get-and-delete — warns if a draft-only server answers HTTP GET or DELETE on the MCP endpoint with anything other than 405 Method Not Allowed.
  6. server-ignores-session-id — sends a request carrying a bogus Mcp-Session-Id and warns if the server rejects it or mints/echoes any session ID back.
  7. server-ignores-last-event-id — sends a request carrying Last-Event-ID: 42 and warns if the server fails to serve it normally (draft streams aren't resumable).

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.

Add SEP-2567 (sessionless Streamable HTTP) conformance coverage

1 participant