@@ -3375,179 +3375,3 @@ func TestStreamableStateless_NewProtocolSession_NoFakeInit(t *testing.T) {
33753375 t .Errorf ("req.ClientCapabilities() = %+v, want non-nil Sampling" , capture .reqClientCapabilities )
33763376 }
33773377}
3378-
3379- func TestStreamableStateless_OldProtocolUnchanged (t * testing.T ) {
3380- // Regression: an old-protocol request to a stateless server must still
3381- // observe a non-nil (synthetic) InitializeParams on the session, so
3382- // existing handlers and the init gate continue to work.
3383- capture := & statelessHandlerCapture {}
3384- mcpServer := NewServer (testImpl , nil )
3385- AddTool (mcpServer , & Tool {Name : "capture" , Description : "captures request info" },
3386- func (ctx context.Context , req * CallToolRequest , args struct {}) (* CallToolResult , any , error ) {
3387- capture .mu .Lock ()
3388- defer capture .mu .Unlock ()
3389- capture .sessionInitParams = req .Session .InitializeParams ()
3390- capture .reqProtocolVersion = req .ProtocolVersion ()
3391- return & CallToolResult {Content : []Content {& TextContent {Text : "ok" }}}, nil , nil
3392- })
3393-
3394- handler := NewStreamableHTTPHandler (
3395- func (* http.Request ) * Server { return mcpServer },
3396- & StreamableHTTPOptions {Stateless : true },
3397- )
3398- httpServer := httptest .NewServer (handler )
3399- defer httpServer .Close ()
3400-
3401- body , err := json .Marshal (map [string ]any {
3402- "jsonrpc" : "2.0" ,
3403- "id" : 1 ,
3404- "method" : "tools/call" ,
3405- "params" : map [string ]any {"name" : "capture" , "arguments" : map [string ]any {}},
3406- })
3407- if err != nil {
3408- t .Fatal (err )
3409- }
3410- httpReq , err := http .NewRequest (http .MethodPost , httpServer .URL , bytes .NewReader (body ))
3411- if err != nil {
3412- t .Fatal (err )
3413- }
3414- httpReq .Header .Set ("Content-Type" , "application/json" )
3415- httpReq .Header .Set ("Accept" , "application/json, text/event-stream" )
3416- httpReq .Header .Set ("MCP-Protocol-Version" , protocolVersion20250618 )
3417-
3418- resp , err := http .DefaultClient .Do (httpReq )
3419- if err != nil {
3420- t .Fatal (err )
3421- }
3422- defer resp .Body .Close ()
3423- if resp .StatusCode != http .StatusOK {
3424- respBody , _ := io .ReadAll (resp .Body )
3425- t .Fatalf ("status = %d, want 200; body = %s" , resp .StatusCode , respBody )
3426- }
3427-
3428- capture .mu .Lock ()
3429- defer capture .mu .Unlock ()
3430- if capture .sessionInitParams == nil {
3431- t .Errorf ("Session.InitializeParams() = nil, want synthetic non-nil for old-protocol session" )
3432- }
3433- if got , want := capture .reqProtocolVersion , protocolVersion20250618 ; got != want {
3434- t .Errorf ("req.ProtocolVersion() = %q (via synthetic session), want %q from MCP-Protocol-Version header" , got , want )
3435- }
3436- }
3437-
3438- func TestStreamableStateless_LegacySessionIgnoredForNewProtocol (t * testing.T ) {
3439- // Under the legacy `allowsessionsinstateless=1` compat flag, stateless
3440- // servers normally read Mcp-Session-Id from the request and call
3441- // GetSessionID. For new-protocol requests, those legacy behaviors must
3442- // be skipped: the session is fully sessionless.
3443- prev := allowsessionsinstateless
3444- allowsessionsinstateless = "1"
3445- t .Cleanup (func () { allowsessionsinstateless = prev })
3446-
3447- var capturedSessionID string
3448- mcpServer := NewServer (testImpl , nil )
3449- AddTool (mcpServer , & Tool {Name : "capture" , Description : "captures session id" },
3450- func (ctx context.Context , req * CallToolRequest , args struct {}) (* CallToolResult , any , error ) {
3451- capturedSessionID = req .Session .ID ()
3452- return & CallToolResult {Content : []Content {& TextContent {Text : "ok" }}}, nil , nil
3453- })
3454-
3455- getSessionIDCalled := false
3456- handler := NewStreamableHTTPHandler (
3457- func (* http.Request ) * Server { return mcpServer },
3458- & StreamableHTTPOptions {Stateless : true },
3459- )
3460- // Patch the server's GetSessionID to detect whether it was consulted.
3461- mcpServer .opts .GetSessionID = func () string {
3462- getSessionIDCalled = true
3463- return "should-not-be-used"
3464- }
3465- httpServer := httptest .NewServer (handler )
3466- defer httpServer .Close ()
3467-
3468- body := newProtocolBody (t , "capture" , struct {}{})
3469- httpReq , err := http .NewRequest (http .MethodPost , httpServer .URL , bytes .NewReader (body ))
3470- if err != nil {
3471- t .Fatal (err )
3472- }
3473- httpReq .Header .Set ("Content-Type" , "application/json" )
3474- httpReq .Header .Set ("Accept" , "application/json, text/event-stream" )
3475- // Explicitly send a session-ID header that the legacy compat path would
3476- // normally honor. For new-protocol requests it must be ignored.
3477- httpReq .Header .Set (sessionIDHeader , "legacy-client-supplied-id" )
3478-
3479- resp , err := http .DefaultClient .Do (httpReq )
3480- if err != nil {
3481- t .Fatal (err )
3482- }
3483- defer resp .Body .Close ()
3484- if resp .StatusCode != http .StatusOK {
3485- respBody , _ := io .ReadAll (resp .Body )
3486- t .Fatalf ("status = %d, want 200; body = %s" , resp .StatusCode , respBody )
3487- }
3488-
3489- if capturedSessionID != "" {
3490- t .Errorf ("Session.ID() = %q, want empty (new-protocol request must ignore Mcp-Session-Id)" , capturedSessionID )
3491- }
3492- if getSessionIDCalled {
3493- t .Errorf ("server.opts.GetSessionID was consulted for a new-protocol request; want it to be skipped" )
3494- }
3495- if echoed := resp .Header .Get (sessionIDHeader ); echoed != "" {
3496- t .Errorf ("response %s header = %q, want empty for new-protocol request" , sessionIDHeader , echoed )
3497- }
3498- }
3499-
3500- func TestStreamableStateless_LegacySessionHonoredForOldProtocol (t * testing.T ) {
3501- // Regression: under `allowsessionsinstateless=1`, an OLD-protocol request
3502- // must still see the legacy session-handling behavior (Mcp-Session-Id
3503- // honored, GetSessionID consulted) so existing deployments don't break.
3504- prev := allowsessionsinstateless
3505- allowsessionsinstateless = "1"
3506- t .Cleanup (func () { allowsessionsinstateless = prev })
3507-
3508- var capturedSessionID string
3509- mcpServer := NewServer (testImpl , nil )
3510- AddTool (mcpServer , & Tool {Name : "capture" , Description : "captures session id" },
3511- func (ctx context.Context , req * CallToolRequest , args struct {}) (* CallToolResult , any , error ) {
3512- capturedSessionID = req .Session .ID ()
3513- return & CallToolResult {Content : []Content {& TextContent {Text : "ok" }}}, nil , nil
3514- })
3515-
3516- handler := NewStreamableHTTPHandler (
3517- func (* http.Request ) * Server { return mcpServer },
3518- & StreamableHTTPOptions {Stateless : true },
3519- )
3520- httpServer := httptest .NewServer (handler )
3521- defer httpServer .Close ()
3522-
3523- body , err := json .Marshal (map [string ]any {
3524- "jsonrpc" : "2.0" ,
3525- "id" : 1 ,
3526- "method" : "tools/call" ,
3527- "params" : map [string ]any {"name" : "capture" , "arguments" : map [string ]any {}},
3528- })
3529- if err != nil {
3530- t .Fatal (err )
3531- }
3532- httpReq , err := http .NewRequest (http .MethodPost , httpServer .URL , bytes .NewReader (body ))
3533- if err != nil {
3534- t .Fatal (err )
3535- }
3536- httpReq .Header .Set ("Content-Type" , "application/json" )
3537- httpReq .Header .Set ("Accept" , "application/json, text/event-stream" )
3538- httpReq .Header .Set (sessionIDHeader , "old-protocol-session-id" )
3539-
3540- resp , err := http .DefaultClient .Do (httpReq )
3541- if err != nil {
3542- t .Fatal (err )
3543- }
3544- defer resp .Body .Close ()
3545- if resp .StatusCode != http .StatusOK {
3546- respBody , _ := io .ReadAll (resp .Body )
3547- t .Fatalf ("status = %d, want 200; body = %s" , resp .StatusCode , respBody )
3548- }
3549-
3550- if capturedSessionID != "old-protocol-session-id" {
3551- t .Errorf ("Session.ID() = %q, want %q (legacy header should be honored for old-protocol requests)" , capturedSessionID , "old-protocol-session-id" )
3552- }
3553- }
0 commit comments