MCP Server Spec: Required Behavior and Optional Capabilities
The MCP server specification separates what every server must do from what it may offer. Knowing the split helps you plan a reliable server, pick compatible clients, and record the implementation choices your team needs to share.
Updated 6 min read
The MCP server specification defines exactly which behaviors every server must implement and which it may offer as optional capabilities. Required behavior is narrow by design, so any compliant client and server can connect; optional capabilities let servers expose tools, resources, and prompts to match their particular use case.
Knowing the split matters when you build a server, evaluate a client library, or check whether two implementations will work together. Tools like fenbs keep those implementation choices in Decisions and rules so every AI connected to the project reads the agreed approach rather than guessing from the spec alone.
What the specification covers
The specification, published at modelcontextprotocol.io (opens in a new tab), defines the full protocol between an MCP client and server. It covers message format, connection lifecycle, capability negotiation, data types, error codes, and each optional feature.
The protocol runs on JSON-RPC 2.0. Requests carry a method and an id. Notifications carry a method with no id. Responses are matched to requests by id. That base layer is the same whether the server runs as a local process or a remote HTTP endpoint.
The spec also describes the two standard transports — stdio for local servers and Streamable HTTP for remote ones — and the message shapes for each optional feature. See mcp server architecture for a wider picture of how the pieces connect.
Required behavior every server must implement
Only a small set of behaviors is required. A server that implements them can connect to any compliant client, even one that offers no optional capabilities.
- Respond to the
initializerequest with the server's name, version, and the capabilities it supports. - Accept the
initializednotification that tells the server the client is ready. - Return well-formed JSON-RPC 2.0 responses, including error objects with the correct numeric code and message string.
- Handle shutdown gracefully when the client closes the connection.
- If tools are declared in the capabilities response, respond correctly to
tools/list.
The initialization handshake is where required and optional behavior first meet. The server declares its capabilities during initialize; the client declares its own. Neither side must offer the same set, but both must complete the handshake correctly for the session to proceed.
Recording the decisions your team makes about required behavior — the capabilities you will and will not implement — keeps later reviewers from reopening settled questions. fenbs is one place to store that record.
Optional capabilities a server may offer
Optional capabilities are declared in the initialize response. A client checks which ones the server lists before calling their methods. Calling a method the server did not declare is a protocol error.
- Tools:
tools/listandtools/call. The most common capability; most client apps require it. - Resources:
resources/list,resources/read, and optionallyresources/subscribe. For exposing file contents, database rows, or other named data. - Prompts:
prompts/listandprompts/get. For providing reusable prompt templates with typed arguments. - Logging:
logging/setLeveland log-message notifications from server to client. - Completions:
completion/complete. Lets a client offer argument suggestions as the user types.
Sampling — the capability that let a server ask the client to run an inference call — is deprecated in the 2026-07-28 version of the specification. Check the current spec at modelcontextprotocol.io before building a server that depends on it.
Record the capabilities your server targets
Add the protocol version, the optional capabilities you chose to implement, and the clients you have tested against as Decisions and rules in fenbs. Every AI assistant connected to the project reads those rules first, so no one rebuilds a choice that is already settled. Create a free account at fenbs.ai, set up a project for the server work, and write the first decisions today.
Create a free accountFree for one person · No credit card · Works with Claude, ChatGPT, Cursor and more
Protocol versions and capability negotiation
Both client and server send a protocolVersion string during initialization. They agree on the highest version both support. If no shared version exists, the client closes the connection.
Version negotiation means a server built for a later spec revision can still talk to an older client, as long as both sides share at least one compatible version. Keep a note of the version your server targets and the clients you have tested — and revisit that note when a client updates.
Transport requirements
The spec defines two transports. Stdio uses standard input and output: the client starts the server as a child process and exchanges JSON-RPC messages through stdin and stdout. Streamable HTTP uses HTTP POST for client-to-server messages and a response stream for server-to-client ones.
Stdio is the transport for local servers. Claude Code and Claude Desktop both use it when they start a local server. Streamable HTTP is for remote servers that clients connect to over a network. The older SSE transport is still described in the spec but new implementations should prefer Streamable HTTP. See mcp server hosting for the deployment side.
Error handling requirements
Error responses must use the JSON-RPC 2.0 error object: a numeric code, a message string, and an optional data field. The spec reserves standard codes for parse errors, invalid requests, method-not-found, invalid parameters, and internal errors.
For tool-level errors — when a tool runs but produces a bad result — the response is a successful JSON-RPC response with isError: true in the tool content. That distinction matters: an AI assistant can read a tool error and explain it; a JSON-RPC error means the communication itself failed.
Reading the spec to make compatibility decisions
The most practical way to use the spec is to check declared capabilities before assuming a client supports something. A client may support tools without supporting resources or prompts. Look at what it declares, not just its name.
When you evaluate an existing server — from the best MCP servers list or the mcp server directory — compare its declared capabilities against what your client actually needs. A mismatch is easier to find before you write integration code than after.
How to do this with fenbs
- Create a free account and set up a project for the server you are building or evaluating.
- Add a Decisions and rules entry for the protocol version your server targets, with the date and the person who decided it.
- Record which optional capabilities you are implementing and why — and which you are deliberately leaving out.
- Add a context note for each client you have tested: the client name, the version, and the result.
- When the spec or a client updates, revisit each entry. Record a new decision that supersedes any choice that has changed.
fenbs does not store spec documents or run MCP connections itself. For the specification, keep the official link at modelcontextprotocol.io (opens in a new tab) in a project note and check it directly when you need the current text.