Skip to content

Commit ede9f22

Browse files
chore: use raw repo resources (#70)
* use raw repo URIs for resources * fetch repository content from raw urls * ensure no error in test write * Update pkg/github/repository_resource.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * use appropriate file name for text file test --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ad58220 commit ede9f22

File tree

2 files changed

+122
-24
lines changed

2 files changed

+122
-24
lines changed

pkg/github/repository_resource.go

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import (
44
"context"
55
"encoding/base64"
66
"errors"
7+
"fmt"
8+
"io"
79
"mime"
10+
"net/http"
811
"path/filepath"
912
"strings"
1013

@@ -113,7 +116,12 @@ func repositoryResourceContentsHandler(client *github.Client) func(ctx context.C
113116
for _, entry := range directoryContent {
114117
mimeType := "text/directory"
115118
if entry.GetType() == "file" {
116-
mimeType = mime.TypeByExtension(filepath.Ext(entry.GetName()))
119+
// this is system dependent, and a best guess
120+
ext := filepath.Ext(entry.GetName())
121+
mimeType = mime.TypeByExtension(ext)
122+
if ext == ".md" {
123+
mimeType = "text/markdown"
124+
}
117125
}
118126
resources = append(resources, mcp.TextResourceContents{
119127
URI: entry.GetHTMLURL(),
@@ -127,28 +135,62 @@ func repositoryResourceContentsHandler(client *github.Client) func(ctx context.C
127135
}
128136
if fileContent != nil {
129137
if fileContent.Content != nil {
130-
decodedContent, err := fileContent.GetContent()
138+
// download the file content from fileContent.GetDownloadURL() and use the content-type header to determine the MIME type
139+
// and return the content as a blob unless it is a text file, where you can return the content as text
140+
req, err := http.NewRequest("GET", fileContent.GetDownloadURL(), nil)
131141
if err != nil {
132-
return nil, err
142+
return nil, fmt.Errorf("failed to create request: %w", err)
133143
}
134144

135-
mimeType := mime.TypeByExtension(filepath.Ext(fileContent.GetName()))
145+
resp, err := client.Client().Do(req)
146+
if err != nil {
147+
return nil, fmt.Errorf("failed to send request: %w", err)
148+
}
149+
defer func() { _ = resp.Body.Close() }()
150+
151+
if resp.StatusCode != http.StatusOK {
152+
body, err := io.ReadAll(resp.Body)
153+
if err != nil {
154+
return nil, fmt.Errorf("failed to read response body: %w", err)
155+
}
156+
return nil, fmt.Errorf("failed to fetch file content: %s", string(body))
157+
}
158+
159+
ext := filepath.Ext(fileContent.GetName())
160+
mimeType := resp.Header.Get("Content-Type")
161+
if ext == ".md" {
162+
mimeType = "text/markdown"
163+
} else if mimeType == "" {
164+
// backstop to the file extension if the content type is not set
165+
mimeType = mime.TypeByExtension(filepath.Ext(fileContent.GetName()))
166+
}
136167

168+
// if the content is a string, return it as text
137169
if strings.HasPrefix(mimeType, "text") {
170+
content, err := io.ReadAll(resp.Body)
171+
if err != nil {
172+
return nil, fmt.Errorf("failed to parse the response body: %w", err)
173+
}
174+
138175
return []mcp.ResourceContents{
139176
mcp.TextResourceContents{
140177
URI: request.Params.URI,
141178
MIMEType: mimeType,
142-
Text: decodedContent,
179+
Text: string(content),
143180
},
144181
}, nil
145182
}
183+
// otherwise, read the content and encode it as base64
184+
decodedContent, err := io.ReadAll(resp.Body)
185+
if err != nil {
186+
return nil, fmt.Errorf("failed to parse the response body: %w", err)
187+
}
146188

147189
return []mcp.ResourceContents{
148190
mcp.BlobResourceContents{
149191
URI: request.Params.URI,
150192
MIMEType: mimeType,
151-
Blob: base64.StdEncoding.EncodeToString([]byte(decodedContent)), // Encode content as Base64
193+
Blob: base64.StdEncoding.EncodeToString(decodedContent), // Encode content as Base64
152194
},
153195
}, nil
154196
}

pkg/github/repository_resource_test.go

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package github
22

33
import (
44
"context"
5-
"encoding/base64"
65
"net/http"
76
"testing"
87

@@ -13,28 +12,35 @@ import (
1312
"github.com/stretchr/testify/require"
1413
)
1514

15+
var GetRawReposContentsByOwnerByRepoByPath mock.EndpointPattern = mock.EndpointPattern{
16+
Pattern: "/{owner}/{repo}/main/{path:.+}",
17+
Method: "GET",
18+
}
19+
1620
func Test_repositoryResourceContentsHandler(t *testing.T) {
1721
mockDirContent := []*github.RepositoryContent{
1822
{
19-
Type: github.Ptr("file"),
20-
Name: github.Ptr("README.md"),
21-
Path: github.Ptr("README.md"),
22-
SHA: github.Ptr("abc123"),
23-
Size: github.Ptr(42),
24-
HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/README.md"),
23+
Type: github.Ptr("file"),
24+
Name: github.Ptr("README.md"),
25+
Path: github.Ptr("README.md"),
26+
SHA: github.Ptr("abc123"),
27+
Size: github.Ptr(42),
28+
HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/README.md"),
29+
DownloadURL: github.Ptr("https://raw.githubusercontent.com/owner/repo/main/README.md"),
2530
},
2631
{
27-
Type: github.Ptr("dir"),
28-
Name: github.Ptr("src"),
29-
Path: github.Ptr("src"),
30-
SHA: github.Ptr("def456"),
31-
HTMLURL: github.Ptr("https://github.com/owner/repo/tree/main/src"),
32+
Type: github.Ptr("dir"),
33+
Name: github.Ptr("src"),
34+
Path: github.Ptr("src"),
35+
SHA: github.Ptr("def456"),
36+
HTMLURL: github.Ptr("https://github.com/owner/repo/tree/main/src"),
37+
DownloadURL: github.Ptr("https://raw.githubusercontent.com/owner/repo/main/src"),
3238
},
3339
}
3440
expectedDirContent := []mcp.TextResourceContents{
3541
{
3642
URI: "https://github.com/owner/repo/blob/main/README.md",
37-
MIMEType: "",
43+
MIMEType: "text/markdown",
3844
Text: "README.md",
3945
},
4046
{
@@ -44,20 +50,41 @@ func Test_repositoryResourceContentsHandler(t *testing.T) {
4450
},
4551
}
4652

47-
mockFileContent := &github.RepositoryContent{
53+
mockTextContent := &github.RepositoryContent{
4854
Type: github.Ptr("file"),
4955
Name: github.Ptr("README.md"),
5056
Path: github.Ptr("README.md"),
51-
Content: github.Ptr("IyBUZXN0IFJlcG9zaXRvcnkKClRoaXMgaXMgYSB0ZXN0IHJlcG9zaXRvcnku"), // Base64 encoded "# Test Repository\n\nThis is a test repository."
57+
Content: github.Ptr("# Test Repository\n\nThis is a test repository."),
5258
SHA: github.Ptr("abc123"),
5359
Size: github.Ptr(42),
5460
HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/README.md"),
5561
DownloadURL: github.Ptr("https://raw.githubusercontent.com/owner/repo/main/README.md"),
5662
}
5763

64+
mockFileContent := &github.RepositoryContent{
65+
Type: github.Ptr("file"),
66+
Name: github.Ptr("data.png"),
67+
Path: github.Ptr("data.png"),
68+
Content: github.Ptr("IyBUZXN0IFJlcG9zaXRvcnkKClRoaXMgaXMgYSB0ZXN0IHJlcG9zaXRvcnku"), // Base64 encoded "# Test Repository\n\nThis is a test repository."
69+
SHA: github.Ptr("abc123"),
70+
Size: github.Ptr(42),
71+
HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/data.png"),
72+
DownloadURL: github.Ptr("https://raw.githubusercontent.com/owner/repo/main/data.png"),
73+
}
74+
5875
expectedFileContent := []mcp.BlobResourceContents{
5976
{
60-
Blob: base64.StdEncoding.EncodeToString([]byte("IyBUZXN0IFJlcG9zaXRvcnkKClRoaXMgaXMgYSB0ZXN0IHJlcG9zaXRvcnku")),
77+
Blob: "IyBUZXN0IFJlcG9zaXRvcnkKClRoaXMgaXMgYSB0ZXN0IHJlcG9zaXRvcnku",
78+
MIMEType: "image/png",
79+
URI: "",
80+
},
81+
}
82+
83+
expectedTextContent := []mcp.TextResourceContents{
84+
{
85+
Text: "# Test Repository\n\nThis is a test repository.",
86+
MIMEType: "text/markdown",
87+
URI: "",
6188
},
6289
}
6390

@@ -94,21 +121,50 @@ func Test_repositoryResourceContentsHandler(t *testing.T) {
94121
expectError: "repo is required",
95122
},
96123
{
97-
name: "successful file content fetch",
124+
name: "successful blob content fetch",
98125
mockedClient: mock.NewMockedHTTPClient(
99126
mock.WithRequestMatch(
100127
mock.GetReposContentsByOwnerByRepoByPath,
101128
mockFileContent,
102129
),
130+
mock.WithRequestMatchHandler(
131+
GetRawReposContentsByOwnerByRepoByPath,
132+
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
133+
w.Header().Set("Content-Type", "image/png")
134+
// as this is given as a png, it will return the content as a blob
135+
_, err := w.Write([]byte("# Test Repository\n\nThis is a test repository."))
136+
require.NoError(t, err)
137+
}),
138+
),
103139
),
104140
requestArgs: map[string]any{
105141
"owner": []string{"owner"},
106142
"repo": []string{"repo"},
107-
"path": []string{"README.md"},
143+
"path": []string{"data.png"},
108144
"branch": []string{"main"},
109145
},
110146
expectedResult: expectedFileContent,
111147
},
148+
{
149+
name: "successful text content fetch",
150+
mockedClient: mock.NewMockedHTTPClient(
151+
mock.WithRequestMatch(
152+
mock.GetReposContentsByOwnerByRepoByPath,
153+
mockTextContent,
154+
),
155+
mock.WithRequestMatch(
156+
GetRawReposContentsByOwnerByRepoByPath,
157+
[]byte("# Test Repository\n\nThis is a test repository."),
158+
),
159+
),
160+
requestArgs: map[string]any{
161+
"owner": []string{"owner"},
162+
"repo": []string{"repo"},
163+
"path": []string{"README.md"},
164+
"branch": []string{"main"},
165+
},
166+
expectedResult: expectedTextContent,
167+
},
112168
{
113169
name: "successful directory content fetch",
114170
mockedClient: mock.NewMockedHTTPClient(

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy