MCP Learning Path: Choose by Role and by Spec Revision

MCP Learning Path: Choose by Role and by Spec Revision

The shortest route through MCP is not a fixed list of chapters. It depends on two things you can decide in a minute: which role you play, and which protocol era your peers speak. The two decisions below become tables, and every stage ends with a check that you can run and that either passes or does not.

Read in this order: pick your role (section 1), pick the protocol era you build for (section 2), migrate if you already have something on an older revision (section 3), follow the stages (section 4), and come back to section 5 when something does not work.

1. Pick your role

RoleYou want toRead, in this orderRun firstYou are done when
Evaluating MCPDecide whether MCP fits and what it coversIntroduction, Architecture, Server conceptsNothing yetYou can say which of tools, resources and prompts each of your use cases needs (prompts, section 1)
Server developerBuild a server other people’s clients can callBuild a server, Tools, Resources, Prompts, Best practicesThe SDK example from “Build a server”, then lint_tools.py against itlint_tools.py (and the resources and prompts linters if you offer them) exits with code 0
Client or host developerConnect your application to servers you did not writeBuild a client, Client best practices, VersioningA request with the protocol version in _meta, or server/discover firstAn UnsupportedProtocolVersionError makes your client retry with a version from supported, and a legacy server is reported, not hung on
IntegratorWire existing servers into a host such as an IDE or chat applicationConnect to local servers, Connect to remote servers, Inspector guideinspector_smoke.py against the server, before the host sees itThe Inspector lists the tools you expect, and the smoke test exits with code 0
Security or platform ownerPut servers behind authentication, a gateway or a firewallAuthorization, Security best practices, Best practicesprobe_http.py against a staging endpointprobe_http.py exits with code 0, and you know why each WARN is acceptable
PublisherList a server so that others can find and install itPublishing, Consumingpreflight.py on your server.jsonpreflight.py exits with code 0 before you run mcp-publisher publish

2. Pick the protocol era

MCP has had five revisions. The table lists what each one added that shows up in your code; the last row is where you should start today.

RevisionWhat it added that you will noticeOfficial changelog
2024-11-05The first revision documented here; the next changelog is written relative to it2025-03-26 changelog
2025-03-26An OAuth 2.1 authorization framework, Streamable HTTP replacing HTTP+SSE, JSON-RPC batching, tool annotations, audio content, the completions capabilityKey changes
2025-06-18Batching removed again, structured tool output, servers as OAuth resource servers with protected resource metadata, Resource Indicators (RFC 8707), elicitation, resource links in tool results, the MCP-Protocol-Version header, title fieldsKey changes
2025-11-25OpenID Connect Discovery, icons, incremental scope consent, URL mode elicitation, tool calling in sampling, client ID metadata documents, experimental tasks, JSON Schema 2020-12 as the default dialectKey changes
2026-07-28No sessions and no initialize handshake: every request carries its version and capabilities. server/discover, subscriptions/listen, multi round-trip requests, a required resultType, caching hints, the Mcp-Method and Mcp-Name headersKey changes

The 2026-07-28 revision calls versions up to 2025-11-25 legacy (they open a session with an initialize handshake) and 2026-07-28 and later modern (version, identity and capabilities travel in each request). An implementation that speaks both is dual-era. What happens when two of them meet is defined by the specification’s compatibility matrix:

ClientServerOutcomeWhat to do
ModernModernWorksserver/discover is optional. On UnsupportedProtocolVersionError, retry with a version from supported
ModernLegacyFailsOn stdio send server/discover first, so that the failure is deterministic, and show the user an actionable error
Dual-eraModernWorksThe client stays modern
Dual-eraLegacyWorksThe probe fails with a non-modern error (or times out) and the client falls back to initialize
LegacyModernFailsA modern-only server SHOULD name the versions it supports in its error to initialize: that message may be all a legacy client can show
LegacyDual-eraWorksThe server answers initialize and serves the client by the negotiated legacy revision

Legacy against legacy works by the legacy revision and is out of scope of the 2026-07-28 text. Which era to build follows from the table:

  • A new server whose clients you control: modern only. It is the smaller implementation, and the clients are yours to upgrade.
  • A server with clients you do not control, such as hosts that still send initialize: dual-era. A server MAY implement both behaviors and serve both eras on one endpoint.
  • A client for servers you do not control: dual-era, because a modern client against a legacy server fails. On stdio, probe with server/discover and fall back on any error that is not a recognized modern error; on Streamable HTTP, attempt a modern request and read the body of a 400 before falling back. Cache the answer per server process or origin.
  • A client for servers you control: modern only.

