What is ServerCapabilities?
ServerCapabilities is the structured declaration a server returns inside the InitializeResult response, telling the client exactly which optional protocol surface it has implemented. Rather than a feature-flag registry, it is a typed capability map: each top-level key (prompts, resources, tools, logging, completions, tasks, experimental) is either absent — meaning the server does not support that primitive at all — or present as an object that may carry sub-flags. Absence is semantically significant; clients must not issue requests for absent capabilities. In the MCP message flow, ServerCapabilities appears exactly once per session, during the initialize handshake that precedes all other traffic. The client sends ClientCapabilities alongside its protocol version, the server replies with ServerCapabilities, and both sides compute the intersection they will use for the lifetime of the connection. Nothing in the spec allows a server to advertise new capabilities mid-session, so the object is effectively immutable after that first exchange. The sub-flags deserve attention: resources carries both subscribe (opt-in to resources/subscribe requests) and listChanged (opt-in to sending resources/list_changed notifications), while prompts and tools each carry only listChanged. This asymmetry reflects the protocol's evolution — resource subscriptions were designed for fine-grained change tracking from the start, whereas tool and prompt lists were originally treated as stable. Servers should omit sub-flags they do not implement rather than setting them to false, as the absence form is the canonical way to signal non-support and some SDK validators warn on unexpected false values.
When to use
In every InitializeResult.
When NOT to use
Never lie or omit — clients gate features on what's declared.
Notes
Omission vs. false for sub-flags
The spec treats a missing key and a false-valued key differently in practice: omitting a capability key entirely signals non-support, while setting it to false can trigger validation warnings in stricter SDK builds. Always omit capabilities the server does not implement rather than explicitly negating them. This keeps the InitializeResult payload smaller and avoids ambiguity when clients check for truthiness.
Client must gate every request on capabilities
A compliant client must check ServerCapabilities before issuing any optional request such as resources/subscribe or prompts/list. Sending an unsupported request is a protocol violation, and well-implemented servers will respond with a -32601 MethodNotFound error. In production, mismatches most often surface when a client is upgraded to support a new primitive but connects to an older server that predates that capability key.
experimental field for in-flight features
The experimental object is an open-ended escape hatch for capabilities that are not yet stable in the spec. Its structure is intentionally untyped, so both client and server must agree on a private schema out-of-band. Avoid shipping production features gated only on experimental keys, because the spec can rename or remove them in any minor revision without a deprecation window.
tasks capability added in later spec revision
The tasks key was introduced after the core primitives (tools, resources, prompts) stabilised, and older SDK versions do not include it in their ServerCapabilities type definitions. If you target broad client compatibility, advertise tasks only when you have confirmed the connected client's protocol version supports it. Checking the protocolVersion field in the initialize request before populating tasks is the safest pattern.
Immutability means no hot-reload of features
Because ServerCapabilities is negotiated once and never re-sent, servers that dynamically load or unload plugins cannot reflect those changes to an already-connected client. The only way to re-negotiate capabilities is to close the session and force the client to reconnect. Design your server's plugin lifecycle accordingly — capability-altering changes should trigger a graceful session teardown rather than attempting to silently expand or shrink the advertised surface.
Fields
| Field | Type | Required | Purpose |
|---|---|---|---|
| prompts | { listChanged?: boolean }? | no | Server exposes prompts. |
| resources | { subscribe?, listChanged? }? | no | Server exposes resources. |
| tools | { listChanged?: boolean }? | no | Server exposes tools. |
| logging | object? | no | Server emits log notifications. |
| completions | object? | no | Server provides argument completions. |
| tasks | object? | no | Server supports task-augmented requests. |
| experimental | object? | no | Experimental capabilities. |
Examples
A server with tools and resources
{
"tools": { "listChanged": true },
"resources": { "subscribe": true, "listChanged": true },
"logging": {}
}
Common mistakes
❌ Declaring subscribe:true but ignoring resources/subscribe
✅ Implement every sub-feature you declare.