Make StreamableHTTPTransport a Rack application

## Motivation and Context

Add `call(env)` to `StreamableHTTPTransport`, making it a Rack application
that works with `mount`, `run`, and Rack middleware.

Refactor examples to use Rack middleware classes for MCP logging instead of proc wrappers,
demonstrating idiomatic Rack composition with the new `run(transport)` pattern.

Update README.md with mount and controller integration patterns.

Closes #59, #60

## How Has This Been Tested?

Added tests for `call(env)` as a Rack app. All tests pass.

## Breaking Change

No breaking changes. All existing APIs are preserved:

- `StreamableHTTPTransport.new(server)` continues to work as before.
- `handle_request(request)` is unchanged. The new `call(env)` is a purely additive public method.
This commit is contained in:
Koichi ITO 2026-03-12 01:05:13 +09:00
commit af99a303e1

View file

@ -3,6 +3,15 @@
require "json"
require_relative "../../transport"
# This file is autoloaded only when `StreamableHTTPTransport` is referenced,
# so the `rack` dependency does not affect `StdioTransport` users.
begin
require "rack"
rescue LoadError
raise LoadError, "The 'rack' gem is required to use the StreamableHTTPTransport. " \
"Add it to your Gemfile: gem 'rack'"
end
module MCP
class Server
module Transports
@ -39,6 +48,11 @@ module MCP
STREAM_WRITE_ERRORS = [IOError, Errno::EPIPE, Errno::ECONNRESET].freeze
SESSION_REAP_INTERVAL = 60
# Rack app interface. This transport can be mounted as a Rack app.
def call(env)
handle_request(Rack::Request.new(env))
end
def handle_request(request)
case request.env["REQUEST_METHOD"]
when "POST"
@ -531,7 +545,7 @@ module MCP
end
end
[200, SSE_HEADERS, body]
[200, SSE_HEADERS.dup, body]
end
# Returns the SSE stream available for server-to-client messages.
@ -613,7 +627,7 @@ module MCP
def setup_sse_stream(session_id)
body = create_sse_body(session_id)
[200, SSE_HEADERS, body]
[200, SSE_HEADERS.dup, body]
end
def create_sse_body(session_id)