3. Moving from 2025-11-25 to 2026-07-28

Each row is one change from the official 2026-07-28 changelog, with what you do about it and where the site covers it.

What changedWhat to doRead
Protocol-level sessions and the Mcp-Session-Id header are removed; list endpoints no longer vary per connectionStop keying state or lists on a session; keep state in your own storage or carry it in requestStateBest practices, section 2
The initialize handshake is removed; every request carries its protocol version and client capabilities in _metaSend the _meta fields on every request; stop waiting for an initialization resultVersioning
server/discover is added, and servers MUST implement itImplement it; call it first only when you want the supported versions up frontDiscover
subscriptions/listen replaces the GET endpoint and resources/subscribeOpen one stream with the notification types you want; list again after a changeResources, section 4
Multi round-trip requests replace server-initiated requests such as roots/list, sampling/createMessage and elicitation/createAnswer an input_required result and retry with inputResponses and requestStateMulti round-trip requests
Every result carries a required resultTypeSend "complete" for ordinary results; read it before anything else on the clientPrompts, section 5
ttlMs and cacheScope are required on the list results and on resources/readSend ttlMs: 0 and "private" if you do not know better; use "public" only for lists identical for all usersTools, Prompts, section 4
Mcp-Method and Mcp-Name are required on Streamable HTTP POST requestsSend both; Base64-encode a non-ASCII Mcp-Name; make your gateway forward themPrompts, section 6
The error code for a missing resource changes from -32002 to -32602Return -32602; clients should still accept -32002 from older serversResources, section 7
ping, logging/setLevel and notifications/roots/list_changed are removedSet the log level per request in _meta; stop sending the removed messagesChangelog
Experimental tasks move out of the core protocol into an official extensionTreat tasks as an extension you negotiate through extensionsExtensions
SSE stream resumability and the Last-Event-ID header are removedA broken response stream loses the request: the client re-issues itChangelog

4. The path in stages

Each stage ends with something you can show. The scripts named below are the ones embedded in the pages they come from. Each exits with code 0 when no check FAILs and with code 1 when at least one does.

StageDoCheckpointPage
1. UnderstandRead the architecture and the server and client concepts. Learn who controls each primitive: the user (prompts), the application (resources), the model (tools)You can name the controller of each primitive in your designArchitecture, Prompts, section 1
2. Build and runBuild a server with an official SDK, following the guide for your languageThe Inspector connects, negotiates a version and lists your tools: inspector_smoke.py exits with code 0Build a server, Inspector guide
3. PrimitivesDefine tools, resources and prompts the way their pages describe, including the failure caseslint_tools.py, lint_resources.py and lint_prompts.py each exit with code 0 on your endpointTools, Resources, Prompts
4. Remote and secureServe over Streamable HTTP, add authorization, check Origin, token audience and the headersprobe_http.py exits with code 0 against stagingBest practices, Authorization
5. ShipDescribe the server in server.json, prove ownership, publishpreflight.py exits with code 0Publishing
6. Stay compatibleDecide the era (section 2), test the rows of the compatibility table that apply to youEach applicable row ends as the table says, not as a hangVersioning

5. When something does not work

SymptomMost likely causeGo to
A 2026-07-28 client rejects your list resultThe result lacks resultType, ttlMs or cacheScope (the 2025-11-25 shape)The list-envelope check of the three linters; Prompts, section 9
HTTP 400 with -32020 on every requestMcp-Method or Mcp-Name is missing, differs from the body, or a non-ASCII value was not Base64-encodedPrompts, section 6
-32022 Unsupported protocol versionThe server does not implement the version you sentRetry with a version from data.supported; Versioning
A legacy client cannot connect to your new serverA modern-only server rejects initializeThe Legacy and Modern row of section 2; build dual-era or upgrade the client
resources/subscribe answers 404 with -32601The method no longer exists in 2026-07-28Use subscriptions/listen; Resources, section 4
A missing resource answers -32002The server is on 2025-11-25 or earlierAccept both codes in the client; Resources, section 7
The model keeps calling the wrong tool, or sends invalid argumentsThe definition does not let the model recoverTools, Best practices, section 4
The server works locally and fails behind a gatewayA gateway drops or rewrites Mcp-Method, Mcp-Name or MCP-Protocol-Versionprobe_http.py; Best practices, section 5

Versions and Verification

Everything above is about the 2026-07-28 revision and the four before it; the facts come from the official changelogs and the versioning page of the specification, pinned to one commit.

Sources