@@ -2061,17 +2061,102 @@ def string
20612061 end
20622062
20632063 test "stateless mode does not support server-sent events" do
2064+ # Notifications have no stream to ride in stateless mode; the transport reports non-delivery
2065+ # instead of raising so per-request session notify_* helpers degrade gracefully (SEP-2567).
20642066 stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
20652067
2066- e = assert_raises ( RuntimeError ) do
2067- stateless_transport . send_notification (
2068- "test_notification" ,
2069- { message : "Hello" } ,
2070- session_id : "some_session_id" ,
2068+ result = stateless_transport . send_notification (
2069+ "test_notification" ,
2070+ { message : "Hello" } ,
2071+ session_id : "some_session_id" ,
2072+ )
2073+
2074+ refute result
2075+ end
2076+
2077+ test "stateless mode does not leak client info onto the shared server" do
2078+ # Each stateless POST runs against an ephemeral per-request session (SEP-2567); concurrent requests
2079+ # must never observe another client's identity through the shared Server instance.
2080+ stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
2081+
2082+ request = create_rack_request (
2083+ "POST" ,
2084+ "/" ,
2085+ { "CONTENT_TYPE" => "application/json" } ,
2086+ {
2087+ jsonrpc : "2.0" ,
2088+ method : "initialize" ,
2089+ id : 1 ,
2090+ params : {
2091+ protocolVersion : "2025-11-25" ,
2092+ capabilities : { roots : { } } ,
2093+ clientInfo : { name : "client-a" , version : "1.0" } ,
2094+ } ,
2095+ } . to_json ,
2096+ )
2097+ response = stateless_transport . handle_request ( request )
2098+
2099+ assert_equal 200 , response [ 0 ]
2100+ assert_nil @server . client_capabilities
2101+ assert_nil @server . instance_variable_get ( :@client )
2102+ end
2103+
2104+ test "stateless mode allows repeated initialize requests" do
2105+ stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
2106+
2107+ 2 . times do |i |
2108+ request = create_rack_request (
2109+ "POST" ,
2110+ "/" ,
2111+ { "CONTENT_TYPE" => "application/json" } ,
2112+ {
2113+ jsonrpc : "2.0" ,
2114+ method : "initialize" ,
2115+ id : i + 1 ,
2116+ params : {
2117+ protocolVersion : "2025-11-25" ,
2118+ clientInfo : { name : "client-#{ i } " , version : "1.0" } ,
2119+ } ,
2120+ } . to_json ,
20712121 )
2122+ response = stateless_transport . handle_request ( request )
2123+
2124+ assert_equal 200 , response [ 0 ]
2125+ body = JSON . parse ( response [ 2 ] [ 0 ] )
2126+ assert body . key? ( "result" ) , "initialize ##{ i + 1 } should succeed, got #{ body . inspect } "
2127+ refute response [ 1 ] . key? ( "Mcp-Session-Id" )
2128+ end
2129+ end
2130+
2131+ test "stateless mode skips progress notifications without raising" do
2132+ reported = [ ]
2133+ configuration = MCP ::Configuration . new
2134+ configuration . exception_reporter = -> ( exception , _context ) { reported << exception }
2135+
2136+ server = Server . new ( name : "stateless_progress_test" , configuration : configuration )
2137+ server . define_tool ( name : "progress_tool" ) do |server_context :|
2138+ server_context . report_progress ( 50 , total : 100 )
2139+ Tool ::Response . new ( [ { type : "text" , text : "ok" } ] )
20722140 end
2141+ stateless_transport = StreamableHTTPTransport . new ( server , stateless : true )
20732142
2074- assert_equal ( "Stateless mode does not support notifications" , e . message )
2143+ request = create_rack_request (
2144+ "POST" ,
2145+ "/" ,
2146+ { "CONTENT_TYPE" => "application/json" } ,
2147+ {
2148+ jsonrpc : "2.0" ,
2149+ method : "tools/call" ,
2150+ id : 1 ,
2151+ params : { name : "progress_tool" , arguments : { } , _meta : { progressToken : "tok" } } ,
2152+ } . to_json ,
2153+ )
2154+ response = stateless_transport . handle_request ( request )
2155+
2156+ assert_equal 200 , response [ 0 ]
2157+ body = JSON . parse ( response [ 2 ] [ 0 ] )
2158+ assert_equal "ok" , body . dig ( "result" , "content" , 0 , "text" )
2159+ assert_empty reported
20752160 end
20762161
20772162 test "stateless mode responds with 202 when client sends a notification/initialized request" do
0 commit comments