Follow an MCP Request: Discovery, Capabilities, State, and Failure

An engineer's walkthrough of the 2026 protocol, including its primitives, transport behavior, migration boundaries, and missing guarantees.

This chapter describes revision 2026-07-28. The running request is: “Show the incident and deployments for checkout.” Understanding it requires four distinct roles.

The host is the application managing the user interaction, model, context, and execution policy. A client is the host-side protocol component talking to a particular server. A server exposes capabilities. The domain service performs the underlying work. They may share processes, but the responsibilities remain different.

A server does not automatically see the conversation or other servers’ results. The host chooses what crosses each boundary. A host connected to two servers is not creating an automatic server-to-server network.

The three server primitives

PrimitiveExampleTypical control modelProtocol operations
ToolFind accessible incidents for a serviceModel proposes an operation; host dispatchestools/list, tools/call
ResourceA runbook identified by a URIApplication chooses contextual dataresources/list, resources/read, templates
PromptAn incident-review template with argumentsUser or application selects an interactionprompts/list, prompts/get

These control models are useful conventions, not mandatory UI rules. A read can be a tool; a resource URI is not necessarily a filesystem path or a publicly fetchable URL. A prompt is reusable content, not higher-priority authority over the host’s policies. See the primary definitions for tools, resources, and prompts.

For our incident product, start with a tool for search and retrieval. Add a runbook resource if clients can use it effectively. Expose a prompt only if a reusable investigation template has product value; implementing all primitives is not a maturity requirement.

A complete ordinary interaction

Host                        MCP client                 Incident server
 |                              |                            |
 | configure endpoint/identity  |                            |
 |----------------------------->|                            |
 |                              | server/discover (optional) |
 |                              |--------------------------->|
 |                              | versions + capabilities    |
 |                              |<---------------------------|
 |                              | tools/list                 |
 |                              |--------------------------->|
 | select useful tool schemas   | tool contracts              |
 |<-----------------------------|<---------------------------|
 | ask model with task + tools  |                            |
 | model proposes a call       |                            |
 | validate host policy        |                            |
 |----------------------------->| tools/call                  |
 |                              |--------------------------->|
 |                              |          authorize + query |
 | use bounded result          | result / error              |
 |<-----------------------------|<---------------------------|

Endpoint configuration and OAuth discovery are not the same as discovering a server’s tool catalog. Likewise, a schema becoming visible does not authorize invoking it. The host can restrict exposure; the server must independently check an invocation.

The modern core carries protocol version and client capabilities on each request. server/discover lets clients inspect supported versions and capabilities without creating a protocol session. A mismatch is a protocol error, not permission to guess. The versioning source describes compatibility with earlier initialization-based revisions.

Read one wire request

This is an illustrative authenticated HTTP request. The token is a placeholder; JSON-RPC ID 41 correlates this request and response.

POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer <access-token-for-this-resource>
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_incident

{
  "jsonrpc": "2.0",
  "id": 41,
  "method": "tools/call",
  "params": {
    "name": "get_incident",
    "arguments": { "id": "inc-acme-1" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {},
      "io.modelcontextprotocol/clientInfo": {
        "name": "incident-assistant", "version": "1.0.0"
      }
    }
  }
}

The body and mirrored headers must agree. The client name is self-reported metadata, not authenticated identity. A JSON-RPC ID is also not an idempotency key: a retry can have a fresh request ID while representing the same business operation. The base message rules and HTTP binding define the envelopes; application replay semantics are separate.

A successful tool result can include text blocks and structured data. The wire result carries resultType: "complete"; higher-level SDKs may handle wire-only fields for you. Validate structured data before treating it as a domain object. Successful parsing does not establish truth or freshness.

Transports: same meaning, different deployment boundary

With stdio, the client launches a subprocess and exchanges newline-delimited JSON-RPC over standard streams. Standard output belongs to protocol messages; diagnostic text belongs on standard error. Local process permissions, installed code, environment variables, and executable provenance become important.

With Streamable HTTP, each client message is a POST to the MCP endpoint. A request receives JSON or a request-scoped SSE response. A long-lived notification subscription is still a specific request, not an ambient conversation. Streaming progress does not imply durable execution or guaranteed recovery after disconnect. These distinctions follow the transport overview.

