[Doc] Align the Docs Page Names With the MCP Specification

Three pages carried names the 2026-07-28 specification does not use: "Multi-Round-Trip Results" for
the pattern the specification titles Multi Round-Trip Requests (MRTR) and serves at basic/patterns/mrtr -
the results are the `InputRequiredResult` wire type, not the feature - plus "Completions" for the utility
the specification titles Completion, and "Notification Subscriptions" for the pattern it titles Subscriptions.
Retitle the three pages to the specification's names, move them to /server/mrtr/ and /client/mrtr/,
/server/completion/, and /server/subscriptions/ following the specification's slugs, update every internal reference,
and keep the URLs published with v1.3.0 working via jekyll-redirect-from.
The Discovery page keeps its title but moves from /server/discovery/ to /server/discover/, matching
the specification's slug and the `server/discover` RPC, with its published URL redirected the same way.
This commit is contained in:
Koichi ITO 2026-08-22 17:47:58 +09:00
commit c9d36b04bf
21 changed files with 55 additions and 45 deletions

View file

@ -9,7 +9,7 @@ Detailed guides are available at https://ruby.sdk.modelcontextprotocol.io.
- Build [MCP servers](https://ruby.sdk.modelcontextprotocol.io/server/) that expose tools, prompts, and resources to any MCP host
- Build [MCP clients](https://ruby.sdk.modelcontextprotocol.io/client/) that connect to any MCP server, with automatic lifecycle negotiation and OAuth 2.1 authorization
- Speak every standard transport: stdio and Streamable HTTP (including SSE), with a Rails integration
- Cover the full protocol surface: server-to-client requests, multi round-trip results, notifications, progress, logging, cancellation, completions, and pagination
- Cover the full protocol surface: server-to-client requests, multi round-trip requests, notifications, progress, logging, cancellation, completions, and pagination
## Installation

View file

@ -25,9 +25,9 @@ This class supports:
- Resource reading via the `resources/read` method (`MCP::Client#read_resource`)
- Prompt listing via the `prompts/list` method (`MCP::Client#prompts`)
- Prompt retrieval via the `prompts/get` method (`MCP::Client#get_prompt`)
- Completion requests via the `completion/complete` method (`MCP::Client#complete`); see [Completions](/server/completions/)
- Completion requests via the `completion/complete` method (`MCP::Client#complete`); see [Completion](/server/completion/)
- Automatic driving of multi round-trip `input_required` results once `on_elicitation`, `on_sampling`,
or `on_roots` handlers are registered; see [Multi-Round-Trip Results](/client/multi-round-trip-results/)
or `on_roots` handlers are registered; see [Multi Round-Trip Requests](/client/mrtr/)
- Cancellation of in-flight requests via the `cancellation:` keyword; see [Cancellation](/client/cancellation/)
- Cursor-based page iteration on the `list_*` methods and whole-collection fetching with
the `max_pages` guard; see [Pagination](/client/pagination/)

View file

@ -32,7 +32,7 @@ only when the server does not serve the modern lifecycle, or when `mode: :legacy
## Lifecycle Negotiation
`MCP::Client#connect` selects the protocol lifecycle automatically by default: on the bundled
`MCP::Client::HTTP` and `MCP::Client::Stdio` transports it probes [`server/discover`](/server/discovery/) first and adopts
`MCP::Client::HTTP` and `MCP::Client::Stdio` transports it probes [`server/discover`](/server/discover/) first and adopts
the stateless modern lifecycle (MCP 2026-07-28, SEP-2575) when the server serves it, falling back to
the classic `initialize` handshake otherwise. Custom transports whose `connect` does not declare
a `mode:` keyword always receive the classic call shape, unchanged.
@ -69,7 +69,7 @@ Pass `mode: :legacy` for an immediate return to the previous behavior, or switch
`MCP::Client#discover` sends `server/discover` directly: sessionless capability discovery
that works before (or instead of) `connect`. It returns an `MCP::Client::DiscoverResult` struct
exposing `supported_versions`, `capabilities`, `server_info`, `instructions`, and
the `ttl_ms` / `cache_scope` cache hints; see the server [Discovery](/server/discovery/) page
the `ttl_ms` / `cache_scope` cache hints; see the server [Discovery](/server/discover/) page
for the wire shapes.
```ruby

62
docs/_client/mrtr.md Normal file
View file

@ -0,0 +1,62 @@
---
layout: default
title: Multi Round-Trip Requests
nav_order: 4
redirect_from:
- /client/multi-round-trip-results/
---
# Multi Round-Trip Requests
MCP 2026-07-28 replaces in-flight server-to-client requests with Multi Round-Trip Requests (SEP-2322): instead of issuing `sampling/createMessage`, `roots/list`,
or `elicitation/create` while a request is being processed, a server may answer with a result whose `resultType` is `"input_required"`, carrying an `inputRequests` map
and an opaque `requestState`; the client fulfills the requests and re-issues the original request with `inputResponses` and the echoed `requestState`.
## Automatic Driving
The Ruby client drives such results automatically: once a handler is registered through `on_elicitation`, `on_sampling`, or `on_roots`, the `call_tool`, `get_prompt`,
and `read_resource` methods fulfill the embedded requests and re-issue the original request with `inputResponses` plus the echoed `requestState`, capped at `input_required_max_rounds`
(10 by default, matching the TypeScript and Python SDKs).
```ruby
client = MCP::Client.new(transport: transport)
client.connect(capabilities: { elicitation: { form: {} } })
client.on_elicitation do |params|
{ action: "accept", content: { name: "Alice" } }
end
# The input_required round trips are driven automatically; this returns the final result.
response = client.call_tool(name: "collect_name", arguments: {})
```
Declare the capabilities matching the registered handlers on `connect`: a server embeds only the request kinds
the client declared.
## Manual Driving
Without a matching handler, `MCP::Client::InputRequiredError` is raised instead of returning the result as if it were final;
the error exposes `input_requests`, `request_state`, and the raw `result` for manual driving via the `input_responses:` and `request_state:` keywords:
```ruby
begin
client.call_tool(name: "collect_name", arguments: {})
rescue MCP::Client::InputRequiredError => error
answers = error.input_requests.transform_values { |request| answer_for(request) }
client.call_tool(
name: "collect_name",
arguments: {},
input_responses: answers,
request_state: error.request_state,
)
end
```
`MCP::ResultType::COMPLETE` and `MCP::ResultType::INPUT_REQUIRED` are provided for forward compatibility.
Servers on legacy protocol versions never send `resultType`, so existing behavior is unchanged.
## Server Side
Authoring `input_required` results with `InputRequiredResult`, securing `requestState`, `resultType` stamping, and the legacy fulfillment
shim that serves pre-2026 clients are documented on the server [Multi Round-Trip Requests](/server/mrtr/) page.

View file

@ -166,7 +166,7 @@ a JSON-RPC `-32601` (method not found) error. To handle methods other than `elic
On a [modern](/client/lifecycle/) connection servers cannot send requests at all; the same
registered handlers instead drive the requests embedded in `input_required` results,
as documented on [Multi-Round-Trip Results](/client/multi-round-trip-results/).
as documented on [Multi Round-Trip Requests](/client/mrtr/).
### Server-to-Client Requests (Sampling)

View file

@ -23,7 +23,7 @@ server = MCP::Server.new(name: "my_server", capabilities: capabilities)
The declared extensions appear in the `initialize` result's `capabilities.extensions`. Extensions the client declared during `initialize` are
readable via `server.client_capabilities[:extensions]` (or `session.client_capabilities[:extensions]` for per-session transports).
On the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle), the same declarations appear in the `server/discover` result,
On the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle), the same declarations appear in the `server/discover` result,
and the client's extensions ride each request's `_meta` envelope instead of an `initialize` handshake.
Inside a handler, `server_context.client_capabilities[:extensions]` reads the current request's declarations
with envelope-first resolution, on either lifecycle.

