Implement MCP 2026-07-28 (#2286)
Co-authored-by: Matt <77928207+mattzcarey@users.noreply.github.com> Co-authored-by: Konstantin Konstantinov <KKonstantinov@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
e4227d135c
commit
1823aae891
1 changed files with 69131 additions and 9001 deletions
|
|
@ -58,6 +58,27 @@ note: 'stateless hosting has no server→client back-channel'
|
|||
`addedInSpecVersion` / `removedInSpecVersion` bound the spec versions a requirement applies to. A behavior changed by a spec release gets a sibling entry: the new entry lists every retired id it replaces in `supersedes` (an array, requires `addedInSpecVersion`), and each retired
|
||||
entry points back via `supersededBy` (requires `removedInSpecVersion`). A coverage gate enforces that the links resolve and are exactly symmetric.
|
||||
|
||||
## The createMcpHandler entry arms (entryStateless / entryModern)
|
||||
|
||||
Two transport arms host the dual-era HTTP entry (`createMcpHandler`) in process via an injected fetch, exactly like the other HTTP arms. They are era-fixed (`TRANSPORT_SPEC_VERSIONS`), so each registers cells on exactly one spec-version axis:
|
||||
|
||||
- `entryStateless` — the entry with its stateless legacy fallback (`legacy: 'stateless'`, the entry's default posture, passed explicitly so the arm stays era-pinned); the scenario's plain client is served per request through the fallback. Cells run on the 2025-11-25 axis only.
|
||||
- `entryModern` — the entry hosted modern-only strict (`legacy: 'reject'`); the arm pins the scenario's client to the 2026-07-28 revision via `setVersionNegotiation()`, and the client attaches the per-request `_meta` envelope to every outgoing request/notification itself. Cells
|
||||
run on the 2026-07-28 axis only. The pin is unconditional, so a scenario that needs to assert non-pin negotiation behavior (e.g. `mode: 'auto'` probing) must restrict off `entryModern` or drive a non-entry transport.
|
||||
|
||||
Both arms are part of the default transport list, so unrestricted requirements run through the entry automatically. When a requirement cannot run on an entry arm, annotate it with a machine-readable reason instead of bending the test:
|
||||
|
||||
```ts
|
||||
entryExclusions: [{ arm: 'entryModern', reason: 'method-not-in-modern-registry' /* optional note */ }];
|
||||
```
|
||||
|
||||
Omitting `arm` excludes both arms. The reasons (`EntryExclusionReason` in types.ts) are the acceptance checklist for re-admitting cells when the corresponding entry feature lands; a coverage gate rejects annotations that would never have an effect. Requirement families that the
|
||||
per-request entry structurally cannot serve at all (server→client requests, sessions/resumability, standalone GET streams) are already expressed through their `transports` restrictions and need no annotation.
|
||||
|
||||
Arm-specific helpers: `wire()`'s fourth argument also accepts `entry` (createMcpHandler hosting overrides — e.g. a `responseMode` or a different `legacy` posture), the returned `Wired.httpLog` records every HTTP exchange (request body, status, content-type, a readable response
|
||||
clone) for raw wire assertions, factories may accept the optional per-request context (`EntryServerFactory`), and `modernEnvelopeMeta()` builds the envelope for bodies that POST raw 2026-era requests through `wired.fetch`. Compositions that the entry no longer expresses through
|
||||
an option (for example an existing sessionful legacy wiring routed via `isLegacyRequest` next to a strict entry) are hosted by the test body itself behind an in-process fetch — see `scenarios/hosting-entry-session.test.ts`.
|
||||
|
||||
## Running
|
||||
|
||||
From the repo root (the suite is the `@modelcontextprotocol/test-e2e` workspace package):
|
||||
|
|
|
|||
Loading…
Reference in a new issue