MCP 2026-07-28 Spec

View Source

Purpose

Map every feature in the MCP 2026-07-28 specification — the modern era — against what wymcp implements, to guide planning. Companion to mcp-spec-2025-11-25-overview.md (the legacy era); the pair is the yardstick while wymcp is dual-era. Era vocabulary: docs/glossary.md (era, lane, era classification, protocol fields).

Status column: the modern core is implemented; scope notes name the planned change that owns each remaining gap.

1. Base Protocol

1.1 The stateless model

The modern era has no handshake and no sessions (SEP-2575, SEP-2567). Every request is self-contained: servers MUST NOT rely on prior requests for context. Cross-call state is the server's own business, carried as explicit handles in ordinary tool arguments.

FeatureSpec requirementwymcp status
Per-request protocol fields in _meta (io.modelcontextprotocol/protocolVersion, …/clientCapabilities)MUST (both required on every request)Wymcp.Plugs.Era classifies on them; Wymcp.Plugs.ProtocolFields enforces both
Reject missing protocol fields with -32602 + HTTP 400MUSTWymcp.Plugs.ProtocolFields (data.reason names the missing field)
io.modelcontextprotocol/clientInfo on requestsSHOULD (client-side)N/A by design: stateless, nothing stored
io.modelcontextprotocol/serverInfo in every result's _metaSHOULDWymcp.Modern stamps it (built by Wymcp.ServerInfo from the :server_info router opt + app config)
resultType on every result ("complete" / "input_required"; extensions may add values)MUST"complete" on every modern result ("input_required" arrives with the planned MRTR change)
Unknown method → HTTP 404 + -32601MUST✅ modern dispatch table (legacy lane still answers 200 — era-varying cell)
Error-code allocation: -32020..-32099 spec-reserved, -32000..-32019 implementation-definedMUST NOT misallocate-32022 allocated; legacy -32001 stays legacy-lane-only

1.2 Version negotiation & era classification

FeatureSpec requirementwymcp status
server/discover — supportedVersions, capabilities (incl. extensions), optional instructions; serverInfo in result _meta; cache hintsMUST implement (clients MAY skip)Wymcp.Methods.Discover
UnsupportedProtocolVersionError -32022 + 400 with data.{supported, requested}MUSTWymcp.Plugs.ProtocolFields
MissingRequiredClientCapabilityError -32021 + 400 with data.requiredCapabilitiesMUST❌ no core emitter (first emitter arrives with the planned MRTR change)
Dual-era serving on one endpoint (modern _meta → stateless; initialize → legacy session semantics)MAYWymcp.Plugs.Era (its moduledoc carries the classification table): era-mix fails closed; no-signal requests classify legacy with a dual-instruction rejection
Era determination cached per originClient-sideN/A

1.3 Streamable HTTP transport (modern)

FeatureSpec requirementwymcp status
Single endpoint, POST-onlyMUST✅ the modern lane is POST-only (GET/DELETE routes remain legacy-lane machinery until the legacy drop; a modern-only server would answer 405)
Response: application/json or SSE response streamMUST offer one; client MUST accept both✅ answers plain JSON (no modern SSE responses — no progress/logging in core scope)
SSE event IDs / Last-Event-ID resumptionRemoved; ignore the header✅ the modern lane never opens a stream (legacy lane keeps its counter behavior)
Closing the SSE response stream = cancellationMUST treat disconnect as cancellationN/A in core scope (no modern SSE responses)
Origin validationMUST✅ origin check — wire checks are era-invariant, run before era classification
Auth per requestStateless model✅ auth check — era-invariant
MCP-Protocol-Version header on every POST, mirroring the body field; Mcp-Method; Mcp-Name; Mcp-Param-* via x-mcp-header; mismatch/missing → -32020 + 400MUST❌ a planned header-binding change (core reads the protocol fields as authoritative and ignores these headers)
Notification POSTs → 202MUST✅ 202-and-drop (the modern core defines no client→server notifications over HTTP)

1.4 Authorization

Unchanged model for wymcp's scope (Bearer via the Wymcp.Auth behaviour). 2026-07-28 auth deltas (RFC 9207 iss validation, Client ID Metadata Documents, application_type in DCR) are client-side or OAuth-server-side — no wymcp surface today.


2. Server Features

2.1 Tools

FeatureSpec requirementwymcp status
tools/listMUST if capability declared✅ served from compile-time tools; runtime tools are legacy-lane-only
tools/callMUST if capability declared✅ session-free Context (session_pid: nil by construction)
Unknown tool name → -32602 (InvalidParamsError)Defined✅ era-varying cell: the legacy lane keeps its pre-existing -32601
Tool set MUST NOT vary per-connection (MAY vary by authorization)MUST✅ holds by construction: compile-time list
Deterministic tools/list orderSHOULD✅ by construction (compile-time order)
Cache hints on tools/list (ttlMs, cacheScope)MUST✅ framework defaults ttlMs: 300_000, cacheScope: "private" (Wymcp.Modern)
listChanged capability + notifications/tools/list_changed over subscriptions/listenMAY❌ deferred — modern lane declares no listChanged; TTL-only freshness (spec-sanctioned); a subscriptions/listen implementation is planned
Pagination (cursor / nextCursor)SHOULD❌ (missing on the legacy lane too — era-neutral gap)
inputSchema/outputSchema loosened to full JSON Schema 2020-12; $ref no network deref; composition boundsMUST (bounds SHOULD)✅ JSV validates full 2020-12 drafts and wymcp-generated schemas are simple
structuredContent may be any JSON valueDefined❌ core (the legacy lane still assumes an object)
x-mcp-header argument annotationsMAY (invalid ⇒ client drops tool)❌ a planned header-binding change

