Update a Python MCP server for the stateless 2026-07-28 specification by removing the initialize handshake, adopting the input_required round-trip, and dropping deprecated features.
- An existing MCP server, or the runnable FlyPython MCP example
- Python 3.12 or newer
- Basic JSON-RPC 2.0 familiarity
The 2026-07-28 Model Context Protocol specification is the largest release since remote MCP: it removes the initialize handshake entirely, replaces server-initiated requests with a multi-round-trip input_required flow, and deprecates roots, sampling, and logging. MCP is now stewarded by the Agentic AI Foundation under the Linux Foundation, and the Python SDK shipped alongside the specification.
You are done migrating when your server answers tools/list and tools/call with no session state of any kind, no tool depends on the deprecated server-initiated requests, and your authorization path satisfies the new issuer rules.
What changed in 2026-07-28
- Stateless-first (SEP-2575, SEP-2567). The
initialize/initializedexchange and theMcp-Session-Idheader are retired. Every request self-describes with protocol version, client identity, and capabilities through_meta. An optionalserver/discoverRPC exists for clients that want capabilities up front. - Multi-round-trip replaces server-initiated requests (SEP-2322).
elicitation/create,sampling/createMessage, androots/listno longer hold open streams. A tool that needs client input returns a result withresultType: "input_required"plus the requests that need answers; the client retries the original call with the answers attached ininputResponses. - Per-request version and routing headers (SEP-2243).
MCP-Protocol-Versiontravels with every request (2026-07-28), and Streamable HTTP requests must includeMcp-MethodandMcp-Nameso infrastructure can route without parsing JSON bodies. - Authorization hardening. Authorization servers must return the
issparameter per RFC 9207 (SEP-2468), clients must setapplication_typeduring dynamic registration (SEP-837), and client credentials are issuer-bound — never reused across authorization servers (SEP-2352). - Response caching (SEP-2549). Responses from
tools/list,prompts/list,resources/list, andresources/readcarryttlMsandcacheScope. - Tasks restructured (SEP-2663). Tasks moved into the
io.modelcontextprotocol/tasksextension with poll-basedtasks/get, a newtasks/update, and change notifications moved to asubscriptions/listenstream. - Deprecations with a minimum twelve-month window. Roots, sampling, and logging are deprecated (SEP-2577), the legacy HTTP+SSE transport has a one-year offramp, and Dynamic Client Registration is replaced by Client ID Metadata Documents (CIMD).
Migration checklist
- Delete the handshake. Remove
initializeandnotifications/initializedhandling and everyMcp-Session-Idlookup. A request that still sendsinitializeshould fail as an unknown method, not be negotiated. - Make each request self-sufficient. Anything your server previously remembered from initialize — client capabilities, protocol version, identity — must now be read per-request from
_meta, or fetched throughserver/discoverwhen a client opts in. - Replace elicitation with the input_required round-trip. A tool that used to call
elicitation/createnow returnsresultType: "input_required"with the questions, and completes when the client retries withinputResponses. - Stop building on deprecated primitives. Roots, sampling, and logging still work during the deprecation window, but new code should not depend on them.
- Upgrade the official Python SDK. The TypeScript, Python, Go, and C# SDKs shipped with the specification; the RC-to-final window was roughly ten weeks, so older SDK versions predate the breaking changes.
- Update transports. Drop the legacy HTTP+SSE transport on its one-year offramp and emit the required
Mcp-MethodandMcp-Nameheaders on Streamable HTTP. - Check the authorization path. Validate
issper RFC 9207, setapplication_typeduring registration, and keep one credential per issuer. - Adopt caching metadata. Annotate list responses with
ttlMsandcacheScopeso stateless infrastructure can cache them safely.
The input_required flow in code
The runnable reference is the MCP tool server example. The essential shape:
class InputRequired(Exception):
def __init__(self, requests):
super().__init__("tool execution needs additional client input")
self.requests = requests
def delete_resource(args, input_responses=None):
answer = str((input_responses or {}).get("confirm", "")).strip().lower()
if answer != "yes":
raise InputRequired([{"id": "confirm", "prompt": "Type 'yes' to confirm deletion."}])
return "deleted"
The server catches InputRequired before its generic exception handler and returns:
{
"result": {
"resultType": "input_required",
"requests": [{"id": "confirm", "prompt": "Type 'yes' to confirm deletion."}],
"content": [{"type": "text", "text": "Additional client input is required before this tool can finish."}]
}
}
The client retries the identical tools/call with "inputResponses": {"confirm": "yes"}. Because the flow is retry-based, an unconfirmed call never mutates anything — the same property that makes idempotent automation safe.
Verify the complete behavior from the repository root, including JSON-RPC error codes and the removed handshake:
python examples/mcp-server/verify.py starter --expect-failure
python examples/mcp-server/verify.py solution
What this guide does not cover
This guide works from the published release notes and the shipped SDKs. Field-level wire schemas for _meta, the requests entries, and cacheScope values live in the specification and SDK types — consult them before shipping, and prefer the official Python SDK over hand-rolled dispatch in production. Authorization deployment details — issuer discovery, credential storage, and CIMD rollout — are also out of scope here.
Sources
- The 2026-07-28 MCP specification release
- 2026-07-28 release candidate and breaking-change overview
- Anthropic: donating MCP to the Agentic AI Foundation
- MCP under the Agentic AI Foundation
Verification record
Editorial review against the 2026-07-28 release notes; the companion example runs its 8-test verifier in the repository. Verified 2026-09-06.
About the author
Organizational byline for FlyPython guides, verification records, and corrections. Editorial standards and contact details →