MCP C# SDK Protocol Negotiation: Pin 2026-07-28 When Fallback Is Unsafe The Model Context Protocol C# SDK 2.0.0 can silently downgrade the protocol version during negotiation, potentially changing the wire contract while still establishing a successful connection. Developers are advised to pin the protocol version to 2026-07-28 when fallback is unsafe, and to inspect the negotiated version and session ID to ensure the expected behavior. MCP C SDK protocol negotiation can quietly change the wire contract beneath an otherwise successful connection. The stable MCP C SDK 2.0.0 release https://github.com/modelcontextprotocol/csharp-sdk/releases/tag/v2.0.0 prefers the 2026-07-28 protocol, but it also keeps older servers working through automatic fallback. That compatibility is useful. It can also hide the fact that a client expecting sessionless behavior actually negotiated an initialize-era session. I treat the negotiated version as part of the application contract. If a feature or deployment assumption requires 2026-07-28 , I pin it and test the failure path. The 2026-07-28 specification https://modelcontextprotocol.io/specification/2026-07-28/changelog removes the initialize handshake and protocol-level HTTP sessions. Clients can call server/discover , and each request carries its protocol version and client capabilities. SDK 2.0 handles the transition for us. A default client first tries the modern path. If it reaches a server that requires stateful HTTP, the server refuses the modern revision and the client can negotiate an older, initialize-capable version instead. The distinction between compatibility and failure matters. The SDK recognizes negotiation responses and does not treat every outage as permission to downgrade. Network failures must still surface, while modern protocol errors carry typed information that can guide selection or rejection. Application retry code should preserve that distinction rather than catch every connection exception and blindly start a legacy flow. That is a successful connection, but it is not the same contract: | Server and client | Result | |---|---| | Stateless server, default client | 2026-07-28 , no session ID | | Stateful server, default client | Down-level version, session ID created | | Stateless server, pinned client | 2026-07-28 , no session ID | | Stateful server, pinned client | Connection fails instead of downgrading | The second row is where an upgrade can become misleading. Health checks stay green, yet code that assumes stateless requests, modern-only extensions, or no session affinity is now running under different rules. For example, the SDK's v2 Tasks extension requires the modern revision; a down-level connection cannot quietly provide an equivalent task wire contract. Leaving ProtocolVersion unset means compatibility mode. Setting it makes that revision the minimum the client accepts. static Task