Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb2503f3

Browse files
mntltySamMorrowDrumsCopilot
authored
chore: repository resource tests (#69)
* refactor to make testing easier* not needed in handler func* small cleanup* create repository_resource_test* remove chatty comments* comment cleanup, function rename and some more tests* fix test for ubuntu runner* remove it for now* make required args explicit instead of panic* more tests and cleanup* 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.goCo-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>---------Co-authored-by: Sam Morrow <info@sam-morrow.com>Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parentb3a3d15 commitb2503f3

File tree

3 files changed

+437
-77
lines changed

3 files changed

+437
-77
lines changed

‎pkg/github/repository_resource.go

Lines changed: 149 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ package github
33
import (
44
"context"
55
"encoding/base64"
6+
"errors"
7+
"fmt"
8+
"io"
69
"mime"
10+
"net/http"
711
"path/filepath"
812
"strings"
913

@@ -13,110 +17,185 @@ import (
1317
"github.com/mark3labs/mcp-go/server"
1418
)
1519

16-
// getRepositoryContent defines the resource template and handler for the Repository Content API.
17-
funcgetRepositoryContent(client*github.Client,t translations.TranslationHelperFunc) (mainTemplate mcp.ResourceTemplate,reftemplate mcp.ResourceTemplate,shaTemplate mcp.ResourceTemplate,tagTemplate mcp.ResourceTemplate,prTemplate mcp.ResourceTemplate,handler server.ResourceTemplateHandlerFunc) {
18-
20+
// getRepositoryResourceContent defines the resource template and handler for getting repository content.
21+
funcgetRepositoryResourceContent(client*github.Client,t translations.TranslationHelperFunc) (mcp.ResourceTemplate, server.ResourceTemplateHandlerFunc) {
1922
returnmcp.NewResourceTemplate(
2023
"repo://{owner}/{repo}/contents{/path*}",// Resource template
2124
t("RESOURCE_REPOSITORY_CONTENT_DESCRIPTION","Repository Content"),
22-
),mcp.NewResourceTemplate(
25+
),
26+
repositoryResourceContentsHandler(client)
27+
}
28+
29+
// getRepositoryContent defines the resource template and handler for getting repository content for a branch.
30+
funcgetRepositoryResourceBranchContent(client*github.Client,t translations.TranslationHelperFunc) (mcp.ResourceTemplate, server.ResourceTemplateHandlerFunc) {
31+
returnmcp.NewResourceTemplate(
2332
"repo://{owner}/{repo}/refs/heads/{branch}/contents{/path*}",// Resource template
2433
t("RESOURCE_REPOSITORY_CONTENT_BRANCH_DESCRIPTION","Repository Content for specific branch"),
25-
),mcp.NewResourceTemplate(
34+
),
35+
repositoryResourceContentsHandler(client)
36+
}
37+
38+
// getRepositoryResourceCommitContent defines the resource template and handler for getting repository content for a commit.
39+
funcgetRepositoryResourceCommitContent(client*github.Client,t translations.TranslationHelperFunc) (mcp.ResourceTemplate, server.ResourceTemplateHandlerFunc) {
40+
returnmcp.NewResourceTemplate(
2641
"repo://{owner}/{repo}/sha/{sha}/contents{/path*}",// Resource template
2742
t("RESOURCE_REPOSITORY_CONTENT_COMMIT_DESCRIPTION","Repository Content for specific commit"),
28-
),mcp.NewResourceTemplate(
43+
),
44+
repositoryResourceContentsHandler(client)
45+
}
46+
47+
// getRepositoryResourceTagContent defines the resource template and handler for getting repository content for a tag.
48+
funcgetRepositoryResourceTagContent(client*github.Client,t translations.TranslationHelperFunc) (mcp.ResourceTemplate, server.ResourceTemplateHandlerFunc) {
49+
returnmcp.NewResourceTemplate(
2950
"repo://{owner}/{repo}/refs/tags/{tag}/contents{/path*}",// Resource template
3051
t("RESOURCE_REPOSITORY_CONTENT_TAG_DESCRIPTION","Repository Content for specific tag"),
31-
),mcp.NewResourceTemplate(
52+
),
53+
repositoryResourceContentsHandler(client)
54+
}
55+
56+
// getRepositoryResourcePrContent defines the resource template and handler for getting repository content for a pull request.
57+
funcgetRepositoryResourcePrContent(client*github.Client,t translations.TranslationHelperFunc) (mcp.ResourceTemplate, server.ResourceTemplateHandlerFunc) {
58+
returnmcp.NewResourceTemplate(
3259
"repo://{owner}/{repo}/refs/pull/{pr_number}/head/contents{/path*}",// Resource template
3360
t("RESOURCE_REPOSITORY_CONTENT_PR_DESCRIPTION","Repository Content for specific pull request"),
34-
),func(ctx context.Context,request mcp.ReadResourceRequest) ([]mcp.ResourceContents,error) {
35-
// Extract parameters from request.Params.URI
61+
),
62+
repositoryResourceContentsHandler(client)
63+
}
3664

37-
owner:=request.Params.Arguments["owner"].([]string)[0]
38-
repo:=request.Params.Arguments["repo"].([]string)[0]
39-
// path should be a joined list of the path parts
40-
path:=strings.Join(request.Params.Arguments["path"].([]string),"/")
65+
funcrepositoryResourceContentsHandler(client*github.Client)func(ctx context.Context,request mcp.ReadResourceRequest) ([]mcp.ResourceContents,error) {
66+
returnfunc(ctx context.Context,request mcp.ReadResourceRequest) ([]mcp.ResourceContents,error) {
67+
// the matcher will give []string with one elemenent
68+
// https://github.com/mark3labs/mcp-go/pull/54
69+
o,ok:=request.Params.Arguments["owner"].([]string)
70+
if!ok||len(o)==0 {
71+
returnnil,errors.New("owner is required")
72+
}
73+
owner:=o[0]
4174

42-
opts:=&github.RepositoryContentGetOptions{}
75+
r,ok:=request.Params.Arguments["repo"].([]string)
76+
if!ok||len(r)==0 {
77+
returnnil,errors.New("repo is required")
78+
}
79+
repo:=r[0]
4380

44-
sha,ok:=request.Params.Arguments["sha"].([]string)
45-
ifok {
46-
opts.Ref=sha[0]
47-
}
81+
// path should be a joined list of the path parts
82+
path:=""
83+
p,ok:=request.Params.Arguments["path"].([]string)
84+
ifok {
85+
path=strings.Join(p,"/")
86+
}
4887

49-
branch,ok:=request.Params.Arguments["branch"].([]string)
50-
ifok {
51-
opts.Ref="refs/heads/"+branch[0]
52-
}
88+
opts:=&github.RepositoryContentGetOptions{}
5389

54-
tag,ok:=request.Params.Arguments["tag"].([]string)
55-
ifok {
56-
opts.Ref="refs/tags/"+tag[0]
57-
}
58-
prNumber,ok:=request.Params.Arguments["pr_number"].([]string)
59-
ifok {
60-
opts.Ref="refs/pull/"+prNumber[0]+"/head"
61-
}
90+
sha,ok:=request.Params.Arguments["sha"].([]string)
91+
ifok&&len(sha)>0 {
92+
opts.Ref=sha[0]
93+
}
6294

63-
// Use the GitHub client to fetch repository content
64-
fileContent,directoryContent,_,err:=client.Repositories.GetContents(ctx,owner,repo,path,opts)
65-
iferr!=nil {
66-
returnnil,err
67-
}
95+
branch,ok:=request.Params.Arguments["branch"].([]string)
96+
ifok&&len(branch)>0 {
97+
opts.Ref="refs/heads/"+branch[0]
98+
}
99+
100+
tag,ok:=request.Params.Arguments["tag"].([]string)
101+
ifok&&len(tag)>0 {
102+
opts.Ref="refs/tags/"+tag[0]
103+
}
104+
prNumber,ok:=request.Params.Arguments["pr_number"].([]string)
105+
ifok&&len(prNumber)>0 {
106+
opts.Ref="refs/pull/"+prNumber[0]+"/head"
107+
}
68108

69-
ifdirectoryContent!=nil {
70-
// Process the directory content and return it as resource contents
71-
varresources []mcp.ResourceContents
72-
for_,entry:=rangedirectoryContent {
73-
mimeType:="text/directory"
74-
ifentry.GetType()=="file" {
75-
mimeType=mime.TypeByExtension(filepath.Ext(entry.GetName()))
109+
fileContent,directoryContent,_,err:=client.Repositories.GetContents(ctx,owner,repo,path,opts)
110+
iferr!=nil {
111+
returnnil,err
112+
}
113+
114+
ifdirectoryContent!=nil {
115+
varresources []mcp.ResourceContents
116+
for_,entry:=rangedirectoryContent {
117+
mimeType:="text/directory"
118+
ifentry.GetType()=="file" {
119+
// this is system dependent, and a best guess
120+
ext:=filepath.Ext(entry.GetName())
121+
mimeType=mime.TypeByExtension(ext)
122+
ifext==".md" {
123+
mimeType="text/markdown"
76124
}
77-
resources=append(resources, mcp.TextResourceContents{
78-
URI:entry.GetHTMLURL(),
79-
MIMEType:mimeType,
80-
Text:entry.GetName(),
81-
})
125+
}
126+
resources=append(resources, mcp.TextResourceContents{
127+
URI:entry.GetHTMLURL(),
128+
MIMEType:mimeType,
129+
Text:entry.GetName(),
130+
})
131+
132+
}
133+
returnresources,nil
82134

135+
}
136+
iffileContent!=nil {
137+
iffileContent.Content!=nil {
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)
141+
iferr!=nil {
142+
returnnil,fmt.Errorf("failed to create request: %w",err)
83143
}
84-
returnresources,nil
85144

86-
}elseiffileContent!=nil {
87-
// Process the file content and return it as a binary resource
145+
resp,err:=client.Client().Do(req)
146+
iferr!=nil {
147+
returnnil,fmt.Errorf("failed to send request: %w",err)
148+
}
149+
deferfunc() {_=resp.Body.Close() }()
88150

89-
iffileContent.Content!=nil {
90-
decodedContent,err:=fileContent.GetContent()
151+
ifresp.StatusCode!=http.StatusOK {
152+
body,err:=io.ReadAll(resp.Body)
91153
iferr!=nil {
92-
returnnil,err
154+
returnnil,fmt.Errorf("failed to read response body: %w",err)
93155
}
156+
returnnil,fmt.Errorf("failed to fetch file content: %s",string(body))
157+
}
94158

95-
mimeType:=mime.TypeByExtension(filepath.Ext(fileContent.GetName()))
96-
97-
// Check if the file is text-based
98-
ifstrings.HasPrefix(mimeType,"text") {
99-
// Return as TextResourceContents
100-
return []mcp.ResourceContents{
101-
mcp.TextResourceContents{
102-
URI:request.Params.URI,
103-
MIMEType:mimeType,
104-
Text:decodedContent,
105-
},
106-
},nil
159+
ext:=filepath.Ext(fileContent.GetName())
160+
mimeType:=resp.Header.Get("Content-Type")
161+
ifext==".md" {
162+
mimeType="text/markdown"
163+
}elseifmimeType=="" {
164+
// backstop to the file extension if the content type is not set
165+
mimeType=mime.TypeByExtension(filepath.Ext(fileContent.GetName()))
166+
}
167+
168+
// if the content is a string, return it as text
169+
ifstrings.HasPrefix(mimeType,"text") {
170+
content,err:=io.ReadAll(resp.Body)
171+
iferr!=nil {
172+
returnnil,fmt.Errorf("failed to parse the response body: %w",err)
107173
}
108174

109-
// Otherwise, return as BlobResourceContents
110175
return []mcp.ResourceContents{
111-
mcp.BlobResourceContents{
176+
mcp.TextResourceContents{
112177
URI:request.Params.URI,
113178
MIMEType:mimeType,
114-
Blob:base64.StdEncoding.EncodeToString([]byte(decodedContent)),// Encode content as Base64
179+
Text:string(content),
115180
},
116181
},nil
117182
}
118-
}
183+
// otherwise, read the content and encode it as base64
184+
decodedContent,err:=io.ReadAll(resp.Body)
185+
iferr!=nil {
186+
returnnil,fmt.Errorf("failed to parse the response body: %w",err)
187+
}
119188

120-
returnnil,nil
189+
return []mcp.ResourceContents{
190+
mcp.BlobResourceContents{
191+
URI:request.Params.URI,
192+
MIMEType:mimeType,
193+
Blob:base64.StdEncoding.EncodeToString(decodedContent),// Encode content as Base64
194+
},
195+
},nil
196+
}
121197
}
198+
199+
returnnil,errors.New("no repository resource content found")
200+
}
122201
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp