AGENT INTELLIGENCE
Guide

MCP deleted sessions and finalized Skills. Here is what the new spec demands of builders.

The 2026-07-28 revision is stateless: no initialize handshake, no Mcp-Session-Id, no server-initiated requests. Skills ships as a Final extension, and a formal 12-month deprecation clock now governs every removal. What to change, who can wait, and the exact removal dates.

If you learned MCP from a tutorial written before August 2026, most of what it taught about transport is now wrong. The current revision, 2026-07-28, published final on July 28, 2026 (release candidate locked May 21), removes the protocol-level session, the initialize handshake, and every server-initiated JSON-RPC request. It replaces them with self-describing requests any server instance can handle. Separately, the Skills extension reached Final status on September 13, 2026, and a formal feature lifecycle now gives every deprecation a minimum 12-month window before removal.

This guide is documentation-based. Nothing here was live-tested; every claim traces to the sources above, and the evidence folder records what was verified against the official spec versus what comes from independent analysis.

What the revision actually changed

The official changelog lists nine major changes. The ones that break existing code:

Sessions and the session ID are gone from Streamable HTTP (SEP-2567). A modern server does not mint session IDs, does not echo them, and tools/list, resources/list, and prompts/list no longer vary per connection. If your server returned a different tool set depending on session state, that behavior has no home in the modern protocol: the tool set is a property of the server, not of a conversation.

No more initialize handshake (SEP-2575). Every request carries its protocol version and client capabilities in _meta (io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientCapabilities). Clients SHOULD identify themselves per request (clientInfo); servers SHOULD identify themselves in each result's _meta (serverInfo). A version the server does not support gets an UnsupportedProtocolVersionError listing the versions it does, and the client retries with one of them.

server/discover is now mandatory for servers. Clients MAY call it up front to pick a version, or use it as a backward-compatibility probe on stdio. The response is cacheable.

Server-initiated requests are gone; Multi Round-Trip Requests (MRTR) replace them (SEP-2322). Servers no longer send their own JSON-RPC requests to clients. When a server needs something mid-call (user input via elicitation, an LLM completion via sampling, the roots list), it returns an interim result with resultType: "input_required" carrying inputRequests and an opaque requestState blob. The client gathers the input and retries the original request with a new ID, adding inputResponses and echoing requestState. Every result now carries a required resultType ("complete" or "input_required"); clients MUST treat results from older servers that omit the field as "complete".

The GET stream, SSE resumability, and several utilities are removed. The standalone SSE stream is replaced by subscriptions/listen, a single long-lived POST-response stream a client opts into for change notifications (toolsListChanged, promptsListChanged, resourcesListChanged, resourceSubscriptions). Last-Event-ID and event IDs are gone: a broken response stream loses the in-flight request, and the client MUST re-issue it as a new request with a new ID. ping, logging/setLevel, and notifications/roots/list_changed are also removed; log level is now requested per call via io.modelcontextprotocol/logLevel in _meta.

The transport mirrors request fields into HTTP headers (SEP-2243). Mcp-Method is required on every POST; Mcp-Name is required on tools/call, resources/read, and prompts/get. A tool parameter can be mirrored into an Mcp-Param-{Name} header via x-mcp-header in its input schema, so gateways can route without parsing bodies. Servers that read the body MUST validate that headers and body agree and reject mismatches with 400 and a HeaderMismatch error (-32020). Note the renumbered error codes: HeaderMismatch is now -32020, MissingRequiredClientCapability is -32021, UnsupportedProtocolVersion is -32022, and resource-not-found moved from -32002 to the JSON-RPC standard -32602.

List results are cacheable (SEP-2549). tools/list, prompts/list, resources/list, resources/read, and resources/templates/list results now carry required ttlMs (freshness hint in milliseconds) and cacheScope ("public" or "private"). Servers SHOULD also return tools/list in a deterministic order, since a stable tool list is what lets client-side and model-provider prompt caches hit.