View file

@ -20,7 +20,7 @@ is never cancellable per the spec.
{: .note }
> Cancellation by notification belongs to the handshake lifecycle, where the session correlates
> `notifications/cancelled` with the in-flight request it targets. Requests of the
> [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) are sessionless single POST exchanges, so a separately
> [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) are sessionless single POST exchanges, so a separately
> POSTed cancel notification cannot reach them; a modern client abandons a request by closing
> the connection instead.

View file

@ -0,0 +1,54 @@
---
layout: default
title: Completion
nav_order: 16
redirect_from:
- /server/completions/
---
# Completion
MCP spec includes [Completions](https://modelcontextprotocol.io/specification/latest/server/utilities/completion),
which enable servers to provide autocompletion suggestions for prompt arguments and resource URIs.
To enable completions, declare the `completions` capability and register a handler:
```ruby
server = MCP::Server.new(
name: "my_server",
prompts: [CodeReviewPrompt],
resource_templates: [FileTemplate],
capabilities: { completions: {} },
)
server.completion_handler do |params|
ref = params[:ref]
argument = params[:argument]
value = argument[:value]
case ref[:type]
when "ref/prompt"
values = case argument[:name]
when "language"
["python", "pytorch", "pyside"].select { |v| v.start_with?(value) }
else
[]
end
{ completion: { values: values, hasMore: false } }
when "ref/resource"
{ completion: { values: [], hasMore: false } }
end
end
```
The handler receives a `params` hash with:
- `ref` - The reference (`{ type: "ref/prompt", name: "..." }` or `{ type: "ref/resource", uri: "..." }`)
- `argument` - The argument being completed (`{ name: "...", value: "..." }`)
- `context` (optional) - Previously resolved arguments (`{ arguments: { ... } }`)
The handler must return a hash with a `completion` key containing `values` (array of strings), and optionally `total` and `hasMore`.
The SDK automatically enforces the 100-item limit per the MCP specification.
The server validates that the referenced prompt, resource, or resource template is registered before calling the handler.
Requests for unknown references return an error.

View file

@ -201,6 +201,6 @@ If an invalid `protocol_version` value is set, an `ArgumentError` is raised.
The pin scopes the `initialize` handshake, so it accepts handshake versions (`2025-11-25` and earlier) only. Per the SEP-2575 era model,
`2026-07-28` carries its version on every request and has no handshake at all, so there is nothing for a pin to configure there and setting it raises `ArgumentError`;
a client asking `initialize` for a modern version is counter-offered the pinned version (or the latest handshake version), matching the TypeScript and Python SDKs.
Clients reach `2026-07-28` through [`server/discover`](/server/discovery/) and the per-request `_meta` envelope, which the bundled transports serve alongside the handshake with no configuration needed.
Clients reach `2026-07-28` through [`server/discover`](/server/discover/) and the per-request `_meta` envelope, which the bundled transports serve alongside the handshake with no configuration needed.
Be sure to check the [MCP spec](https://modelcontextprotocol.io/specification/versioning) for the protocol version to understand the supported features for the version being set.

63
docs/_server/discover.md Normal file
View file

@ -0,0 +1,63 @@
---
layout: default
title: Discovery
nav_order: 3
redirect_from:
- /server/discovery/
---
# Discovery
`server/discover` is the sessionless capability discovery method of MCP 2026-07-28 (SEP-2575).
It responds before `initialize` and without an `Mcp-Session-Id`, so a client learns what a server
offers in a single exchange, without creating a session.
## The Discovery Result
The result carries the modern `supportedVersions`, `capabilities`, and `instructions`, together with
the required `ttlMs`/`cacheScope` cache hints, and the server identity as the optional
`io.modelcontextprotocol/serverInfo` stamp in the result `_meta`.
The request takes no parameters:
```json
{ "jsonrpc": "2.0", "id": 1, "method": "server/discover" }
```
The server answers with the full discovery result:
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"supportedVersions": ["2026-07-28"],
"capabilities": { "tools": { "listChanged": true } },
"instructions": "Use the tools of this server as a last resort",
"_meta": { "io.modelcontextprotocol/serverInfo": { "name": "my_server", "version": "1.0.0" } },
"ttlMs": 0,
"cacheScope": "private",
"resultType": "complete"
}
}
```
## The Stateless Modern Lifecycle
Beyond answering `server/discover`, the server serves the full stateless modern lifecycle: requests carrying the SEP-2575 `_meta` envelope
(`io.modelcontextprotocol/protocolVersion`, `clientInfo`, and `clientCapabilities`) are validated per request,
and the Streamable HTTP transport serves them on a sessionless single-exchange path.
Calling `server/discover` first is not required: the envelope alone selects the modern lifecycle for a request,
while a client performing the classic `initialize` handshake is served on the handshake lifecycle -
the lifecycle is a per-request property, not a server-wide mode.
The bundled transports serve `server/discover` and the per-request `_meta` envelope alongside the `initialize` handshake
with no configuration needed. The `protocol_version` pin scopes the handshake only
and does not affect the modern lifecycle; see [Configuration](/server/configuration/) for details.
## Client Side
On the client, `MCP::Client#connect` negotiates the lifecycle automatically by default
(probe `server/discover`, fall back to the `initialize` handshake), `connect(mode: :modern)` skips
the handshake entirely, `connect(mode: :legacy)` forces the classic handshake, and `MCP::Client#discover`
exposes the raw discovery result. See the client-side [Lifecycle](/client/lifecycle/) page for details.

