
The TL;DR
The 2026-07-28 MCP specification is still a release candidate, but the beta SDKs already include changes that server authors should test before the final version is locked.
-
• The Error Code Change Fails Silently
Missing-resource errors move from the custom
-32002code to the standard JSON-RPC-32602code. Handlers that still match the old value may stop firing without producing an obvious failure. -
• Task Creation Moves to the Server
The redesigned Tasks extension removes
tasks/list, and the server now decides when a request becomes a task. Implementations built on the earlier experimental API will need new task tracking and bookkeeping rather than a small patch. -
• The Beta SDKs Are Already Ahead
The Python, TypeScript, Go, and C# beta SDKs released on June 29 already reflect newer request shapes and behaviors. Testing against them now can reveal migration problems that may not be obvious from reading the release candidate alone.
Passing every test against the current beta SDKs doesn’t mean your error handling still works. The literal -32002 doesn’t throw when it stops matching the errors it was written to catch. It falls through to a generic handler instead, and the first sign of trouble is a support ticket asking why missing-resource errors suddenly look different.
MCP servers give AI agents access to outside tools and data, and for the server authors who build and run them, the real risk in this migration hides in the gap between what the 2026-07-28 spec still technically allows and what the reference SDKs no longer contain. The release candidate locked May 21, 2026, and the final spec ships July 28.
Stateless MCP Removes Protocol Sessions