Tasks moved out of the core into an official extension (SEP-2663). The redesigned io.modelcontextprotocol/tasks extension replaces blocking tasks/result with polling via tasks/get, adds tasks/update for client-to-server input, removes tasks/list, and lets servers return task handles unsolicited. Anyone who shipped against the experimental 2025-11-25 Tasks API needs to migrate to the new lifecycle.

Authorization hardened. OAuth clients now MUST validate the iss parameter on authorization responses (RFC 9207); client credentials are bound to the issuing authorization server; scope step-up is formalized. OAuth 2.0 Dynamic Client Registration (RFC 7591) is deprecated in favor of Client ID Metadata Documents.

Who is affected: the three eras

The spec's versioning page defines the terminology, and its compatibility matrix defines the outcomes:

  • Legacy means 2025-11-25 and earlier: session-based, with the initialize handshake and server-initiated requests.
  • Modern means 2026-07-28 and later: per-request _meta, no sessions.
  • Dual-era means an implementation that supports both.

Modern client against legacy server: fails. Legacy client against modern server: fails, and legacy clients have no fall-forward mechanism, so the spec asks modern-only servers to name their supported versions in any error returned to an initialize request. Dual-era against either: works. A dual-era server chooses behavior from how the client opens: a request carrying modern _meta is served statelessly; an initialize request selects legacy semantics. Both can run concurrently on the same endpoint.

The practical consequence: only the server can bridge the eras. If you have clients you do not control and cannot upgrade, your server must be dual-era for a bounded period with a written end date.

