MCP 2026-07-28 Protocol and Compatibility Decision

Tracker references: pm-sqvshj, pm-vae5ec, and pm-55yf1t. MRTR, durable tasks, and the cache/schema surface are tracked by pm-rz9gep, pm-rzs24j, and pm-hv1x1x. Subscriptions, Streamable HTTP, remote authorization, and the deprecation ratchet are tracked by pm-v7e337, pm-3zh9s4, and pm-vzcisw. Skills and Apps are tracked by pm-8nzivt and pm-pznhee. Progressive tool discovery and canonical model-facing results are tracked by pm-3g3f8z.

Status: accepted. MCP 2026-07-28 is pm's canonical protocol revision.

Decision

The public pm SDK owns protocol versions, request metadata validation, capability checks, discovery, result envelopes, error codes, HTTP header parity, subscriptions, authorization boundaries, trace isolation, and legacy result interpretation. pm-mcp is a JSON-RPC/stdio adapter over those contracts; pm-mcp-http binds the same dispatcher to sessionless Streamable HTTP POST.

Modern requests are stateless. Every request carries:

  • io.modelcontextprotocol/protocolVersion = 2026-07-28;
  • io.modelcontextprotocol/clientCapabilities as an object, including {};
  • optional io.modelcontextprotocol/clientInfo with name and version.

Every modern result carries an explicit resultType (complete, input_required, or task) and io.modelcontextprotocol/serverInfo in result _meta. The mandatory server/discover method returns the supported modern revisions, deterministic capabilities, public cache policy, server identity, and bounded instructions. No modern request reads identity, capabilities, or version from a previous request.

The optional dev.unbrained.pm/progressive-tool-discovery extension is also request-local. Negotiated clients receive a stable five-tool entry catalog and expand it with pm_discover; unnegotiated clients retain the complete profile-selected list. Negotiated tool results designate structuredContent.result as canonical and use text content only as a pointer, while the isolated compatibility adapter retains duplicated JSON text for existing clients. The public SDK owns ranking, cursor binding, token and omission receipts, and cache invalidation. See Progressive Tool Discovery.

Legacy boundary

Legacy means every revision that establishes a session with an initialize handshake, which the canonical revision defines as 2025-11-25 and earlier. The stdio adapter therefore accepts the full initialize-era set, newest first:

Revision Accepted at initialize Notes
2025-11-25 yes Final legacy revision; offered when a client omits protocolVersion
2025-06-18 yes Earlier legacy revision retained for existing consumers

initialize answers with the revision the client requested, because a legacy client has no fall-forward mechanism and treats the answered version as the negotiated one. A revision outside the set is refused with -32022 and the refusal names every supported revision, since that error text is the only diagnostic such a client can surface.

PM_MCP_LEGACY_PROTOCOL_VERSIONS on the public SDK surface is the single declaration of this set. The plugin MCP smoke gate reads it from the built SDK and drives one real initialize per declared revision against the released launcher, plus a negative control for an undeclared revision, so the accepted set and the documented set cannot drift apart.

Unversioned requests stay on this legacy path because they cannot claim the current revision; initialize enriches their client identity but is not required for compatibility with older pm hosts. The adapter has no session id, does not affect modern requests, and is excluded from server/discover's supportedVersions because it cannot be selected through modern per-request metadata.

The adapter is scheduled for removal only after published-client telemetry and release probes show no required legacy consumers for two consecutive release windows. Removal is a reviewed compatibility change, never a history rewrite.

Transport behavior

  • Stdio modern clients call server/discover with current request metadata, then send the same version and capability keys on every request.
  • Stdio legacy clients retain their existing response shapes and may use initialize with any accepted legacy revision to supply client identity.
  • Streamable HTTP requires MCP-Protocol-Version and Mcp-Method on every request. Mcp-Name is required only for prompts/get, resources/read, and tools/call; other methods omit it. Schema-declared x-mcp-header values are encoded, decoded, and compared with tool arguments before dispatch. Header/version mismatch uses code -32020 and HTTP 400.
  • subscriptions/listen is a request-scoped stdio or SSE stream. The first message acknowledges the supported filter; later messages carry its request id as io.modelcontextprotocol/subscriptionId. Disconnect deletes the subscription and a caller retries lost work with a new request id.
  • The remote adapter defaults to loopback, enforces exact browser origins, bounds bodies, maps parse/invalid requests to HTTP 400, and can require an issuer-, audience-, and scope-bound bearer token.
  • Unsupported modern versions return -32022 plus the exact supported modern list. Missing required capabilities return -32021 with a structured capability map. Malformed metadata uses JSON-RPC Invalid Params -32602.