View file

@ -13,9 +13,9 @@ Elicitation is a **server-to-client request**. The server sends a request and bl
{: .note }
> Unlike [roots](/server/roots/) and [sampling](/server/sampling/), elicitation carries no SEP-2577 deprecation
> and remains fully available. On the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) (MCP 2026-07-28), which forbids server-initiated requests,
> and remains fully available. On the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) (MCP 2026-07-28), which forbids server-initiated requests,
> an `elicitation/create` request is embedded in an `input_required` result instead;
> see [Multi-Round-Trip Results](/server/multi-round-trip-results/).
> see [Multi Round-Trip Requests](/server/mrtr/).
{: .important }
> Per SEP-2260, server-to-client requests (`roots/list`, `sampling/createMessage`, `elicitation/create`) must be associated with

View file

@ -31,11 +31,11 @@ It implements the Model Context Protocol specification, handling model context r
- `initialize` - Initializes the protocol and returns server capabilities
- `server/discover` - Sessionless capability discovery (MCP 2026-07-28, SEP-2575): returns the server's capabilities
before `initialize` and without an `Mcp-Session-Id`, and anchors the stateless modern lifecycle; see [Discovery](/server/discovery/)
before `initialize` and without an `Mcp-Session-Id`, and anchors the stateless modern lifecycle; see [Discovery](/server/discover/)
- `subscriptions/listen` - Long-lived notification subscription stream (MCP 2026-07-28, SEP-2575), replacing the legacy HTTP GET
listening stream; see [Notification Subscriptions](/server/notification-subscriptions/)
listening stream; see [Subscriptions](/server/subscriptions/)
- Multi round-trip `input_required` results (MCP 2026-07-28, SEP-2322): handlers return `MCP::Server::InputRequiredResult` to ask
the client for additional input instead of performing a server-initiated request; see [Multi-Round-Trip Results](/server/multi-round-trip-results/)
the client for additional input instead of performing a server-initiated request; see [Multi Round-Trip Requests](/server/mrtr/)
- `ping` - Simple health check
- `logging/setLevel` - Configures the minimum log level for the server
- `tools/list` - Lists all registered tools and their schemas