Migration checklist

  • Delete session plumbing: stop minting and echoing Mcp-Session-Id; a GET or DELETE to the MCP endpoint from an older client should get 405 Method Not Allowed (the modern behavior the independent migration analysis documents; the spec's transport pages define the stateless model).
  • Attach _meta (protocol version, client capabilities) to every request; keep the MCP-Protocol-Version HTTP header matching the body.
  • Implement server/discover on every server. It is mandatory.
  • Move cross-call state out of the transport and into tool arguments: mint an explicit, server-issued handle from one tool and have the model pass it back as an ordinary argument. Back the handle with shared, durable storage (database, job queue, object store) so any instance can serve any call. Give handles an expiry, and answer stale handles with a model-readable tool error ("that export has expired, start a new one"), not a transport error.
  • Replace server-initiated elicitation and sampling with MRTR. Protect requestState (encrypt or sign it), since it round-trips through the client. Test the retry path with a different server instance handling the second request.
  • Replace the GET stream with subscriptions/listen. Note the one deployment caveat the migration analysis flags: subscriptions/listen is still a long-lived SSE stream, so delivering notifications to a client whose stream is held by a different instance requires shared pub/sub. Request handling is stateless; notification fan-out is a deployment concern you still have to design.
  • Make tools/list deterministic; add ttlMs and cacheScope; announce changes to subscribers.
  • Validate Origin; pass SSE through proxies unbuffered and forward Mcp-* headers untouched.
  • Do not adopt Roots, Sampling, Logging, or Dynamic Client Registration in new code. Existing uses get a migration ticket dated before July 2027.

The deprecation clock: plan against real dates

This is the revision's most planning-friendly addition. The feature lifecycle policy (SEP-2596, adopted in this revision) defines Active, Deprecated, and Removed states with a minimum 12-month deprecation window measured from the release of the revision in which the feature is first marked Deprecated. The earliest removal is the first revision released as Current on or after the window elapses. Removal itself is a Core Maintainer decision taken during release preparation and may happen later. The registry, not the calendar, is the source of truth. One guardrail: a 12-month floor can only be shortened to a minimum of 90 days when there is an active security risk with no in-place mitigation.

As of this writing the registry lists:

FeatureDeprecated inEarliest removal
Roots2026-07-28First revision released on or after 2027-07-28
Sampling2026-07-28First revision released on or after 2027-07-28
Logging2026-07-28First revision released on or after 2027-07-28
Dynamic Client Registration2026-07-28First revision released on or after 2027-07-28
includeContext: "thisServer" / "allServers"2025-11-25Follows Sampling
HTTP+SSE transport2025-03-26Three months after SEP-2596 reaches Final

Nothing has been removed under the policy yet. Deprecated features keep working during the window, but new implementations should not adopt them. Elicitation, notably, is not deprecated: it survives in the modern revision through the MRTR pattern.

Also worth knowing: the policy obliges Tier 1 SDKs to mark deprecated APIs in their next release (native mechanism per language) and SHOULD emit runtime warnings. The Python SDK already does this via MCPDeprecationWarning.

Skills: the third capability layer

On September 13, 2026, SEP-2640 was marked Final with its PR merged, and the Skills extension shipped in the modelcontextprotocol/ext-skills repository with its stable specification written against base protocol revision 2026-07-28. The extension identifier is io.modelcontextprotocol/skills.

The idea: tools tell an agent what actions it can take, resources tell it what data it can access, and skills tell it the workflow for combining them. The extension adds skills/list and skills/get methods over the existing Resources primitive, plus an optional resources/directory/read. A skill entry carries a URI (conventionally skill://<skill-path>/<file-path>), its full YAML frontmatter (name and description always required), and a manifest of every file with SHA-256 digests and byte sizes. Reading the actual file content still uses resources/read.

Three details matter for server authors:

First, digest validation is an integrity check, not a security boundary. The spec's ground rules, as summarized by the independent analysis: every file read is validated against the entry's digest and size, and on mismatch the request is rejected and the client refreshes the entry and re-runs approval checks. Digests are not signed, so they confirm content matches the entry, nothing more. All MCP-sourced skills injected into model context must be marked with their originating server, and cross-origin resource reads from skills are prohibited.

Second, the payload format is deliberately not MCP's. The extension is a transport binding; the skill format itself belongs to agentskills.io. There is one source of truth for what a skill is, and MCP is one way to deliver it. Servers declare support by advertising io.modelcontextprotocol/skills in the extensions map of their capabilities, which automatically means they implement both methods.

Third, a single skill is capped at 512 files or 16 MiB, whichever comes first.

The SDKs lag the spec in places

The TypeScript SDK 2.0.0 is now a nine-package family (server, client, node, express, hono, fastify, core, plus server-legacy and codemod). server-legacy is a frozen copy of v1 server code for migration purposes only and is planned for removal in v3. The Python SDK's 2.x line is the current stable (2.2.0): it speaks 2026-07-28 and negotiates back to every earlier revision, while 1.x lives on a maintenance branch (1.30.0) with critical fixes only. Since pip install mcp now installs 2.x, the SDK's own docs advise pinning mcp>=1.28,<2 until you have migrated.

Where the SDKs are behind the spec, confirmed from their own release notes and roadmap: the Python 2.2.0 release lists "Known gaps" of the Tasks extension (SEP-2663), DPoP-bound access tokens (SEP-1932), and the workload-identity jwt-bearer grant, all not yet implemented. The TypeScript SDK's v2 beta notes aimed to ship Tasks support "with the stable release or soon after." Check your SDK's release notes before designing around Tasks, DPoP, or jwt-bearer.

What remains unknown

When the next spec revision will ship, and whether Roots, Sampling, and Logging are removed at the first revision eligible after July 28, 2027 or linger Deprecated for years. The registry decides; there is no announced schedule.

How fast the client ecosystem moves to the modern era. Dual-era support is a cost you pay per server until your clients upgrade, and legacy clients cannot fall forward, so the migration end date is set by the slowest client you serve, not by the spec. This research did not survey client support beyond the SDKs; the spec site maintains a client matrix for extensions, which is the place to watch for Skills.

Whether SDK conformance keeps pace with spec ambition. The release-candidate post ties Standards Track SEPs to the conformance suite and an SDK tier system, with relegation for Tier 1 SDKs that fail to surface deprecations. That machinery exists on paper; how strictly it is applied is the interesting question for the next revision cycle.

Next in the file

AI agents breached 27 companies and stole 600,000 cards. The playbook is open source.

Gambit Security reconstructed a near-autonomous attack campaign from the operator's own staging server: three open-source AI tools, brief instructions in Chinese from a human, and costs averaging $25.46 per target.