In depth
MCP capabilities are declared during the initial handshake. When a client calls `initialize`, it sends its own capabilities (things it supports, like `sampling` or `roots/listChanged`). The server responds with its capabilities (`tools`, `resources`, `prompts`, `logging`, `completions`, etc.).
This bidirectional declaration lets clients and servers negotiate what features to use. A client that doesn't support `sampling` should never receive sampling requests; a server that doesn't support `prompts` won't advertise a prompts list. Capability checking is the official way to detect optional features.
Capabilities can be nested — e.g. `tools: { listChanged: true }` means the server supports the `tools/list_changed` notification. This lets future additions be introduced without breaking existing servers (which simply don't advertise the new capability).
Capabilities are the forward-compatibility story of MCP. New versions of the spec add new capabilities; old clients simply don't negotiate them.