From 17604beff9740a4ce49508418f900696601f1bba Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Sun, 5 Apr 2026 19:03:43 +0900 Subject: [PATCH] [Doc] Update Rails controller example in README.md The following two points have been updated: ## 1. Use ActionController::API in README controller example `ActionController::Base` includes CSRF protection which rejects POST requests without an authenticity token. MCP clients do not send CSRF tokens, so the controller example should inherit from `ActionController::API` instead. ## 2. Use `stateless: true` for `StreamableHTTPTransport.new` The controller creates a new transport per request, so the session stored on the previous transport is lost. Without `stateless: true`, the second request with `Mcp-Session-Id` returns 404 because the new transport has an empty session map. To share sessions via `Mcp-Session-Id` across requests, there are two approaches. One is persisting the transport in a class variable. The other is mounting the transport as a Rack app via https://github.com/modelcontextprotocol/ruby-sdk/pull/263. Both approaches maintain sessions, so features that depend on `server_context` within the SDK (Progress, Sampling) work correctly. However, per-request user-specific context such as `server_context: { user_id: current_user.id }` cannot be passed since the server is shared across all requests. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6da1928..6a85f34 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ You can use `StreamableHTTPTransport#handle_request` to handle requests with pro status codes (e.g., 202 Accepted for notifications). ```ruby -class McpController < ActionController::Base +class McpController < ActionController::API def create server = MCP::Server.new( name: "my_server", @@ -124,7 +124,8 @@ class McpController < ActionController::Base prompts: [MyPrompt], server_context: { user_id: current_user.id }, ) - transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) + # Since the `MCP-Session-Id` is not shared across requests, `stateless: true` is set. + transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, stateless: true) server.transport = transport status, headers, body = transport.handle_request(request)