2.2 Resources, Prompts, Completion

Unimplemented in wymcp on both lanes (era-neutral gaps — legacy overview §§2.2–2.3, §4.5). Modern deltas when they arrive: results become cacheable (ttlMs/cacheScope MUST on the five list/read operations), resources/subscribe is replaced by subscriptions/listen, resource-not-found is -32602 (was -32002).


3. Server-initiated interaction (MRTR)

Server→client JSON-RPC requests are forbidden in the modern era. MRTR (SEP-2322) replaces them: only tools/call, resources/read, and prompts/get may answer resultType: "input_required" with inputRequests (ElicitRequest / CreateMessageRequest / ListRootsRequest) and/or opaque requestState; the client retries the original request (new JSON-RPC id) threading inputResponses. Interim results are never cacheable. Servers MUST NOT request capabilities the client did not declare (-32021 otherwise).

FeatureSpec requirementwymcp status
MRTR on tools/call (elicitation; sampling/roots are Deprecated features)Pattern defined❌ the planned MRTR change (obsoletes deliver_response on the modern lane)
notifications/elicitation/complete, elicitationIdRemovedN/A

4. Utilities

4.1 Removed / deprecated in 2026-07-28

Legacy featureModern fatewymcp consequence
pingRemovedModern lane has no ping; 404 + -32601. Legacy lane keeps it
initialize / notifications/initializedRemoved (absent from the modern schema)Legacy-lane-only; era-classification signal
Sessions / Mcp-Session-Id / DELETE teardownRemovedLegacy-lane-only; header on a modern-fields request = era-mix, fails closed (wymcp policy)
GET standalone SSE streamRemoved → subscriptions/listenLegacy-lane-only; listen deferred (a subscriptions/listen implementation is planned)
SSE event IDs / Last-Event-ID replayRemoved; broken stream → client re-issuesLegacy-lane-only
logging/setLevelRemoved; per-request _meta logLevel opt-in defined — but the whole Logging feature is Deprecated (SEP-2577)Modern lane implements no logging (wymcp's decision): no capability, no notifications/message; Context.log answers {:error, :not_supported}
notifications/roots/list_changedRemovedNever implemented
Roots, Sampling, Logging featuresDeprecated (≥ 12-month window)Informs the planned MRTR change's scope (elicitation is the live part)
notifications/cancelled (client → server)stdio-only; on HTTP, stream close is the signalModern lane: none (no SSE responses in core scope). Legacy lane keeps Methods.Cancelled
Tasks (experimental core feature)Moved to extension io.modelcontextprotocol/tasks (SEP-2663): tasks/get polling, tasks/update, no tasks/list, no blocking tasks/result❌ a planned tasks-extension change

4.2 Progress

progressToken / notifications/progress survive, riding the originating request's SSE response stream. ❌ out of core scope (no modern SSE responses; zero consumer usage of report_progress — it stays :ok, its silent no-op no-session value, on the modern lane). Becomes real work the day a consumer needs it; a natural companion to the planned MRTR change's stream machinery.

4.3 Caching (SEP-2549)

FeatureSpec requirementwymcp status
ttlMs (int ≥ 0) + cacheScope ("public"/"private") on resultType: "complete" results of server/discover, tools/list (+ prompts/resources ops when they exist)MUSTWymcp.Modern defaults: discover 3_600_000, tools/list 300_000, both "private"
TTL is a freshness hint; no revalidation mechanism; stale-if-error allowedDefinedN/A (client-side)
"private" caches never shared across authorization contextsMUST (cache-side)N/A — but the reason "private" is wymcp's never-wrong default

4.4 Subscriptions (subscriptions/listen)

Single long-lived POST-response stream for opted-in change notifications (toolsListChanged, …); server MUST send notifications/subscriptions/acknowledged first; every stream message carries io.modelcontextprotocol/subscriptionId; server MUST NOT send un-requested types. ❌ deferred whole (a subscriptions/listen implementation is planned) — the modern lane's tool list cannot change within a process, so there is nothing to signal.

4.5 Extensions

extensions maps on both capability objects (SEP-2133): identifier → settings, reverse-DNS-prefixed identifiers, revert-to-core-or-reject negotiation. ❌ no core scope beyond serving an absent field; first real use is the planned tasks extension.

4.6 OpenTelemetry trace context

_meta conventions for traceparent/tracestate/baggage (SEP-414, documented, not required). ❌ not in scope; kin to the deferred rejection-observability work.


5. Summary

The modern era in one paragraph: stateless, POST-only, per-request protocol fields instead of a handshake, server/discover instead of initialize, cache hints instead of a notification stream (unless a client opens subscriptions/listen), MRTR instead of server-initiated requests, tasks as an extension, and Logging/Sampling/Roots deprecated.

wymcp today: the modern core is live beside the 2025-11-25-only legacy lane — era classification with the fail-closed era-mix policy, server/discover, protocol-fields enforcement (-32602/-32022), resultType: "complete", serverInfo result _meta, cache hints, plain-JSON responses, and era-tagged telemetry. Planned changes own MRTR, header binding (-32020, Mcp-Method/Mcp-Name), subscriptions/listen, the tasks extension, and the eventual legacy drop (which deletes the legacy lane and its overview doc, ending the dual-era period).