diff --git a/client/transport/streamable_http.go b/client/transport/streamable_http.go index 5a6a0f888..26c65f061 100644 --- a/client/transport/streamable_http.go +++ b/client/transport/streamable_http.go @@ -573,17 +573,17 @@ func (c *StreamableHTTP) SendNotification(ctx context.Context, notification mcp. } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted { - // Handle unauthorized error - if resp.StatusCode == http.StatusUnauthorized { - if c.oauthHandler != nil { - return &OAuthAuthorizationRequiredError{ - Handler: c.oauthHandler, - } + switch resp.StatusCode { + case http.StatusOK, http.StatusAccepted, http.StatusNoContent: + return nil + case http.StatusUnauthorized: + if c.oauthHandler != nil { + return &OAuthAuthorizationRequiredError{ + Handler: c.oauthHandler, } - return ErrUnauthorized } - + return ErrUnauthorized + default: body, _ := io.ReadAll(resp.Body) return fmt.Errorf( "notification failed with status %d: %s", @@ -591,8 +591,6 @@ func (c *StreamableHTTP) SendNotification(ctx context.Context, notification mcp. body, ) } - - return nil } func (c *StreamableHTTP) SetNotificationHandler(handler func(mcp.JSONRPCNotification)) { diff --git a/client/transport/streamable_http_test.go b/client/transport/streamable_http_test.go index 60f5547e9..f1e8e500b 100644 --- a/client/transport/streamable_http_test.go +++ b/client/transport/streamable_http_test.go @@ -988,6 +988,36 @@ func TestStreamableHTTP_SendNotification_Unauthorized_StaticToken(t *testing.T) } } +// TestStreamableHTTP_SendNotification_Accepts204NoContent verifies that SendNotification +// treats HTTP 204 No Content as a success response per RFC 7231. +// See: https://github.com/mark3labs/mcp-go/issues/700 +func TestStreamableHTTP_SendNotification_Accepts204NoContent(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNoContent) + })) + defer server.Close() + + transport, err := NewStreamableHTTP(server.URL) + if err != nil { + t.Fatalf("Failed to create StreamableHTTP: %v", err) + } + + if err := transport.Start(context.Background()); err != nil { + t.Fatalf("Failed to start transport: %v", err) + } + + err = transport.SendNotification(context.Background(), mcp.JSONRPCNotification{ + JSONRPC: "2.0", + Notification: mcp.Notification{ + Method: "notifications/initialized", + }, + }) + + if err != nil { + t.Fatalf("SendNotification should accept 204 No Content, got error: %v", err) + } +} + // TestStreamableHTTPHostOverride tests Host header override for StreamableHTTP transport func TestStreamableHTTPHostOverride(t *testing.T) { // Create a test server that captures the Host header