Removed, migrated, and deprecated behavior

Prior behavior Disposition
initialize and notifications/initialized Legacy stdio adapter only; absent from modern behavior.
Mcp-Session-Id and protocol sessions Removed; explicit application handles are ordinary arguments.
ping Legacy adapter only; modern calls return Method Not Found.
resources/subscribe and resources/unsubscribe Replaced by the implemented subscriptions/listen filter.
SSE event ids and Last-Event-ID replay Removed; callers retry with a new request id.
Reverse roots/list, sampling, and elicitation requests Migrate to MRTR input_required under pm-rz9gep.
Core tasks Migrate to io.modelcontextprotocol/tasks under pm-rzs24j.
logging/setLevel Removed; optional request-local log metadata is never shared, and remote operations use host OpenTelemetry.
Roots, Sampling, Logging, HTTP+SSE, and non-none includeContext Retain only in bounded legacy compatibility while pm-vzcisw drives deprecation removal.
Dynamic Client Registration Legacy authorization fallback; Client ID Metadata Documents are canonical under pm-3zh9s4.

Rollout, rollback, and proof

Local and hosted gates must prove SDK primitives, direct server calls, real stdio, packed artifacts, npx, bunx, and published artifacts agree on discovery and the canonical revision. Negative controls cover unsupported versions, missing/malformed metadata, missing capabilities, header mismatch, removed methods, and omitted modern resultType.

Rollback preserves the SDK contract and re-enables only the reviewed legacy adapter. It must never reintroduce session ids or make modern behavior depend on initialization. Release evidence records exact source, package, tag, and consumer revisions separately.

MRTR, tasks, and cache behavior

An SDK handler that cannot finish without host input throws PmMcpInputRequiredError. The modern adapter validates the requested elicitation/create, roots/list, or sampling/createMessage capability and returns resultType: "input_required". Continuation state can be bounded, HMAC-sealed, expiry-bound, method-bound, parameter-bound, principal-bound, and protected from in-process replay with the public interaction helpers. Retry payloads arrive as request-local inputResponses; they never depend on a protocol session.

Clients negotiate io.modelcontextprotocol/tasks in request capabilities. Eligible long-running tools/call operations can then return a durable task handle. tasks/get, tasks/update, and tasks/cancel are principal-scoped, persist records atomically under the ignored tracker runtime area, enforce immutable terminal states, expire abandoned work deterministically, and turn a disappeared worker into an actionable terminal failure. Task state remains retrieved through the task methods. Change subscriptions do not become task-progress channels, and pm's current handlers do not emit request progress or deprecated log-message notifications.

Modern tool, resource, resource-template, and prompt list/read results carry explicit ttlMs and cacheScope. Tool schemas are validated as bounded JSON Schema 2020-12 documents before advertisement. Tool and resource data stay private; public metadata lists may be cached for their advertised lifetime.

Skills and Apps extensions

Discovery advertises the stable io.modelcontextprotocol/ui MCP Apps extension and the revision-pinned draft io.modelcontextprotocol/skills extension. They remain optional and request-local. Apps require the stable 2026-01-26 MIME capability; Skills require the exact SEP-2640 commit and explicit directory-read support for bulk reads. An incompatible Apps declaration is treated as absent by tools/list and resources/list, so those discovery methods degrade to the non-UI surface; direct ui:// reads remain strict and return the allocated missing-capability error. Skills methods fail closed on an incompatible draft declaration because their entire method family depends on that exact negotiated revision.

The public SDK owns skill parsing, digesting, pagination, origin provenance, resource bounds, App contracts, tool metadata, sandbox policy, and accessible self-contained HTML. The server only applies negotiation and dispatch. See MCP Skills and Apps for the wire examples and trust model.

Public SDK

Use PM_MCP_PROTOCOL_VERSION, resolveMcpRequestContext(), buildMcpDiscoverResult(), buildMcpCompleteResult(), PmMcpSubscriptionRegistry, buildMcpHttpRequestHeaders(), validateMcpHttpRequestHeaders(), buildMcpProtectedResourceMetadata(), and the issuer/trace authorization helpers from @unbrained/pm-cli/sdk. See MCP interaction and task SDK and remote transport, authorization, and migration, plus MCP Skills and Apps.


Mcp_2026_07_28 remote
Report an issue