View file

@ -15,7 +15,7 @@ The `notifications/message` notification is used for structured logging between
> while remaining fully supported under `2025-11-25`. Use stderr or OpenTelemetry for new servers.
{: .note }
> On the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle), where `logging/setLevel` does not exist, the level
> On the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle), where `logging/setLevel` does not exist, the level
> comes per request from the `io.modelcontextprotocol/logLevel` `_meta` member: it authorizes
> `notifications/message` for that request only, delivered on the request's own response stream.
> A request without the member (or with an unrecognized level) receives no log messages.

87
docs/_server/mrtr.md Normal file
View file

@ -0,0 +1,87 @@
---
layout: default
title: Multi Round-Trip Requests
nav_order: 10
redirect_from:
- /server/multi-round-trip-results/
---
# Multi Round-Trip Requests
The [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) (MCP 2026-07-28) forbids server-initiated requests. Instead, per SEP-2322, a `tools/call`, `prompts/get`, or `resources/read` handler that
opts in to [`server_context:`](/server/server-context/) may return `MCP::Server::InputRequiredResult.new(input_requests:, request_state:)` to ask the client for
additional input (`elicitation/create`, `sampling/createMessage`, or `roots/list` shapes):
```ruby
class GreetingTool < MCP::Tool
description "Greets the user by name"
def self.call(server_context:, **_args)
response = server_context.input_response("user_name")
unless response
return MCP::Server::InputRequiredResult.new(
input_requests: {
user_name: {
method: "elicitation/create",
params: {
message: "What is your name?",
requestedSchema: {
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
},
},
},
},
)
end
MCP::Tool::Response.new([{ type: "text", text: "Hello, #{response.dig(:content, :name)}!" }])
end
end
```
## Deterministic Replay
On the retried request the handler re-runs from the start and reads the answers via
`server_context.input_responses` / `server_context.input_response(key)` and the echoed opaque `server_context.request_state`
(deterministic replay; the server holds no memory between rounds). The server returns `-32021`
when an embedded request needs a client capability the request did not declare.
## Securing `requestState`
The echoed `requestState` arrives as client-controlled input: pass `MCP::Server::RequestStateSecurity.new(key:)` (a 32-byte key) via
`Server.new(request_state_security:)` to have it sealed with AES-256-GCM and bound to a TTL plus the originating method, target, and arguments,
all transparently to handlers. Multi-process deployments must share the key across workers; without `request_state_security:` the state crosses
the wire exactly as the handler wrote it and protecting it is the handler author's responsibility.
```ruby
server = MCP::Server.new(
name: "my_server",
tools: [GreetingTool],
request_state_security: MCP::Server::RequestStateSecurity.new(key: ENV.fetch("MCP_REQUEST_STATE_KEY")),
)
```
## `resultType` Stamping
SEP-2322 also makes `resultType` a required member of every result a 2026-07-28 server returns. The server stamps `resultType: "complete"`
on all results of requests carrying the modern `_meta` envelope (and on `server/discover` results), while results that already carry
a discriminator (`"input_required"`, the tasks extension's `"task"`) keep it. Legacy results stay unstamped, and clients treat an absent
`resultType` as `"complete"` per the spec.
## Legacy Clients
Handlers written in the 2026 style serve pre-2026 clients too: when a `tools/call`, `prompts/get`, or `resources/read` handler returns
an `InputRequiredResult` on the legacy wire, the server fulfills it in place of the client's driver. Each `inputRequests` entry is sent
as the equivalent real server-to-client request (`elicitation/create`, `sampling/createMessage`, `roots/list`), associated with
the originating request per SEP-2260; the answers are collected under the same keys, and the handler re-runs with
`server_context.input_responses` populated and the raw `requestState` echoed, the same deterministic replay contract
the modern client driver follows. The shim is on by default (matching the TypeScript SDK) and capped at 8 rounds;
`MCP::Server.new(input_required_legacy_shim: false)` restores the strict rejection of `input_required` results on legacy requests.
## Client Side
`call_tool`, `get_prompt`, and `read_resource` drive `input_required` results automatically once the matching handlers are registered;
see the client [Multi Round-Trip Requests](/client/mrtr/) page.

View file

@ -26,8 +26,8 @@ No extra configuration is needed.
- **`notify_tools_list_changed`**, **`notify_prompts_list_changed`**, and **`notify_resources_list_changed`** are always broadcast to all connected clients,
as they represent server-wide state changes. These should be called on the `server` instance directly.
On the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) (MCP 2026-07-28), clients receive these broadcasts through the `subscriptions/listen` stream;
see [Notification Subscriptions](/server/notification-subscriptions/).
On the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) (MCP 2026-07-28), clients receive these broadcasts through the `subscriptions/listen` stream;
see [Subscriptions](/server/subscriptions/).
## Notification Format

