What is capability negotiation, and why does it matter for backward compatibility?
Quick Answer
Capability negotiation is the part of the initialize handshake where client and server each declare, in a capabilities object, which optional protocol features they support — tools, resources (and whether they support subscribe), prompts, sampling, roots, logging, completions — often with finer sub-flags like listChanged. Neither side is required to support everything MCP defines. A server can offer only tools and nothing else; a client can decline to support sampling. Both sides are expected to check the other's declared capabilities before using a feature, rather than assuming support and failing at call time. This makes it safe to add new optional capabilities in later spec revisions: an old client or server simply never declares (and never gets asked to use) a capability it doesn't know about, without breaking.
Detailed Answer
MCP has grown new features across spec revisions — annotations, structured tool output, elicitation, audio content. Capability negotiation is the mechanism that lets that growth happen without breaking existing implementations.
What gets declared
"capabilities": {
"tools": { "listChanged": true },
"resources": { "subscribe": true, "listChanged": true },
"prompts": { "listChanged": true },
"logging": {},
"completions": {}
}
Presence of a key means "I support this primitive at all." Nested flags refine that further. resources.subscribe specifically means "you may call resources/subscribe against me," independent of whether you support resources at all.
The rule both sides follow
A client must not call tools/call if the server never declared a tools capability. A server must not send notifications/resources/updated if the client never declared it accepts resource subscriptions. This "only use what was declared" discipline is what makes the negotiation meaningful rather than decorative. It's a contract, not just metadata.
Why this protects backward compatibility
Suppose a future spec revision adds a brand-new primitive — say, a hypothetical workflows capability. An old client that predates it simply never emits capabilities.workflows, and never receives workflow-related messages. The server sees the capability is absent and just doesn't use that feature with this client. No version bump is required for unrelated features to keep working. No client/server pair needs to support the protocol's entire surface area to interoperate correctly. This is also why a minimal MCP server — tools only, no resources, no prompts, no sampling — is a completely valid, spec-compliant server, not a partial implementation.