Skip to content
Merged
5 changes: 3 additions & 2 deletions pkg/github/repository_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,14 @@ func RepositoryResourceContentsHandler(resourceURITemplate *uritemplate.Template
}

resp, err := rawClient.GetRawContent(ctx, owner, repo, path, rawOpts)
if err != nil {
return nil, fmt.Errorf("failed to get raw content: %w", err)
}
defer func() {
_ = resp.Body.Close()
}()
// If the raw content is not found, we will fall back to the GitHub API (in case it is a directory)
switch {
case err != nil:
return nil, fmt.Errorf("failed to get raw content: %w", err)
case resp.StatusCode == http.StatusOK:
ext := filepath.Ext(path)
mimeType := resp.Header.Get("Content-Type")
Expand Down
40 changes: 40 additions & 0 deletions pkg/github/repository_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package github

import (
"context"
"errors"
"net/http"
"net/url"
"testing"
Expand All @@ -12,6 +13,15 @@ import (
"github.com/stretchr/testify/require"
)

// errorTransport is a http.RoundTripper that always returns an error.
type errorTransport struct {
err error
}

func (t *errorTransport) RoundTrip(*http.Request) (*http.Response, error) {
return nil, t.err
}

type resourceResponseType int

const (
Expand Down Expand Up @@ -272,3 +282,33 @@ func Test_repositoryResourceContents(t *testing.T) {
})
}
}

// Test_repositoryResourceContentsHandler_NetworkError tests that a network error
// during raw content fetch does not cause a panic (nil response body dereference).
func Test_repositoryResourceContentsHandler_NetworkError(t *testing.T) {
base, _ := url.Parse("https://raw.example.com/")
networkErr := errors.New("network error: connection refused")

httpClient := &http.Client{Transport: &errorTransport{err: networkErr}}
client := github.NewClient(httpClient)
mockRawClient := raw.NewClient(client, base)
deps := BaseDeps{
Client: client,
RawClient: mockRawClient,
}
ctx := ContextWithDeps(context.Background(), deps)

handler := RepositoryResourceContentsHandler(repositoryResourceContentURITemplate)

request := &mcp.ReadResourceRequest{
Params: &mcp.ReadResourceParams{
URI: "repo://owner/repo/contents/README.md",
},
}

// This should not panic, even though the HTTP client returns an error
resp, err := handler(ctx, request)
require.Error(t, err)
require.Nil(t, resp)
require.ErrorContains(t, err, "failed to get raw content")
}