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/clientCapabilitiesas an object, including{};- optional
io.modelcontextprotocol/clientInfowithnameandversion.
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/discoverwith 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
initializewith any accepted legacy revision to supply client identity. - Streamable HTTP requires
MCP-Protocol-VersionandMcp-Methodon every request.Mcp-Nameis required only forprompts/get,resources/read, andtools/call; other methods omit it. Schema-declaredx-mcp-headervalues are encoded, decoded, and compared with tool arguments before dispatch. Header/version mismatch uses code-32020and HTTP 400. subscriptions/listenis a request-scoped stdio or SSE stream. The first message acknowledges the supported filter; later messages carry its request id asio.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
-32022plus the exact supported modern list. Missing required capabilities return-32021with 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.