Session IDs Are Gone
Until now, a client connecting to an MCP server opened with a handshake and got back a session ID, and every later call carried that ID so the server could find the conversation again. Two Specification Enhancement Proposals (SEPs) remove that entirely. SEP-2575 drops the initialize/initialized handshake. SEP-2567 drops the Mcp-Session-Id header and the protocol-level session it represented. Together, a request no longer depends on what happened in a previous one.
Anything a server kept in memory between requests loses its anchor the moment a client stops sending Mcp-Session-Id, and under 2026-07-28 it never does. A shopping cart tied to a session ID. An open file handle keyed on that header. Both need a different home.
The fix mirrors how ordinary HTTP APIs have handled this for years. A tool call returns an explicit identifier, and the caller passes it back as an ordinary argument on the next request.
// Old pattern (state hung off the protocol session, removed under SEP-2567)sessions[mcpSessionId].basket = basket;// New pattern (the tool mints an explicit handle)return { content: [{ type: "text", text: JSON.stringify({ basket_id: "b_8f3a" }) }] };// Every later call passes it back as an ordinary argument// tools/call { name: "add_item", arguments: { basket_id: "b_8f3a", sku: "..." } }
_meta Replaces Session Negotiation
protocolVersion, clientInfo, and capabilities now travel inside _meta on every request instead of being negotiated once at connection time, and a new server/discover method lets a client ask for server capabilities whenever it needs them rather than as a mandatory first step.
New HTTP Headers Are Required
Streamable HTTP requests now require an Mcp-Method header, and tool, resource, or prompt calls also require Mcp-Name (SEP-2243), so a gateway can route on the operation without opening the request body. A current SDK generates and validates both headers correctly. A WAF, CDN, or reverse proxy stripping unrecognized Mcp-* headers in front of the server is a separate problem the SDK cannot fix, and from the outside that failure looks exactly like a client bug. Walking the full request path end to end, from CDN through proxy to server, is the same fix behind most MCP connection failures that turn out to be infrastructure rather than code.
Resource Error Codes Changed in MCP v2
A single-line error handler is where this migration bites hardest, because nothing about the failure looks like a failure.
SEP-2164 moves the “resource not found” error from MCP’s custom -32002 to the standard JSON-RPC code -32602, Invalid Params.
// Before (matches the old custom code)if (error.code === -32002) { return handleMissingResource(error);}// After (matches the standard JSON-RPC code)if (error.code === -32602) { return handleMissingResource(error);}
handleMissingResource quietly stops running. The error falls through to a generic handler, and nobody notices until a support ticket asks why missing-resource errors behave differently than they used to. Searching the codebase and test fixtures for the literal string -32002 today catches it before a user does.
C# SDK users have three more codes to update in the 2.0.0-preview.1 release. MCP’s new error-code allocation policy renumbered them as part of the same change. HeaderMismatch moves from -32001 to -32020, MissingRequiredClientCapability moves from -32003 to -32021, and UnsupportedProtocolVersion moves from -32004 to -32022. Same failure mode, same fix.
Updating Long-Running Tasks for MCP v2
Long-running work in MCP, a report generation job, a batch export, used to behave like a queue a client could inspect from outside. That queue is gone. Servers built on the experimental Tasks feature from the 2025-11-25 spec need a real port, not a relocation, since Tasks moved into an official extension under SEP-2663 and the request shape changed along with the address.
tasks/list Is Gone
The old pattern polled every task in flight. The new one hands task creation to the server entirely. A client advertises support for the Tasks extension in its capabilities, but it doesn’t ask for a task on any given call. The server decides, per request, whether a tool call is worth turning into one, and only then hands back a task handle instead of an immediate result.
// Before (2025-11-25, experimental Tasks){ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "generate_report", "arguments": { "range": "q2" } } }{ "jsonrpc": "2.0", "id": 2, "method": "tasks/list" }// no longer exists in the v2 Tasks extension// After (SEP-2663, Tasks extension){ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "generate_report", "arguments": { "range": "q2" }, "task": { "ttl": 600000 } } }{ "jsonrpc": "2.0", "id": 1, "result": { "task": { "taskId": "b3f1c9a2", "status": "working", "pollInterval": 5000 } } }// A client polls the one task it holds a handle for{ "jsonrpc": "2.0", "id": 2, "method": "tasks/get", "params": { "taskId": "b3f1c9a2" } }
tasks/list has no replacement because it has no safe scope left. A server can’t reliably enumerate every in-flight task across every caller once sessions are gone, since there’s no session to scope the list to and no caller identity a server can trust without extra state it would have to build itself. A dashboard that used to list every in-flight task now needs that bookkeeping at the application layer.
tasks/get Replaces tasks/result
Two other methods changed in the same move. The old blocking read, tasks/result, is replaced by tasks/get. A new tasks/update method handles cases where a task needs input mid-run, and tasks/cancel signals cancellation intent, though cancellation is cooperative and a task may still finish before the server acts on it.
Protect Your Server from Task Flooding
Task creation is cheap for the caller and resource-heavy for the server, since a client can attach a task object to many calls without waiting on any of them. Rate-limiting task creation the same way any other resource-intensive endpoint gets rate-limited closes that gap early.
Deprecated MCP Features to Replace