View file

@ -13,11 +13,11 @@ A `ping` request has no parameters, and the receiver MUST respond promptly with
{: .note }
> `ping` belongs to the handshake lifecycle: MCP 2026-07-28 removes the method altogether (SEP-2575),
> since requests of the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) are single POST exchanges whose connection
> since requests of the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) are single POST exchanges whose connection
> itself signals liveness, leaving nothing to probe between requests. The server answers `ping` on
> the handshake lifecycle only - a modern request naming it is rejected with `-32601` Method not found -
> and calling `ping` on a `server_context` while serving a modern request raises an error. The long-lived
> [`subscriptions/listen`](/server/notification-subscriptions/) stream is kept alive by SSE keepalive
> [`subscriptions/listen`](/server/subscriptions/) stream is kept alive by SSE keepalive
> frames instead.
Servers respond to incoming `ping` requests automatically - no setup is required.

View file

@ -51,7 +51,7 @@ The `server_context.report_progress` method accepts:
string tokens are supported.
{: .note }
> On the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle), progress notifications emitted during a request ride
> On the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle), progress notifications emitted during a request ride
> the request's own SSE response stream. The bundled transport buffers them and flushes after
> the handler returns, so they preserve order but arrive together with the final response rather
> than in real time.

View file

@ -0,0 +1,73 @@
---
layout: default
title: Subscriptions
nav_order: 12
redirect_from:
- /server/notification-subscriptions/
---
# Subscriptions
`subscriptions/listen` is the long-lived notification subscription stream of MCP 2026-07-28 (SEP-2575),
replacing the legacy HTTP GET listening stream.
## Subscribing
The client opts in via the `notifications` filter (`toolsListChanged` / `promptsListChanged` / `resourcesListChanged` / `resourceSubscriptions`),
the server acknowledges the honored subset with `notifications/subscriptions/acknowledged` as the first stream message,
and every delivered notification carries the correlating `io.modelcontextprotocol/subscriptionId` in `_meta`.
The honored subset follows the capabilities the server declares (`listChanged` and `subscribe` flags).
The client opens the stream with a `subscriptions/listen` request:
```json
{
"jsonrpc": "2.0",
"id": "listen-1",
"method": "subscriptions/listen",
"params": {
"notifications": { "toolsListChanged": true, "resourceSubscriptions": ["file:///a.txt"] },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "my_client", "version": "1.0.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
```
The first SSE event on the stream is the acknowledgement:
```json
{
"jsonrpc": "2.0",
"method": "notifications/subscriptions/acknowledged",
"params": {
"notifications": { "toolsListChanged": true, "resourceSubscriptions": ["file:///a.txt"] },
"_meta": { "io.modelcontextprotocol/subscriptionId": "listen-1" }
}
}
```
## Transport Support
The stream is served on the Streamable HTTP modern path; stdio answers `-32601`.
## Limits and Keepalives
Concurrent streams are capped by `max_listen_subscriptions:` (default 1000; pass `nil` to remove the cap), and each stream receives an SSE keepalive
comment frame every `listen_keepalive_interval:` seconds (default 15) so a dropped connection frees its slot; pass `listen_keepalive_interval: nil`
when an upstream proxy already keeps the stream alive.
```ruby
transport = MCP::Server::Transports::StreamableHTTPTransport.new(
server,
max_listen_subscriptions: 500,
listen_keepalive_interval: 30,
)
```
A stream stays open until the client closes the connection; on graceful shutdown via `transport.close`,
each open stream receives its `subscriptions/listen` response before closing.
See [Notifications](/server/notifications/) for the notification types themselves and their session scoping.

