How to Build a Read-Only MCP Server When an API Lacks a Read-Only OAuth Scope
A Model Context Protocol (MCP) server lets an AI client call named tools, which are functions that connect to external services. If…
The Model Context Protocol (MCP) lets a client exchange structured requests with a server. A protocol version is a dated set of rules for those messages. It is separate from the server software’s own release number, so a package version or a serverInfo value does not establish which MCP protocol versions the server supports.
MCP negotiates protocol versions in two ways. In revisions through 2025-11-25, the client and server choose a version when they connect. The 2026-07-28 specification describes a newer method: each request names its version, and the client can ask which versions the server supports. Use server/discover to check a server that uses the newer method. For an older server, use initialize. Its response gives the version chosen for that connection, but may not list every version the server supports.
MCP names each protocol version with a date in YYYY-MM-DD format. A new date marks a revision with changes that break compatibility with earlier versions. Improvements that remain compatible do not necessarily get a new date. The versioning guide identifies 2026-07-28 as the current protocol version. Check the official release record to confirm whether that revision is final.
For revisions from 2024-10-07 through 2025-11-25, the client begins with an initialize request. It proposes a protocol version and sends information about the client and the features it supports. The server replies with the version the connection will use, plus information about the server and the features it supports. The initialization schema says a client that cannot use the returned version must disconnect.
The response tells you which version the client and server chose for this connection. It may not list all versions the server supports. To find out more, check the server’s documentation for each version or test other versions and record the results. Each test is a probe.
With Streamable HTTP, MCP’s method for sending messages over HTTP, the client includes an MCP-Protocol-Version header in later requests. The header names the version chosen during initialization. The transport specification says the server returns 400 Bad Request if the header value is invalid or unsupported.
In the 2026-07-28 specification, each request names its protocol version. The request puts the version in a metadata object called _meta, under io.modelcontextprotocol/protocolVersion. With Streamable HTTP, the client also puts that version in the MCP-Protocol-Version header. A server that supports several versions checks each request’s declared version and accepts or rejects the request accordingly. The versioning specification describes this model.
The same specification defines UnsupportedProtocolVersionError for a request that names a version the server does not support. The example uses JSON-RPC error code -32022. JSON-RPC is the format MCP uses for requests and responses. The error data gives the attempted version in requested and the versions the server supports in supported. The client can use that list to choose a version both sides support and try again.
To check a server that uses the newer model, send a server/discover request with the modern protocol version in _meta. For Streamable HTTP, put the same version in the MCP-Protocol-Version header. If the request succeeds, read result.supportedVersions. This field lists the protocol versions the server says it supports. The discovery specification requires servers to implement this request. Clients can skip it and send another request instead.
A discovery response also includes capabilities, a description of the server’s features. These features do not add versions to supportedVersions. The response may also name the server, but the specification treats that identity as self-reported and unverified. Do not rely on it for security decisions.
If the server instead returns a recognized UnsupportedProtocolVersionError, check error.data.supported. Although the server rejected your requested version, the list gives the versions it supports. Choose a version that both the client and server support, then try again.
For an older server, send initialize with a version the client supports, then read the server’s protocolVersion response. That value names the version chosen for the connection. It does not give a complete list of versions the server supports.
A failed request does not tell you which protocol model the server uses. A timeout, an HTTP status code, or an unfamiliar error does not show which versions the server supports. The newer compatibility guidance gives different checks for each transport.
For a server launched over stdio, first send server/discover. Use the older initialize handshake only if the response is not a recognized error from the newer model. For Streamable HTTP, send a request using the newer model. If the server returns 400 Bad Request, check the response body before trying the older handshake. A recognized JSON-RPC error from the newer model can show that the server uses that model but rejected the request, such as when the requested version is unsupported.
When a client sends a request using the newer model to a legacy server, the result can be unclear. The server might return an implementation-defined error or no response. Silence or an unfamiliar error alone does not show that the server is legacy. The compatibility guidance recommends keeping the detected protocol model for as long as a stdio server process runs or an HTTP origin is in use. Probe again if that assumption later fails.
An SDK’s defaults also affect how it checks a server. For example, the TypeScript SDK’s version guide says its default path uses older initialization without first probing the server. This behavior applies to that SDK and version; it is not a general MCP protocol rule.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com