Skip to content

Interaction Patterns & Response Handling

Pattern Direction Description
Request → Response Client → Server Every request carries an id; the response echoes it
Notification Both ways Fire-and-forget, no id, no response
Streaming Server → Client Streamable HTTP can deliver progress/log notifications and the final response via a request-scoped SSE stream

How a “session” starts depends on the spec era:

  • Modern (2026-07-28 and later) — MCP is stateless: there is no handshake and no protocol-level session. Every request carries the protocol version, client info, and client capabilities in _meta fields. Clients discover server capabilities with the server/discover request (mandatory for servers to implement).
  • Legacy (2025-11-25 and earlier) — a connection-scoped session was established via the initialize handshake.
sequenceDiagram
participant C as Client
participant S as Server
rect rgba(200, 200, 200, 0.15)
  note over C,S: Legacy (2025-11-25 and earlier)
  C->>S: initialize (version, capabilities, clientInfo)
  S->>C: InitializeResult (negotiated version, capabilities, serverInfo)
  C->>S: notifications/initialized
  note over C,S: Normal operation, then transport-level shutdown
end
rect rgba(200, 200, 200, 0.15)
  note over C,S: Modern (2026-07-28)
  C->>S: server/discover (version + capabilities in _meta)
  S->>C: supportedVersions, capabilities, serverInfo
  C->>S: any request (self-contained _meta on each one)
  S->>C: response
end
Legacy handshake vs. modern stateless requests

Shutdown remains transport-level in both eras: on stdio the client closes the server’s stdin (then terminates the process if needed); on HTTP there is nothing to tear down — each request is independent.

  • Responses are matched to requests by id
  • A response is either a result or an error — never both
  • Tool results contain a content array (text, image, audio, resource links, embedded resources), optional structuredContent, and an isError flag for in-band tool execution errors
  • In 2026-07-28, every result also carries a required resultType: "complete" or "input_required" (MRTR)
  • Hosts feed tool results back into the model’s context for the next turn

Servers with the listChanged capability notify clients when offerings change:

  • notifications/tools/list_changed
  • notifications/resources/list_changed
  • notifications/prompts/list_changed

Clients respond by re-fetching the corresponding list. In 2026-07-28, receiving these notifications requires the client to open a long-lived subscriptions/listen stream and opt into the notification types it wants.

  • Pagination — list operations (tools/list, resources/list, resources/templates/list, prompts/list) return an opaque nextCursor for fetching more items; clients pass it back as cursor and MUST NOT parse or modify it. A missing nextCursor means the end of results.
  • Progress — the client puts a progressToken in the request’s _meta; the server MAY emit notifications/progress (with a progress value that must increase, optional total, optional message) while the request is in flight.
  • Cancellation — on stdio, the client sends notifications/cancelled with the requestId; on Streamable HTTP (2026-07-28), simply closing the SSE response stream is the cancellation signal.
  • Ping — a ping request answered with an empty result existed through 2025-11-25 to verify liveness; it was removed in 2026-07-28.
  • Resource subscriptions — servers with the subscribe capability send notifications/resources/updated for specific resources. In 2026-07-28 clients opt in via the subscriptions/listen stream; the older resources/subscribe / resources/unsubscribe methods belong to legacy revisions.
  • In the current revision, requests are stateless and self-contained; the initialize handshake and protocol-level sessions belong to legacy revisions (2025-11-25 and earlier)
  • list_changed notifications enable dynamic discovery at runtime
  • Servers no longer initiate requests in 2026-07-28 — server-to-client interactions use the MRTR pattern (InputRequiredResult)
  • Pagination cursors are opaque; an invalid cursor yields error -32602

📝 Check your knowledge

1. In the 2026-07-28 revision, how does a server request LLM sampling or user input from the client?
2. Which sequence describes the legacy (2025-11-25 and earlier) session handshake?
3. How does a client learn that a server's tool list changed at runtime?
4. What must a client do with a nextCursor value returned by a paginated list operation?
5. How does a client cancel an in-flight request over Streamable HTTP in 2026-07-28?
6. What happened to the ping utility in the 2026-07-28 revision?