View file

@ -76,10 +76,10 @@ The following examples show two common integration styles in Rails.
> routed to the same instance.
>
> Stateless mode (`stateless: true`) does not use sessions and works with any server configuration.
> Requests of the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) (MCP 2026-07-28) are likewise sessionless single exchanges and work
> with any configuration, with two caveats: a [`subscriptions/listen`](/server/notification-subscriptions/) stream
> Requests of the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) (MCP 2026-07-28) are likewise sessionless single exchanges and work
> with any configuration, with two caveats: a [`subscriptions/listen`](/server/subscriptions/) stream
> is held in memory by the process that accepted it, so only notifications emitted in that process reach it,
> and the [multi round-trip](/server/multi-round-trip-results/) `requestState` crosses workers only when they
> and the [multi round-trip](/server/mrtr/) `requestState` crosses workers only when they
> share a `RequestStateSecurity` key.
{: .important }
@ -103,7 +103,7 @@ The following examples show two common integration styles in Rails.
> so both `"mcp.example.com"` and `"mcp.example.com:8443"` work. Pass `dns_rebinding_protection: false`
> to disable the check entirely (e.g., when an upstream proxy or middleware already validates `Host`/`Origin`).
> The check runs before any lifecycle dispatch, so it protects requests of
> the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) as well.
> the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) as well.
### Rails (mount)
@ -179,7 +179,7 @@ and repeated `initialize` requests are permitted. Request-scoped notifications s
(there is no stream to deliver them), while server-to-client requests (`sampling/createMessage`, `roots/list`, `elicitation/create`) raise an error.
{: .note }
> This transport option is distinct from the sessionless [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) of MCP 2026-07-28.
> This transport option is distinct from the sessionless [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) of MCP 2026-07-28.
> The option governs how handshake-lifecycle clients are served; modern requests carry their own `_meta` envelope
> and are served as single exchanges whether or not `stateless: true` is set.
@ -203,7 +203,7 @@ This mode is suitable for simple tool servers that do not need server-initiated
{: .note }
> Like [stateless mode](#stateless-mode), this option applies to handshake-lifecycle clients only.
> Requests of the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) of MCP 2026-07-28 ignore `enable_json_response:`
> Requests of the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) of MCP 2026-07-28 ignore `enable_json_response:`
> and are served as SSE-framed single exchanges, so request-scoped notifications still reach the client.
### Session Limits
@ -224,8 +224,8 @@ transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, session
```
Stateless mode (`stateless: true`) retains no sessions, so neither limit applies to it. The same holds
for requests of the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle), which never create a session;
their long-lived [`subscriptions/listen`](/server/notification-subscriptions/) streams are bounded separately
for requests of the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle), which never create a session;
their long-lived [`subscriptions/listen`](/server/subscriptions/) streams are bounded separately
by `max_listen_subscriptions:`.
### Session Ownership
@ -253,7 +253,7 @@ it also records the `Origin` header at `initialize` and rejects a later request
when both are present - a non-browser client that omits `Origin` (e.g. `curl` or a script) is not stopped by this check.
Enforcing ownership against a determined attacker requires supplying the validator with an authenticated principal.
Requests of the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) carry no `Mcp-Session-Id` and touch no stored session,
Requests of the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) carry no `Mcp-Session-Id` and touch no stored session,
so there is no session to steal, and neither the validator nor the recorded-`Origin` comparison runs for them
(the per-request `Origin` validation of the DNS rebinding protection above still applies);
on that path, authorization is enforced per request by the deploying application.
@ -271,4 +271,4 @@ transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, max_req
```
Unlike the deployment options above, these bounds apply to both lifecycles:
requests of the [modern lifecycle](/server/discovery/#the-stateless-modern-lifecycle) are read through the same byte and nesting limits.
requests of the [modern lifecycle](/server/discover/#the-stateless-modern-lifecycle) are read through the same byte and nesting limits.

View file

@ -13,7 +13,7 @@ The official Ruby SDK for the [Model Context Protocol](https://modelcontextproto
- Build [MCP servers](/server/) that expose tools, prompts, and resources to any MCP host
- Build [MCP clients](/client/) that connect to any MCP server, with automatic lifecycle negotiation and OAuth 2.1 authorization
- Speak every standard transport: stdio and Streamable HTTP (including SSE), with a Rails integration
- Cover the full protocol surface: server-to-client requests, multi round-trip results, notifications, progress, logging, cancellation, completions, and pagination
- Cover the full protocol surface: server-to-client requests, multi round-trip requests, notifications, progress, logging, cancellation, completions, and pagination
## Quick Start
@ -110,8 +110,8 @@ For comprehensive documentation, see:
- [Installation](/installation/) - installing the gem and optional feature dependencies
- [Examples](/examples/) - runnable example scripts and a complete Rails application
- [Protocol Versions](/protocol-versions/) - supported versions, the era model, and client negotiation
- [Building Servers](server/) - transports, discovery, tools, prompts, resources, server-to-client requests, multi round-trip results, notifications, protocol utilities, and configuration
- [Building Clients](client/) - transports, lifecycle negotiation, multi round-trip results, and OAuth 2.1 authorization
- [Building Servers](server/) - transports, discovery, tools, prompts, resources, server-to-client requests, multi round-trip requests, notifications, protocol utilities, and configuration
- [Building Clients](client/) - transports, lifecycle negotiation, multi round-trip requests, and OAuth 2.1 authorization
- [Extensions](/extensions/) - capability extensions and MCP Apps
## API Documentation

Some files were not shown because too many files have changed in this diff Show more