From 27b3354f800628f556a4b8de13095c9bb087743b Mon Sep 17 00:00:00 2001 From: ZAIN ULHASSAN <49031989+hassan123789@users.noreply.github.com> Date: Thu, 1 Jan 2026 01:16:20 +0900 Subject: [PATCH] mcp: fix SSEClientTransport to report HTTP errors properly (#740) Fix SSEClientTransport.Connect to check HTTP status code before attempting to parse SSE events. Fixes #714 --- mcp/sse.go | 8 +++++++ mcp/sse_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/mcp/sse.go b/mcp/sse.go index 7f64491..a668c6d 100644 --- a/mcp/sse.go +++ b/mcp/sse.go @@ -351,6 +351,14 @@ func (c *SSEClientTransport) Connect(ctx context.Context) (Connection, error) { return nil, err } + // Check HTTP status code before attempting to parse SSE events. + // This ensures proper error reporting for authentication failures (401), + // authorization failures (403), and other HTTP errors. + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + resp.Body.Close() + return nil, fmt.Errorf("failed to connect: %s", http.StatusText(resp.StatusCode)) + } + msgEndpoint, err := func() (*url.URL, error) { var evt Event for evt, err = range scanEvents(resp.Body) { diff --git a/mcp/sse_test.go b/mcp/sse_test.go index 25435ff..132a031 100644 --- a/mcp/sse_test.go +++ b/mcp/sse_test.go @@ -131,3 +131,58 @@ type roundTripperFunc func(*http.Request) (*http.Response, error) func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } + +func TestSSEClientTransport_HTTPErrors(t *testing.T) { + tests := []struct { + name string + statusCode int + wantErrContain string + }{ + { + name: "401 Unauthorized", + statusCode: http.StatusUnauthorized, + wantErrContain: "Unauthorized", + }, + { + name: "403 Forbidden", + statusCode: http.StatusForbidden, + wantErrContain: "Forbidden", + }, + { + name: "404 Not Found", + statusCode: http.StatusNotFound, + wantErrContain: "Not Found", + }, + { + name: "500 Internal Server Error", + statusCode: http.StatusInternalServerError, + wantErrContain: "Internal Server Error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a test server that returns the specified status code + httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, http.StatusText(tt.statusCode), tt.statusCode) + })) + defer httpServer.Close() + + clientTransport := &SSEClientTransport{ + Endpoint: httpServer.URL, + } + + c := NewClient(testImpl, nil) + _, err := c.Connect(context.Background(), clientTransport, nil) + + if err == nil { + t.Fatalf("expected error, got nil") + } + + errStr := err.Error() + if !bytes.Contains([]byte(errStr), []byte(tt.wantErrContain)) { + t.Errorf("error message %q does not contain %q", errStr, tt.wantErrContain) + } + }) + } +}