Skip to content

Commit c3e9723

Browse files
authored
fix: ignore session IDs in stateless mode instead of rejecting them (#375)
1 parent 75abd9a commit c3e9723

File tree

2 files changed

+60
-7
lines changed

2 files changed

+60
-7
lines changed

server/streamable_http.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,9 +614,7 @@ func (s *StatelessSessionIdManager) Generate() string {
614614
return ""
615615
}
616616
func (s *StatelessSessionIdManager) Validate(sessionID string) (isTerminated bool, err error) {
617-
if sessionID != "" {
618-
return false, fmt.Errorf("session id is not allowed to be set when stateless")
619-
}
617+
// In stateless mode, ignore session IDs completely - don't validate or reject them
620618
return false, nil
621619
}
622620
func (s *StatelessSessionIdManager) Terminate(sessionID string) (isNotAllowed bool, err error) {

server/streamable_http_test.go

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,8 @@ func TestStreamableHTTP_POST_SendAndReceive_stateless(t *testing.T) {
419419
}
420420
})
421421

422-
t.Run("Invalid session id", func(t *testing.T) {
423-
// send ping message
422+
t.Run("Session id ignored in stateless mode", func(t *testing.T) {
423+
// send ping message with session ID - should be ignored in stateless mode
424424
pingMessage := map[string]any{
425425
"jsonrpc": "2.0",
426426
"id": 123,
@@ -441,8 +441,63 @@ func TestStreamableHTTP_POST_SendAndReceive_stateless(t *testing.T) {
441441
}
442442
defer resp.Body.Close()
443443

444-
if resp.StatusCode != 400 {
445-
t.Errorf("Expected status 400, got %d", resp.StatusCode)
444+
// In stateless mode, session IDs should be ignored and request should succeed
445+
if resp.StatusCode != http.StatusOK {
446+
t.Errorf("Expected status 200, got %d", resp.StatusCode)
447+
}
448+
449+
// Verify the response is valid
450+
responseBody, err := io.ReadAll(resp.Body)
451+
if err != nil {
452+
t.Fatalf("Failed to read response: %v", err)
453+
}
454+
var response map[string]any
455+
if err := json.Unmarshal(responseBody, &response); err != nil {
456+
t.Fatalf("Failed to unmarshal response: %v", err)
457+
}
458+
if response["id"].(float64) != 123 {
459+
t.Errorf("Expected id 123, got %v", response["id"])
460+
}
461+
})
462+
463+
t.Run("tools/list with session id in stateless mode", func(t *testing.T) {
464+
// Test the specific scenario from the issue - tools/list with session ID
465+
toolsListMessage := map[string]any{
466+
"jsonrpc": "2.0",
467+
"method": "tools/list",
468+
"id": 1,
469+
}
470+
toolsListBody, _ := json.Marshal(toolsListMessage)
471+
req, err := http.NewRequest("POST", server.URL, bytes.NewBuffer(toolsListBody))
472+
if err != nil {
473+
t.Fatalf("Failed to create request: %v", err)
474+
}
475+
req.Header.Set("Content-Type", "application/json")
476+
req.Header.Set(headerKeySessionID, "mcp-session-2c44d701-fd50-44ce-92b8-dec46185a741")
477+
478+
resp, err := server.Client().Do(req)
479+
if err != nil {
480+
t.Fatalf("Failed to send message: %v", err)
481+
}
482+
defer resp.Body.Close()
483+
484+
// Should succeed in stateless mode even with session ID
485+
if resp.StatusCode != http.StatusOK {
486+
bodyBytes, _ := io.ReadAll(resp.Body)
487+
t.Errorf("Expected status 200, got %d. Response: %s", resp.StatusCode, string(bodyBytes))
488+
}
489+
490+
// Verify the response is valid
491+
responseBody, err := io.ReadAll(resp.Body)
492+
if err != nil {
493+
t.Fatalf("Failed to read response: %v", err)
494+
}
495+
var response map[string]any
496+
if err := json.Unmarshal(responseBody, &response); err != nil {
497+
t.Fatalf("Failed to unmarshal response: %v", err)
498+
}
499+
if response["id"].(float64) != 1 {
500+
t.Errorf("Expected id 1, got %v", response["id"])
446501
}
447502
})
448503
}

0 commit comments

Comments
 (0)