What an MCP server does in 2026: much more than a list of tools

What an MCP server does in 2026: much more than a list of tools

Ask ten developers what a Model Context Protocol (MCP) server does, and most will say it exposes tools to an AI model. That’s true, and it’s also the smallest part of the job.

This is part 1 of my series on what an MCP server does under the 2026-07-28 specification, the revision that made MCP stateless. This part puts the whole server on one page. The facts are as I read them in October 2026. MUST, SHOULD, MAY and OPTIONAL in capitals are the specification’s words, with their RFC 2119 meaning; when something is my own advice, I say so.

In brief

  1. Tools are the smallest part of the job. A production server also describes itself, works without sessions, gives caching hints, asks for input without blocking, reports progress and honours cancellation.
  2. There are three roles: host, client and server. The host creates one client per server, and the model never talks to a server directly.
  3. The work splits into duties, features and extensions. Duties are the protocol’s own rules, most of them MUST or SHOULD; features are the primitives you choose to offer; and extensions are opt-in, per request.
  4. A request passes through seven stages inside the server, and it can leave early with an error, a request for more input or a task.
  5. A one-tool Python server already answers server/discover and speaks 2026-07-28, because the official software development kit (SDK) handles both.

The rest of the job

A production MCP server in 2026 also:

  • describes itself to clients, through server/discover;
  • handles every request without a session;
  • tells clients how long they can cache its answers;
  • asks users for missing information without blocking;
  • reports progress and honours cancellation;
  • acts as an OAuth 2.1 resource server, when it’s a remote server over HTTP that needs authorization;
  • continues distributed traces, when the client sends trace context.

On top of that, it may render an interactive user interface (UI), run long jobs or ship whole workflows to the agent, through extensions the client opts into.

The specification’s authorization page makes authorization OPTIONAL and says a server running over standard input and output (stdio) SHOULD NOT follow it, taking its credentials from the environment instead. Trace context in _meta, from Specification Enhancement Proposal SEP-414, is a convention, not a requirement: the keys are reserved, and a server that follows it continues a trace the client sends.

Hosts, clients and servers

MCP is an open standard that lets AI applications discover and use external tools, data and prompt templates through one common interface. Without it, every pairing of an app and a service needs its own integration, the “N×M” problem. With it, a service implements one server and every compliant host can use it: as of October 2026, Claude, ChatGPT, VS Code, Cursor, Gemini CLI, Microsoft 365 Copilot and many more.

MCP defines three roles. The host is the AI application you interact with. It creates one client for each server it connects to. The model never talks to a server directly: the host decides what to send, what to show and what to ask you to approve.

The host’s runtime holds the conversation, the approvals and the UI, and sits between the large language model (LLM) and the clients. A server can be a local process over stdio, or a remote service over Streamable HTTP in front of software-as-a-service (SaaS) APIs and databases. Clients and servers exchange JSON-RPC messages.

Host applicationUserHost runtimeconversation, approvals, UILLMMCP client AMCP client BMCP server Alocal process over stdioMCP server Bremote service over Streamable HTTPLocal files,dev toolsSaaS APIs,databasesJSON-RPCJSON-RPC

Duties, features and extensions

The work falls into three groups:

  • Protocol duties: discovery, stateless request handling, caching hints, transport rules, authorization, observability and the interaction patterns. Most are MUST or SHOULD.
  • Server features: tools, resources and prompts, plus utilities such as completion and pagination. You implement the ones that fit. The three differ mainly in who decides when each is used: the model, the application or the user.
  • Extensions: opt-in capabilities beyond the core, such as MCP Apps, Tasks and Skills, negotiated per request and never on by default.

The table below gives the interaction patterns their own label: they’re how a request becomes a conversation.

ItemCategorySpec status
server/discoverDutyMUST implement
Server instructionsDutyOptional field (my advice: set it)
Stateless _meta handling, resultTypeDutyMUST
Caching hints, ttlMs and cacheScopeDutyMUST include on complete discover, list and read results
Transport rules and headersDutyMUST, per transport
AuthorizationDutyOptional overall, MUST-heavy when used
OpenTelemetry trace contextDutyConvention
Multi Round-Trip Requests (MRTR)PatternRequired for any server-to-client input
ElicitationPatternOptional, a client capability
Progress, cancellationPatternOptional, but cancellation semantics are mandatory on HTTP
subscriptions/listenPatternRequired if you emit change notifications
Tools, resources, promptsFeatureOptional: implement the ones that fit
MCP Apps, Tasks, Skills, enterprise auth, client credentialsExtensionOpt-in, negotiated per request