SEP-2577 deprecates three original MCP primitives, Roots, Sampling, and Logging, each with a documented replacement and a twelve-month grace period under the spec’s new lifecycle policy. None of them need an urgent rewrite, but new code should reach for the replacement rather than the deprecated call.
Replacing Sampling
Sampling asked the connected client to run inference mid-call, an approach that assumed a persistent connection the stateless model no longer guarantees.
# Old pattern: server asks the client's model to generate textresponse = await ctx.session.create_message( messages=[{"role": "user", "content": prompt}], max_tokens=500,)# New pattern: server calls its own LLM provider directlyresponse = await anthropic_client.messages.create( model="claude-sonnet-5", max_tokens=500, messages=[{"role": "user", "content": prompt}],)
Calling a provider directly trades the client’s model choice and API key for a connection that doesn’t need to stay open. Sampling can stay client-side through the Multi Round-Trip Requests pattern instead, a handler returns an InputRequiredResult, and the client retries with the completion attached. Only the old push-style sampling/createMessage call, made outside that pattern, actually breaks.
Replacing Roots
Context that used to come from a roots/list call now belongs in a tool parameter, a resource URI, or startup config, with the same request-and-retry pattern available for anything a server genuinely needs at runtime.
Replacing Logging
Logging moves to standard observability tooling. STDIO servers write to stderr. Everything else routes through OpenTelemetry, and _meta now carries standard W3C Trace Context fields, traceparent, tracestate, and baggage, for tracing a request across services.
Grepping the codebase for roots/list, sampling/createMessage, and logging/message before touching anything else on this list turns up the real inventory fast.
Installing and Pinning the New SDK Betas
All four Tier 1 SDKs shipped betas on June 29, 2026. Treating them as a branch to test against, not a production upgrade, protects against the public API shifting before the stable release ships.
Python
uv add "mcp[cli]==2.0.0b1"# or: pip install "mcp[cli]==2.0.0b1"
TypeScript
npm install @modelcontextprotocol/server@betanpm install @modelcontextprotocol/client@beta
Go.
go get github.com/modelcontextprotocol/[email protected]
C#
dotnet add package ModelContextProtocol --prerelease
SDK Changes to Watch Before Production
Each SDK carries its own surprises beyond the install command, worth knowing before any of this touches production traffic.
- Python:
FastMCPis renamedMCPServer, though the decorator API carries over unchanged. Libraries that depend onmcpshould add an upper bound now, something likemcp>=1.27,<2, so the stable v2 release doesn’t surprise downstream users. - TypeScript: Version 2 splits the single
@modelcontextprotocol/sdkpackage into@modelcontextprotocol/serverand@modelcontextprotocol/client, plus thin adapters for Node.js, Express, Hono, and Fastify. A codemod handles most of the mechanical rewrite vianpx @modelcontextprotocol/codemod@beta v1-to-v2 .For HTTP deployments,createMcpHandlerfrom@modelcontextprotocol/serverserves 2026-07-28 per request while still answering 2025-11-25 traffic from the same endpoint. Wiring MCP directly into Node’shttpmodule rather than through Express, Hono, or Fastify calls forNodeStreamableHTTPServerTransport, shipped in@modelcontextprotocol/node. The framework adapters moved fast during the beta, so confirming the exact wiring against the SDK’s migration docs beats guessing from an older example. - Go and C#: Neither gets a package split or a rework of the day-to-day API, but statelessness is an explicit switch rather than a default. Setting
StreamableHTTPOptions.Stateless = trueserves 2026-07-28 over HTTP in Go, while leaving it unset negotiates clients down to 2025-11-25. C#’s2.0.0-preview.1defaultsHttpServerTransportOptions.Statelessto true and marks the deprecated Roots, Sampling, and Logging APIs[Obsolete]. - Compatibility: None of this forces a sprint. TypeScript’s v1.x branch keeps shipping security updates for at least six months after v2 ships, and Python’s v1.x branch continues to receive critical fixes on its own schedule, so testing the betas in a branch while production stays on stable is a fully supported position.
- Schema and caching: Tool schemas can now use full JSON Schema 2020-12,
oneOf,anyOf,allOf, and$ref, and list and read results carryttlMsandcacheScopefields so clients such as Claude and Cursor can cache them safely instead of re-fetching on every connection.
Common Mistakes That Undo the Fix
A few patterns show up repeatedly once teams start migrating, and each one reintroduces a problem the redesign was built to remove.
- Treating the New Headers as optional
Mcp-MethodandMcp-Nameare required on Streamable HTTP requests. A validating gateway rejects requests missing either one, so an older client SDK simply stops connecting. - Trusting requestState: The Multi Round-Trip Requests pattern hands the client an opaque
requestStatestring to echo back on the next call. It comes back as attacker-controlled input. Anything it carries that affects authorization needs to be signed, an HMAC codec for example, rather than passed through as plain text. - Assuming the Upgrade Forces a Hard Cutover: It doesn’t. A migrated Python server answers the legacy
initializehandshake alongsideserver/discoverfrom the same endpoint, so old clients keep connecting with no shim needed. New clients fall back automatically against old servers too. A manual version-detection layer solves a problem the SDKs already handle on their own. - Mapping Sensitive Parameters into x-mcp-header: A tool parameter annotated this way gets mirrored into an
Mcp-Param-{Name}HTTP header, so an API key or a piece of personal data mapped by mistake is visible to every load balancer, proxy, and log line along the way. Security researchers at Akamai flagged this specific risk shortly after the release candidate shipped. Mismatches between the header and body get rejected outright at request time, though the exposure risk still applies to every request that succeeds without one.
The same release tightens OAuth token validation and authorization server checks considerably, worth auditing alongside this.
Three Ways to Test Before You Ship
A server that passes its unit tests can still fail the first time it sits behind real infrastructure. Three checks catch most of what unit tests miss.
- Run It Behind a Round-Robin Load Balancer: Running behind a plain round-robin load balancer with no sticky routing reveals a hidden session dependency the test suite didn’t exercise. It’s the clearest real-world signal that tasks/list calls and session cleanup were actually removed everywhere they need to be.
mTarsier, the free, open-source MCP configuration manager we built and maintain here at MCP360, is a fast way to check the client side of the same problem. Its tsr ping and tsr clients commands report in one pass which configured clients can actually reach a given server, so a client that can’t connect rules out common connection failure patterns before the migration itself gets blamed. - Test In-Process Before You Test Over the Network: Testing in-process before testing over the network catches API-shape mistakes before a transport is even involved. Both the Python and TypeScript v2 SDKs support connecting a client directly to a server instance in memory, the same pattern as a web framework’s test client.
# Python, connect directly to the server object, no subprocess or portasync with Client(mcp) as client: result = await client.call_tool("add", {"a": 5, "b": 3})
- Run the Conformance Suite for Your Language: Running the conformance suite for a given language as it becomes available matters because a Standards Track SEP can’t reach Final status without a matching scenario in MCP’s conformance suite, the same one that scores official SDKs under SEP-2484. Checking a given SDK’s CI status directly beats assuming full coverage, since Tasks scenarios in particular are still catching up now that Tasks just moved into its own extension.
Server-Author Checklist Before July 28
| Area | Do This |
|---|---|
| Session state | Grep for Mcp-Session-Id. Replace it with an explicit handle passed back as a tool argument. |
| Header handling | Confirm your WAF, CDN, and reverse proxy pass Mcp-Method and Mcp-Name through untouched. |
| Tasks | Grep for tasks/list and blocking tasks/result calls. Replace them with tasks/get polling. |
| Task limits | Rate-limit task creation. It is cheap for callers and expensive for your server. |
| Roots | Grep for roots/list. Move context into tool parameters, resource URIs, or startup configuration. |
| Sampling | Grep for sampling/createMessage. Call your LLM provider directly, or keep sampling client-side through the MRTR pattern. |
| Logging | Grep for logging/message. Switch to stderr for STDIO or OpenTelemetry for everything else. |
| Error codes | Grep for the literal -32002. Update it to -32602. C# servers should also check -32001, -32003, and -32004. |
| Headers in transit | Confirm Mcp-Method and Mcp-Name are sent on every request, and that no sensitive value is mapped through x-mcp-header. |
| SDK pin | Pin an exact pre-release version. Never let a floating range resolve into a beta. |
| Load test | Run the server behind a plain round-robin load balancer with sticky routing disabled. |
| Client test | Confirm every supported client still connects after migration. |
Frequently Asked Questions
What is an MCP server?
An MCP server is a program that exposes tools, data, or actions to an AI model over the Model Context Protocol, letting an agent search a database, call an API, or read a file instead of only generating text. It communicates over JSON-RPC 2.0, and a single client such as Claude or Cursor can connect to several MCP servers at once through one shared interface.
What is the MCP SDK, and do I need one?
The MCP SDK is the official library, available in Python, TypeScript, Go, C#, and other languages, that handles a server’s protocol plumbing so a developer defines tools instead of writing JSON-RPC handling by hand. Teams reaching MCP tools through a gateway such as MCP360 instead of hosting servers themselves don’t need to install or pin an SDK at all, since there’s no server-side code to version.
Why does the MCP error code change from -32002 to -32602?
The 2026-07-28 spec moves the missing-resource error from MCP’s custom code -32002 to the standard JSON-RPC code -32602 under SEP-2164, aligning it with how other JSON-RPC errors are already classified. A handler written to match the old literal -32002 doesn’t throw when this changes, it simply stops catching the error, so searching a codebase and test fixtures for that string before the final spec locks catches it early.
Why was tasks/list removed from the MCP Tasks extension?
Without protocol-level sessions, a server has no reliable way to know which caller owns which task, so listing every task in flight can’t be scoped safely to one client. SEP-2663 dropped tasks/list for that reason when Tasks moved from an experimental core feature into its own extension. A client now tracks and polls the single task it holds a handle for through tasks/get instead of requesting a full list.
Is the MCP 2026-07-28 spec live yet?
No. As of this writing, 2026-07-28 is a release candidate, locked May 21, 2026, with the final specification scheduled to publish July 28, 2026. The current stable spec remains 2025-11-25, and nothing in it stops working on that date. What already changed is the reference code, the beta SDKs for Python, TypeScript, Go, and C# shipped June 29. Teams reaching MCP through MCP360 inherit most of that migration work from their provider directly.
How do I check an MCP server for hidden session dependencies?
Running the server behind a plain round-robin load balancer with sticky routing disabled is the clearest test, since anything that breaks reveals state that was silently relying on a client landing on the same instance twice. Testing in-process, without a network transport, catches API-shape mistakes even earlier. Tools such as MCP360’s mTarsier can also confirm which configured clients still connect after a migration, ruling out connection problems before assuming the server itself is at fault.
Do I need to rewrite Roots, Sampling, or Logging code before July 28?
Not urgently. SEP-2577 deprecates all three with a twelve-month grace period, so code built on them keeps working past the final spec date. New code should reach for the replacements instead. Roots becomes a tool parameter or resource URI, Sampling becomes a direct call to an LLM provider, and Logging becomes stderr for STDIO servers or OpenTelemetry for everything else, all under the spec’s formal lifecycle policy.
What happens if a tool parameter gets mapped into x-mcp-header?
A parameter mapped this way gets mirrored into an HTTP header visible to every load balancer, proxy, and log line the request passes through, so mapping an API key or personal data by mistake exposes it along the whole path. Security researchers at Akamai flagged this risk shortly after the release candidate shipped. Mismatches between the header and the request body get rejected outright, which catches an obviously wrong mapping, though a correctly formatted but sensitive value still gets exposed.
Conclusion
The stateless core isn’t a one-time cleanup. Every future MCP revision inherits the same _meta envelope, the same extensions framework, and the same twelve-month deprecation runway, so the work done against 2026-07-28 carries into every release after it.
Starting with the greps above against one server, testing it behind a plain load balancer, pinning an exact SDK version, and running the checklist before touching anything else turns a spec migration into a few afternoons of work instead of a scramble in late July.
MCP360 absorbs most of the routing and header work above for teams that reach MCP tools through its gateway rather than hosting their own servers, though confirming that coverage directly still beats assuming it. How a gateway distributes stateless requests across dozens of connected servers, and what changes for sticky sessions and load balancing at that layer, is a related migration with its own checklist.
Article by
HarsheenMCP & AI Agents | Content Writer
Harsheen is a content writer covering AI agents, automation, and no-code tools. She writes across topics from chatbots and customer experience to MCP and enterprise workflows, showing how real teams adopt AI in everyday operations.




