@@ -578,6 +578,111 @@ class StreamableHTTPTransportTest < ActiveSupport::TestCase
578578 assert_equal "Method not allowed" , body [ "error" ]
579579 end
580580
581+ test "stateless mode allows requests without session IDs, responding with a new session ID" do
582+ stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
583+
584+ init_request = create_rack_request (
585+ "POST" ,
586+ "/" ,
587+ { "CONTENT_TYPE" => "application/json" } ,
588+ { jsonrpc : "2.0" , method : "initialize" , id : "init" } . to_json ,
589+ )
590+ init_response = stateless_transport . handle_request ( init_request )
591+ session_id = init_response [ 1 ] [ "Mcp-Session-Id" ]
592+
593+ assert_not_nil session_id
594+ end
595+
596+ test "stateless mode responds without any session ID when session ID is present" do
597+ stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
598+
599+ request = create_rack_request (
600+ "POST" ,
601+ "/" ,
602+ {
603+ "CONTENT_TYPE" => "application/json" ,
604+ "HTTP_MCP_SESSION_ID" => "unseen_session_id" ,
605+ } ,
606+ { jsonrpc : "2.0" , method : "ping" , id : "123" } . to_json ,
607+ )
608+
609+ response = stateless_transport . handle_request ( request )
610+ assert_equal 200 , response [ 0 ]
611+ assert_equal (
612+ {
613+ "Content-Type" => "application/json" ,
614+ } ,
615+ response [ 1 ] ,
616+ )
617+
618+ body = JSON . parse ( response [ 2 ] [ 0 ] )
619+ assert_equal "2.0" , body [ "jsonrpc" ]
620+ assert_equal "123" , body [ "id" ]
621+ end
622+
623+ test "stateless mode responds with 405 when SSE is requested" do
624+ stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
625+
626+ get_request = create_rack_request (
627+ "GET" ,
628+ "/" ,
629+ {
630+ "CONTENT_TYPE" => "application/json,text/event-stream" ,
631+ } ,
632+ )
633+ response = stateless_transport . handle_request ( get_request )
634+ assert_equal 405 , response [ 0 ]
635+ assert_equal ( { "Content-Type" => "application/json" } , response [ 1 ] )
636+
637+ body = JSON . parse ( response [ 2 ] [ 0 ] )
638+ assert_equal "Method not allowed" , body [ "error" ]
639+ end
640+
641+ test "stateless mode silently responds with success to session DELETE when session ID is not present" do
642+ stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
643+
644+ delete_request = create_rack_request (
645+ "DELETE" ,
646+ "/" ,
647+ { } ,
648+ )
649+ response = stateless_transport . handle_request ( delete_request )
650+ assert_equal 200 , response [ 0 ]
651+ assert_equal ( { "Content-Type" => "application/json" } , response [ 1 ] )
652+
653+ body = JSON . parse ( response [ 2 ] [ 0 ] )
654+ assert body [ "success" ]
655+ end
656+
657+ test "stateless mode silently responds with success to session DELETE when session ID is provided" do
658+ stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
659+
660+ delete_request = create_rack_request (
661+ "DELETE" ,
662+ "/" ,
663+ { "HTTP_MCP_SESSION_ID" => "session_id" } ,
664+ )
665+ response = stateless_transport . handle_request ( delete_request )
666+ assert_equal 200 , response [ 0 ]
667+ assert_equal ( { "Content-Type" => "application/json" } , response [ 1 ] )
668+
669+ body = JSON . parse ( response [ 2 ] [ 0 ] )
670+ assert body [ "success" ]
671+ end
672+
673+ test "stateless mode does not support notifications" do
674+ stateless_transport = StreamableHTTPTransport . new ( @server , stateless : true )
675+
676+ # There are no sessions, so this should fail
677+ assert_raises ( RuntimeError , "Stateless mode does not support notifications" ) do
678+ stateless_transport . send_notification (
679+ "test_notification" ,
680+ { message : "Hello" } ,
681+ session_id : "some_session_id" ,
682+ )
683+ end
684+ end
685+
581686 test "handle post request with a standard error" do
582687 request = create_rack_request (
583688 "POST" ,
0 commit comments