One request through the server

Now follow one tools/call through a server: each stage corresponds to a duty. The transport parses the message (the Mcp-Method and Mcp-Name headers apply only over Streamable HTTP). Authorization runs on a remote server that needs it, and telemetry continues a trace only when the client sent traceparent. Then the server reads the envelope: with no session, every request carries its own.

From there, a request can leave early in three ways: an unsupported version gets error -32022; a request that needs more input returns input_required instead of blocking; and long work, for a client that opted into tasks, becomes a task. Otherwise the server runs it, reporting progress and honouring cancellation, and builds the result.

Incoming request1. Transportparse JSON-RPC,check Mcp-Method / Mcp-Name headers2. Authorizationvalidate bearer token,audience, scopes3. Telemetrycontinue traceparent from _meta4. Enveloperead protocolVersion,clientCapabilities, clientInfoVersionsupported?UnsupportedProtocolVersion-320225. Dispatchtools/call, resources/read,prompts/get, ...Needs moreinput?Return resultType input_required+ inputRequests + requestStateLong-runningand client opted into tasks?Return resultType task+ taskId6. Execute against backendprogress, cancellation7. Build resultcontent + structuredContent+ _meta serverInfoResponsenoyesyesnoyesno

The smallest server, in Python

The Python in this series uses the official SDK, mcp 2.3.0. Here’s the smallest server I could write, called through its in-memory Client:

import anyio
from mcp import Client
from mcp.server import MCPServer

mcp = MCPServer("calculator", version="1.0.0")


@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b


async def main():
    async with Client(mcp) as client:
        print("protocol:", client.protocol_version)
        print("discover:", client.session.discover_result.supported_versions)
        result = await client.call_tool("add", {"a": 2, "b": 3})
        print("content:", result.content[0].text)
        print("structuredContent:", result.structured_content)
        print("serverInfo:", result.meta["io.modelcontextprotocol/serverInfo"])


anyio.run(main)

It prints:

protocol: 2026-07-28
discover: ['2026-07-28']
content: 5
structuredContent: {'result': 5}
serverInfo: {'name': 'calculator', 'version': '1.0.0'}

One function, and the server already answers discovery: the client’s first call was server/discover, and the two settled on 2026-07-28. The result has the shape of stage 7: the sum as text in content, the same value in structuredContent (the SDK wraps a plain int as {"result": ...}), and serverInfo in _meta.

Three SDK details are worth knowing:

  • MCPServer is the version 2 name of the class older examples call FastMCP, and the mcp.server.fastmcp import is gone.
  • By default, Client tries server/discover first and falls back to the older initialize handshake for a server that doesn’t answer it.
  • Given a server object rather than a URL or a command, Client in its default mode calls it in-process, with no JSON-RPC framing, so stage 1 never runs. That’s for testing, not for deployment.

Method and caveats

  • Built from chapters 1 and 3 of my guide to MCP servers, written against the 2026-07-28 specification. Product facts are as of October 2026.
  • The Python sample was run against mcp 2.3.0 on 8 October 2026, and the output shown is what it printed. The SDK details come from its documentation and source.
  • Where the specification is more precise than my notes (when a server acts as an OAuth resource server, whether it continues traces, and which results carry caching hints), this article follows its authorization page, SEP-414 and its caching page.
SeriesWhat an MCP server actually does in 2026Part 1 of 35
  1. What an MCP server does in 2026: much more than a list of tools
  2. MCP goes stateless: what changed in the 2026-07-28 specification
  3. server/discover: the one method every MCP server must implement
  4. Server instructions: the paragraph every model reads first
  5. Stateless MCP requests: _meta, resultType and explicit handles
  6. Caching hints and pagination in MCP
  7. MCP transports in 2026: stdio and Streamable HTTP

I’m Amir Pournasserian. I build AI and platform systems for a living, maintain FluentCMS and YeSvelte, and write here about what I find along the way.