diff --git a/README.md b/README.md index dba8f942..c496c7ba 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,6 @@ class McpController < ActionController::API ) # 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) render(json: body.first, status: status, headers: headers) @@ -1120,7 +1119,6 @@ For more details, see the [MCP Logging specification](https://modelcontextprotoc ```ruby server = MCP::Server.new(name: "my_server") transport = MCP::Server::Transports::StdioTransport.new(server) -server.transport = transport # The client first configures the logging level (on the client side): transport.send_request( @@ -1174,8 +1172,6 @@ server = MCP::Server.new(name: "my_server") # Default Streamable HTTP - session oriented transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) -server.transport = transport - # When tools change, notify clients server.define_tool(name: "new_tool") { |**args| { result: "ok" } } server.notify_tools_list_changed diff --git a/conformance/server.rb b/conformance/server.rb index 4bfac2ad..0b18858d 100644 --- a/conformance/server.rb +++ b/conformance/server.rb @@ -394,7 +394,7 @@ def initialize(port: DEFAULT_PORT) def start server = build_server - transport = build_transport(server) + transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) configure_handlers(server) rack_app = build_rack_app(transport) @@ -480,12 +480,6 @@ def resource_templates ] end - def build_transport(server) - transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) - server.transport = transport - transport - end - def configure_handlers(server) server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "debug") server.server_context = server diff --git a/examples/http_server.rb b/examples/http_server.rb index 7c0e760c..ebe53372 100644 --- a/examples/http_server.rb +++ b/examples/http_server.rb @@ -94,7 +94,6 @@ def template(args, server_context:) # Create the Streamable HTTP transport transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) -server.transport = transport # Create a logger for MCP-specific logging mcp_logger = Logger.new($stdout) diff --git a/examples/streamable_http_server.rb b/examples/streamable_http_server.rb index a7857f63..35350d1e 100644 --- a/examples/streamable_http_server.rb +++ b/examples/streamable_http_server.rb @@ -63,7 +63,6 @@ def call(message:, delay: 0) # Create the Streamable HTTP transport transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) -server.transport = transport # Create a logger for MCP request/response logging mcp_logger = Logger.new($stdout) diff --git a/lib/mcp/transport.rb b/lib/mcp/transport.rb index 4e3f85f3..9f94c601 100644 --- a/lib/mcp/transport.rb +++ b/lib/mcp/transport.rb @@ -7,6 +7,7 @@ class Transport # Initialize the transport with the server instance def initialize(server) @server = server + server.transport = self end # Send a response to the client diff --git a/test/mcp/progress_test.rb b/test/mcp/progress_test.rb index 491245b4..e9552ba5 100644 --- a/test/mcp/progress_test.rb +++ b/test/mcp/progress_test.rb @@ -26,7 +26,6 @@ def handle_request(request); end setup do @server = Server.new(name: "test_server") @transport = MockTransport.new(@server) - @server.transport = @transport @session = ServerSession.new(server: @server, transport: @transport) end diff --git a/test/mcp/server/transports/stdio_notification_integration_test.rb b/test/mcp/server/transports/stdio_notification_integration_test.rb index c20fc069..ec67eaff 100644 --- a/test/mcp/server/transports/stdio_notification_integration_test.rb +++ b/test/mcp/server/transports/stdio_notification_integration_test.rb @@ -61,7 +61,6 @@ def closed? resources: [], ) @transport = StdioTransport.new(@server) - @server.transport = @transport end teardown do diff --git a/test/mcp/server/transports/streamable_http_notification_integration_test.rb b/test/mcp/server/transports/streamable_http_notification_integration_test.rb index 92bb3977..355078fb 100644 --- a/test/mcp/server/transports/streamable_http_notification_integration_test.rb +++ b/test/mcp/server/transports/streamable_http_notification_integration_test.rb @@ -15,7 +15,6 @@ class StreamableHTTPNotificationIntegrationTest < ActiveSupport::TestCase resources: [], ) @transport = StreamableHTTPTransport.new(@server) - @server.transport = @transport end test "server notification methods send SSE notifications through HTTP transport" do diff --git a/test/mcp/server/transports/streamable_http_transport_test.rb b/test/mcp/server/transports/streamable_http_transport_test.rb index 473d849b..83b89551 100644 --- a/test/mcp/server/transports/streamable_http_transport_test.rb +++ b/test/mcp/server/transports/streamable_http_transport_test.rb @@ -2591,7 +2591,6 @@ def string server = Server.new(name: "test", tools: [], prompts: [], resources: []) server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "debug") transport = StreamableHTTPTransport.new(server) - server.transport = transport server.define_tool(name: "log_tool") do |server_context:| server_context.notify_log_message(data: "secret", level: "info") @@ -2668,7 +2667,6 @@ def string test "session-scoped progress notification is sent only to the originating session" do server = Server.new(name: "test", tools: [], prompts: [], resources: []) transport = StreamableHTTPTransport.new(server) - server.transport = transport server.define_tool(name: "progress_tool") do |server_context:| server_context.report_progress(50, total: 100, message: "halfway") @@ -2749,7 +2747,6 @@ def string test "each session stores its own client info independently" do server = Server.new(name: "test", tools: [], prompts: [], resources: []) transport = StreamableHTTPTransport.new(server) - server.transport = transport # Initialize session 1 with client "alpha". init1 = create_rack_request( @@ -2794,7 +2791,6 @@ def string test "each session stores its own logging level independently" do server = Server.new(name: "test", tools: [], prompts: [], resources: []) transport = StreamableHTTPTransport.new(server) - server.transport = transport # Initialize two sessions. init1 = create_rack_request( diff --git a/test/mcp/server_notification_test.rb b/test/mcp/server_notification_test.rb index 82bb9bda..74a89cc9 100644 --- a/test/mcp/server_notification_test.rb +++ b/test/mcp/server_notification_test.rb @@ -36,7 +36,6 @@ def handle_request(request); end ) @mock_transport = MockTransport.new(@server) - @server.transport = @mock_transport end test "#notify_tools_list_changed sends notification through transport" do @@ -122,14 +121,13 @@ def handle_request(request); end end test "notification methods handle transport errors gracefully" do - # Create a transport that raises errors - error_transport = Class.new(MockTransport) do + # Replace server's transport with one that raises on send_notification. + Class.new(MockTransport) do def send_notification(method, params = nil) raise StandardError, "Transport error" end end.new(@server) - @server.transport = error_transport @server.logging_message_notification = MCP::LoggingMessageNotification.new(level: "error") # Mock the exception reporter diff --git a/test/mcp/server_progress_test.rb b/test/mcp/server_progress_test.rb index f31dfaa0..e13fde56 100644 --- a/test/mcp/server_progress_test.rb +++ b/test/mcp/server_progress_test.rb @@ -89,7 +89,6 @@ def call(**kwargs) ) @mock_transport = MockTransport.new(@server) - @server.transport = @mock_transport @session = ServerSession.new(server: @server, transport: @mock_transport) end diff --git a/test/mcp/server_sampling_test.rb b/test/mcp/server_sampling_test.rb index bf250e4f..1526f5f5 100644 --- a/test/mcp/server_sampling_test.rb +++ b/test/mcp/server_sampling_test.rb @@ -41,7 +41,6 @@ def close; end ) @mock_transport = MockTransport.new(@server) - @server.transport = @mock_transport # Simulate client initialization with sampling capability. @server.handle({ @@ -197,8 +196,8 @@ def close; end test "init with sampling capability allows create_sampling_message" do server = Server.new(name: "test", version: "1.0") - mock_transport = MockTransport.new(server) - server.transport = mock_transport + # Assigns server.transport via Transport#initialize, which create_sampling_message requires. + MockTransport.new(server) server.handle({ jsonrpc: "2.0", @@ -222,8 +221,8 @@ def close; end test "init without capabilities rejects create_sampling_message" do server = Server.new(name: "test", version: "1.0") - mock_transport = MockTransport.new(server) - server.transport = mock_transport + # Assigns server.transport via Transport#initialize, which create_sampling_message requires. + MockTransport.new(server) server.handle({ jsonrpc: "2.0", @@ -247,7 +246,6 @@ def close; end test "create_sampling_message uses per-session capabilities via ServerSession" do transport = MCP::Server::Transports::StreamableHTTPTransport.new(@server) - @server.transport = transport # Session with sampling capability passes validation (fails at send_request due to no stream). session_with_sampling = ServerSession.new(server: @server, transport: transport, session_id: "s1") @@ -277,7 +275,6 @@ def close; end test "ServerSession#client_capabilities falls back to server global capabilities" do transport = MCP::Server::Transports::StreamableHTTPTransport.new(@server) - @server.transport = transport # Session without capabilities stored falls back to @server.client_capabilities. session = ServerSession.new(server: @server, transport: transport, session_id: "s3") @@ -295,8 +292,8 @@ def close; end test "session init does not overwrite server global client_capabilities" do server = Server.new(name: "test", version: "1.0") - mock_transport = MockTransport.new(server) - server.transport = mock_transport + # Assigns server.transport via Transport#initialize, which create_sampling_message requires. + MockTransport.new(server) # Non-session init sets global capabilities. server.handle({ @@ -314,7 +311,6 @@ def close; end # Session-scoped init must NOT overwrite global capabilities. transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) - server.transport = transport session = ServerSession.new(server: server, transport: transport, session_id: "s1") server.handle( @@ -340,7 +336,6 @@ def close; end test "Server#create_sampling_message does not see session-scoped capabilities from HTTP init" do server = Server.new(name: "test", version: "1.0") transport = MCP::Server::Transports::StreamableHTTPTransport.new(server) - server.transport = transport # HTTP init stores capabilities on the session, not on the server. session = ServerSession.new(server: server, transport: transport, session_id: "s1")