MCP C# Per-Request Client Capabilities: Read the Request, Not the Server A developer highlights a C# pitfall in the MCP 2026-07-28 protocol, where per-request client capabilities replace the old initialization handshake. The developer explains that servers must read capabilities from the current JSON-RPC request's _meta field, not from server properties or cached values, and demonstrates using the ModelContextProtocol.AspNetCore 2.2.0 package to access them via RequestContext. With MCP C per-request client capabilities , the 2026-07-28 protocol changes where a server must read what the caller supports. Modern stateless requests carry their own clientCapabilities inside meta ; there is no initialization handshake whose values can be retained for the connection. That creates a small but important C trap. Inside a stateless HTTP handler, request.Server.ClientCapabilities is intentionally null . The authoritative value lives on the current JSON-RPC request. If I cache a previous value, or treat the server property as the modern source, I can make the wrong decision for the next call. The MCP 2026-07-28 release https://blog.modelcontextprotocol.io/posts/2026-07-28/ removed protocol-level sessions and the initialize exchange from the modern path. Every request is self-contained, which lets separate server instances handle calls without sticky routing or a shared MCP session store. The request now carries reserved metadata such as: { " meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientInfo": { "name": "report-client", "version": "1.0.0" }, "io.modelcontextprotocol/clientCapabilities": { "extensions": { "com.example/report-export": {} } } } } The key word is request . A server must not infer capabilities from an earlier message. Two calls reaching the same process may legitimately declare different extension support, and concurrent handlers must stay isolated. The older handshake model encouraged a connection-level mental model: negotiate once, then consult the negotiated server object later. That assumption does not survive a sessionless request that can land on any instance. There is no durable "current client" whose feature set safely belongs in a singleton, static field, or process-wide cache. Even when one client normally sends the same declaration each time, the protocol boundary still has to treat the envelope it received as the source for that call. This also matters during staged migrations. A load test, proxy, or compatibility client can mix modern requests with different extension maps in one server process. Code that appears correct with one client may fail only under overlap, which is why I prefer a concurrency regression instead of a single serialized example. I used the stable ModelContextProtocol.AspNetCore 2.2.0 package https://www.nuget.org/packages/ModelContextProtocol.AspNetCore/2.2.0 for the sample. This is released functionality, not a preview API. The C SDK exposes the parsed value through JsonRpcMessageContext.ClientCapabilities . A tool can reach it from its injected RequestContext