How MCP Servers Handle Requests
The Model Context Protocol gives Claude a standard way to reach outside its own context window and touch real systems.
A server that speaks MCP might wrap a database, a ticketing system, a filesystem, or an internal API.
Before any of that is useful, though, a client and a server have to agree on what the server can do and how to talk to it.
That agreement, and everything that follows it, is the request lifecycle this page walks through.
Understanding the lifecycle matters because most bugs in a new MCP server are not tool bugs at all.
They are lifecycle bugs: a tool registered too late, a capability never declared, a response shaped wrong for the step the client thinks it is on.
Summary
- Core Idea: Every MCP interaction moves through the same four stages: connect, negotiate capabilities, exchange requests, and eventually disconnect.
- Why It Matters: A server that skips or reorders a stage will look broken to the client even if every individual tool handler is correct.
- Key Concepts: connection, capability negotiation, tools, resources, prompts, request/response cycle.
- When to Use: Reach for this mental model any time you're debugging "the client can't see my tool," designing a new handler, or deciding what belongs in a tool versus a resource.
- Limitations / Trade-offs: The lifecycle describes structure, not performance. A well-formed request/response cycle can still be slow, insecure, or wrong on the inside.
- Related Topics: scaffolding a minimal server, stdio versus HTTP/SSE transport, authenticating remote servers.
Foundations
An MCP server is a program that exposes a fixed set of capabilities to a client over a defined protocol.
Those capabilities fall into three categories: tools the client can invoke, resources the client can read, and prompts the client can fetch as reusable templates.
The client is usually Claude itself, acting through an application like Claude Code, Claude Desktop, or a custom integration built on the Agent SDK.
Think of the server as a receptionist at a building with several departments.
The receptionist does not do the work of every department, it just knows which room handles which request and passes things along correctly.
An MCP server behaves the same way: it does not need to know why Claude wants to call a tool, only how to route the call to the right handler and return a well-formed result.
The lifecycle itself has four stages, in a fixed order:
1. Connect client opens a transport (stdio pipe, or HTTP/SSE session)
2. Negotiate client and server exchange supported capabilities and versions
3. Exchange client lists/calls tools, reads resources, fetches prompts
4. Disconnect transport closes, session state is torn downNo stage can be skipped, and stage 3 cannot begin until stage 2 finishes successfully.
Mechanics & Interactions
Connection is transport-specific but protocol-agnostic in intent.
Over stdio, the client spawns the server as a subprocess and talks to it over standard input and output.
Over HTTP/SSE, the client opens a network connection to a running server process, often authenticating first.
Either way, the goal of this stage is the same: establish a channel the two sides can exchange structured messages over.
Capability negotiation happens immediately after connection, before the client trusts the server for anything else.
The client sends an initialize request describing what it supports; the server responds with its own supported protocol version and a manifest of what it offers, which tools exist, which resources are readable, which prompts are available.
This is the point where a client learns "this server has a search_tickets tool" or "this server exposes a config://settings resource."
A tool the server never registered in this exchange effectively does not exist to the client, no matter how correct its underlying handler code is.
This is the single most common source of "my tool doesn't show up" bugs: the handler was written, but never wired into the registration the server advertises during negotiation.
Request exchange is where the actual work happens, and it is itself a repeating request/response cycle.
The client sends a request, naming a tool and its arguments, or naming a resource URI, or naming a prompt template.
Client -> Server: call_tool("get_weather", {"city": "Denver"})
Server: routes to the registered handler for get_weather
Server -> Client: { "temperature": 61, "conditions": "clear" }The server routes that request to the matching handler, runs it, and returns a structured response.
Each call is independent at the protocol level, the server does not assume any ordering between one tool call and the next unless the server's own logic imposes one.
Resources behave similarly but are read-oriented: the client asks for a URI, the server returns content, no arguments beyond the URI itself are typically involved.
Prompts are templates the server hands back for the client to fill in and use, closer to a reusable snippet than a live action.
Disconnection closes the transport and discards any per-session state the server was holding.
A well-built server treats each session as disposable: if it needs to remember something past a single connection, that belongs in a real datastore, not in memory tied to the transport.
Advanced Considerations & Applications
At scale, the lifecycle interacts with two things that do not show up in a toy example: concurrency and statefulness.
A single server process, especially one deployed over HTTP/SSE, may hold many client sessions open at once, each having gone through its own negotiation independently.
The server has to keep each session's negotiated capabilities and any session-scoped state separate, mixing them up between clients is a correctness bug, not just a performance one.
Statefulness inside a tool handler is also worth being deliberate about.
A tool that reads and writes to an external system, a database row, a file, an API, should treat every invocation as a fresh request even if the underlying resource has state, because the protocol itself gives no guarantee about how many times a tool will be called or in what order relative to other tools.
| Approach | Strength | Weakness | Best Fit |
|---|---|---|---|
| Stateless handlers, state lives externally | Safe under concurrent clients, easy to reason about | Requires a real datastore or API behind the tool | Production servers with more than one client |
| In-memory session state | Simple, fast, no external dependency | Lost on restart, breaks under multiple server instances | Local, single-user, stdio development servers |
Security follows directly from the lifecycle too.
Because negotiation happens before any tool call is trusted, an HTTP/SSE server that skips authentication at the connection stage has no later point in the lifecycle where it can safely add that check back in without breaking the protocol's assumptions.
That is why authentication for remote servers is handled at connection time, not inside individual tool handlers.
Common Misconceptions
- "A tool exists once I write the handler function." - The client only learns about a tool during capability negotiation, so the handler must also be registered with the server's tool list, not just implemented.
- "Tools, resources, and prompts are interchangeable." - They are distinct capability types with different semantics: tools perform actions and can have side effects, resources are read-only content, prompts are reusable templates.
- "The server remembers what happened in the last tool call." - Unless the server explicitly persists state outside the connection, each request should be treated as independent; relying on implicit memory across calls is a common source of subtle bugs.
- "Negotiation is a formality that can be skipped in a minimal server." - Even a minimal server built with an SDK performs negotiation automatically; skipping or misconfiguring it is what causes "the client sees zero tools" failures.
- "stdio and HTTP/SSE have fundamentally different lifecycles." - The four-stage lifecycle is the same for both, only the connection stage differs in mechanics.
FAQs
What are the four stages of the MCP request lifecycle?
- Connect: client and server establish a transport.
- Negotiate: both sides exchange supported capabilities and protocol version.
- Exchange: the client calls tools, reads resources, or fetches prompts, and the server responds.
- Disconnect: the transport closes and session state is torn down.
Why can't a client call a tool before negotiation finishes?
The client doesn't yet know what the server offers. Negotiation is where the server's tool, resource, and prompt manifest is sent, so any call attempted earlier has nothing to route against on the client side.
My tool handler works when I test it directly, but the client says it doesn't exist. Why?
- The handler function was likely written but never registered with the server's tool list.
- Registration is what makes a tool appear in the capability manifest sent during negotiation.
- Writing the function alone is not enough; it must be wired into the server's declared capabilities.
What's the difference between a tool and a resource?
A tool is something the client invokes to perform an action, which may have side effects and takes arguments. A resource is read-only content the client fetches by URI, closer to reading a file than calling a function.
Does the server need to remember previous tool calls in a session?
Not by default. Each request in the exchange stage is generally treated as independent. If a server needs memory across calls, that should be built deliberately, usually backed by an external store, not assumed from the protocol itself.
Is the lifecycle different for stdio versus HTTP/SSE servers?
No. The four stages are the same for both. What differs is how the connect stage works mechanically, a subprocess pipe for stdio versus a network session for HTTP/SSE.
What happens if a client and server support different protocol versions?
Negotiation is exactly where this mismatch surfaces. The client and server exchange version information at this stage, and the SDKs generally handle falling back to a mutually supported version or failing clearly if none exists.
Can a single server handle multiple clients at once?
Yes, particularly over HTTP/SSE, where multiple sessions can be open concurrently. Each session goes through its own connect and negotiate stages independently, and the server must keep their state separate.
Where does authentication fit into this lifecycle?
For remote servers, authentication happens at or before the connect stage, since everything after negotiation assumes the connection is already trusted. Checking credentials inside individual tool handlers is too late in the lifecycle to be a sound security boundary.
What's a practical example of a full request cycle?
A client calls a get_weather tool with a city argument. The server routes that call to its registered get_weather handler, the handler runs and returns structured data, and the server sends that data back as the response, closing out one exchange within the larger session.
Why does this lifecycle matter if I'm just using an SDK that handles it for me?
The SDK handles the mechanics, but understanding the stages helps you diagnose why a tool isn't showing up, why a session behaves unexpectedly under concurrent clients, or why an authentication check needs to live where it does.
Do prompts and resources also go through capability negotiation?
Yes. Negotiation covers all three capability types together, tools, resources, and prompts. A resource or prompt the server hasn't declared is just as invisible to the client as an unregistered tool.
Related
- MCP Server Basics - scaffold a minimal server and see the lifecycle in a runnable form.
- stdio vs HTTP/SSE MCP Deployment Comparison - how the connection stage differs by transport.
- Authenticating Remote MCP Servers - where auth fits relative to the connect and negotiate stages.
- Building an MCP Server with the Python SDK - see tool, resource, and prompt registration in code.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the current Model Context Protocol Python/TypeScript SDKs. Model names, SDK versions, and the MCP spec move quickly - verify current specifics at platform.claude.com/docs and modelcontextprotocol.io before relying on them.