For a customer-facing provider, HTTP usually gives a manageable deployment boundary. For a local filesystem integration, stdio may be simpler. Neither choice supplies tenant isolation by itself.

What changed in 2026

Earlier tutorial pattern2026-07-28 baselineDesign consequence
initialize / notifications/initialized handshakePer-request metadata and discoveryDo not attach authority to initialization state
Protocol session and Mcp-Session-IdNo core protocol sessionKeep business state explicit and independently stored
Server-initiated requestsInput requirements returned through MRTRModel interactive work as resumable application phases
Resumable SSE with event replayNo core SSE redeliveryReconcile interrupted work using business identifiers
Experimental core tasksOptional Tasks extensionTest actual extension support

Roots, Sampling, and Logging are deprecated in this revision; deprecated does not mean removed. Older clients still exist, so support policy must distinguish protocol revisions from SDK package majors. The versioned changelog is the authority for this comparison.

The practical SDK trap is subtler: a manually constructed v2 client defaults to legacy behavior. Our lab opts into versionNegotiation: { mode: { pin: "2026-07-28" } } and uses serveStdio on the server. Tests assert the selected era. The SDK migration guide explains these entry points. A modern package name is insufficient evidence of a modern exchange.

Capabilities and extensions are compatibility contracts

Capabilities tell peers which protocol features they can use. They are not OAuth scopes. “This client supports elicitation” does not mean “this user may approve a production rollback.”

The Tasks extension supports work represented by a durable task handle and later status/result retrieval. It is useful when a request outlives a normal interactive wait. Our design recommendation is to keep task ownership, retention, cancellation policy, and result authorization in the application service. A handle identifies work; possession alone should not grant access.

MCP Apps and other extensions add capabilities beyond the core. Require them only when a workflow needs them, and record support by host version. A simple tools-only client should not be surprised by a mandatory UI or long-running task it cannot handle.

Interactive requests without hidden conversation state

Multi Round-Trip Requests let a server return input_required with requests for additional input. The client fulfills supported input requests and retries the original operation with inputResponses and echoed requestState. The Ruby SDK’s MRTR guide provides a readable account of this protocol pattern.

For an application designer, the important question is what happens between rounds. If the user supplies a missing reason, authorization may have changed before the retry. If state crosses the client, treat it as untrusted until verified. Bind any approval artifact to the actor, target, exact intended effect, expiry, and relevant object version. A returned form answer is not automatically a business authorization grant.

Discovery freshness and cache boundaries

List/read results have freshness and sharing hints, ttlMs and cacheScope. The caching guide describes how a client can use them. These hints help avoid repeatedly downloading stable catalogs and data.

Our application recommendation is stricter than merely checking a TTL: partition private entries by authorization context, tenant, endpoint, revision, and other inputs that affect visibility. A tool list can change when permissions change. A cached description can help the host choose a call; it must never replace the server’s fresh authorization check.

Three failure layers

FailureExampleCorrect engineering response
Transport/authenticationConnection reset, HTTP 401Restore connectivity or authorization; preserve uncertainty about writes
ProtocolUnknown method, unsupported version, malformed envelopeCorrect compatibility or request construction
Tool/domainIncident inaccessible, invalid business transitionInterpret the domain outcome; do not blindly retry

MCP can communicate errors, but it cannot choose your recovery policy. For the local lab, expected domain failures return isError: true; the client helper raises them for deterministic tests. Production clients should classify errors before retrying or involving a user.

The key test of understanding is this: the server committed a note, then the response disappeared. What does a new JSON-RPC request ID solve? Only correlation. What prevents a duplicate note? An application operation key, a durable atomic record, and a defined retry contract. That is where phase two begins.

Mcp-engineering Ch 3/8
  1. 1 MCP in 2026: Understand the Protocol, Then Engineer the System 10m
  2. 2 Why MCP Exists When APIs and CLIs Already Work 25m
  3. 3 Follow an MCP Request: Discovery, Capabilities, State, and Failure 30m
  4. 4 Consuming Several MCP Servers Without Losing Control 25m
  5. 5 What Established MCP Operators Actually Teach Us 20m
  6. 6 Design an MCP Provider for Hundreds of Customer Organizations 35m
  7. 7 Build a Two-Server MCP Investigation, Step by Step 45m
  8. 8 From a Working MCP Lab to a Service Customers Can Rely On 35m