8000 feature: repo resource · github/github-mcp-server@59ae504 · GitHub
[go: up one dir, main page]

Skip to content

Commit 59ae504

Browse files
feature: repo resource
1 parent 09366fa commit 59ae504

File tree

4 files changed

+138
-3
lines changed

4 files changed

+138
-3
lines changed

go.mod

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.23.7
55
require (
66
github.com/aws/smithy-go v1.22.3
77
github.com/google/go-github/v69 v69.2.0
8-
github.com/mark3labs/mcp-go v0.11.2
8+
github.com/mark3labs/mcp-go v0.14.1
99
github.com/sirupsen/logrus v1.9.3
1010
github.com/spf13/cobra v1.9.1
1111
github.com/spf13/viper v1.19.0
@@ -28,10 +28,11 @@ require (
2828
github.com/spf13/cast v1.6.0 // indirect
2929
github.com/spf13/pflag v1.0.6 // indirect
3030
github.com/subosito/gotenv v1.6.0 // indirect
31+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
3132
go.uber.org/atomic v1.9.0 // indirect
3233
go.uber.org/multierr v1.9.0 // indirect
33-
golang.org/x/sys v0.18.0 // indirect
34-
golang.org/x/text v0.14.0 // indirect
34+
golang.org/x/sys v0.28.0 // indirect
35+
golang.org/x/text v0.21.0 // indirect
3536
gopkg.in/ini.v1 v1.67.0 // indirect
3637
gopkg.in/yaml.v3 v3.0.1 // indirect
3738
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
3030
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
3131
github.com/mark3labs/mcp-go v0.11.2 h1:mCxWFUTrcXOtJIn9t7F8bxAL8rpE/ZZTTnx3PU/VNdA=
3232
github.com/mark3labs/mcp-go v0.11.2/go.mod h1:cjMlBU0cv/cj9kjlgmRhoJ5JREdS7YX83xeIG9Ko/jE=
33+
github.com/mark3labs/mcp-go v0.14.0 h1:/bASI77oZbDKTQoCIxxPFu+UKn0o6OeA9C3cBrbapxM=
34+
github.com/mark3labs/mcp-go v0.14.0/go.mod h1:xBB350hekQsJAK7gJAii8bcEoWemboLm2mRm5/+KBaU=
3335
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
3436
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
3537
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
@@ -71,6 +73,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
7173
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7274
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
7375
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
76+
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
77+
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
7478
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
7579
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
7680
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=

pkg/github/repository_resource.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
"mime"
7+
"path/filepath"
8+
"strings"
9+
10+
"github.com/google/go-github/v69/github"
11+
"github.com/mark3labs/mcp-go/mcp"
12+
"github.com/mark3labs/mcp-go/server"
13+
)
14+
15+
// getRepositoryContent defines the resource template and handler for the Repository Content API.
16+
func getRepositoryContent(client *github.Client) (mainTemplate mcp.ResourceTemplate, reftemplate mcp.ResourceTemplate, shaTemplate mcp.ResourceTemplate, tagTemplate mcp.ResourceTemplate, prTemplate mcp.ResourceTemplate, handler server.ResourceTemplateHandlerFunc) {
17+
18+
return mcp.NewResourceTemplate(
19+
"repo://{owner}/{repo}/contents{/path*}", // Resource template
20+
"Repository Content", // Description
21+
), mcp.NewResourceTemplate(
22+
"repo://{owner}/{repo}/refs/heads/{branch}/contents{/path*}", // Resource template
23+
"Repository Content for specific branch", // Description
24+
), mcp.NewResourceTemplate(
25+
"repo://{owner}/{repo}/sha/{sha}/contents{/path*}", // Resource template
26+
"Repository Content for specific commit", // Description
27+
), mcp.NewResourceTemplate(
28+
"repo://{owner}/{repo}/refs/tags/{tag}/contents{/path*}", // Resource template
29+
"Repository Content for specific tag", // Description
30+
), mcp.NewResourceTemplate(
31+
"repo://{owner}/{repo}/refs/pull/{pr_number}/head/contents{/path*}", // Resource template
32+
"Repository Content for specific pull request", // Description
33+
), func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
34+
// Extract parameters from request.Params.URI
35+
36+
owner := request.Params.Arguments["owner"].([]string)[0]
37+
repo := request.Params.Arguments["repo"].([]string)[0]
38+
// path should be a joined list of the path parts
39+
path := strings.Join(request.Params.Arguments["path"].([]string), "/")
40+
41+
opts := &github.RepositoryContentGetOptions{}
42+
43+
sha, ok := request.Params.Arguments["sha"].([]string)
44+
if ok {
45+
opts.Ref = sha[0]
46+
}
47+
48+
branch, ok := request.Params.Arguments["branch"].([]string)
49+
if ok {
50+
opts.Ref = "refs/heads/" + branch[0]
51+
}
52+
53+
tag, ok := request.Params.Arguments["tag"].([]string)
54+
if ok {
55+
opts.Ref = "refs/tags/" + tag[0]
56+
}
57+
prNumber, ok := request.Params.Arguments["pr_number"].([]string)
58+
if ok {
59+
opts.Ref = "refs/pull/" + prNumber[0] + "/head"
60+
}
61+
62+
// Use the GitHub client to fetch repository content
63+
fileContent, directoryContent, _, err := client.Repositories.GetContents(ctx, owner, repo, path, opts)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
if directoryContent != nil {
69+
// Process the directory content and return it as resource contents
70+
var resources []mcp.ResourceContents
71+
for _, entry := range directoryContent {
72+
mimeType := "text/directory"
73+
if entry.GetType() == "file" {
74+
mimeType = mime.TypeByExtension(filepath.Ext(entry.GetName()))
75+
}
76+
resources = append(resources, mcp.TextResourceContents{
77+
URI: entry.GetHTMLURL(),
78+
MIMEType: mimeType,
79+
Text: entry.GetName(),
80+
})
81+
82+
}
83+
return resources, nil
84+
85+
} else if fileContent != nil {
86+
// Process the file content and return it as a binary resource
87+
88+
if fileContent.Content != nil {
89+
decodedContent, err := fileContent.GetContent()
90+
if err != nil {
91+
return nil, err
92+
}
93+
94+
mimeType := mime.TypeByExtension(filepath.Ext(fileContent.GetName()))
95+
96+
// Check if the file is text-based
97+
if strings.HasPrefix(mimeType, "text") {
98+
// Return as TextResourceContents
99+
return []mcp.ResourceContents{
100+
mcp.TextResourceContents{
101+
URI: request.Params.URI,
102+
MIMEType: mimeType,
103+
Text: decodedContent,
104+
},
105+
}, nil
106+
}
107+
108+
// Otherwise, return as BlobResourceContents
109+
return []mcp.ResourceContents{
110+
mcp.BlobResourceContents{
111+
URI: request.Params.URI,
112+
MIMEType: mimeType,
113+
Blob: base64.StdEncoding.EncodeToString([]byte(decodedContent)), // Encode content as Base64
114+
},
115+
}, nil
116+
}
117+
}
118+
119+
return nil, nil
120+
}
121+
}

pkg/github/server.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ func NewServer(client *github.Client) *server.MCPServer {
2020
server.WithResourceCapabilities(true, true),
2121
server.WithLogging())
2222

23+
// Add GitHub Resources
24+
defaultTemplate, branchTemplate, tagTemplate, shaTemplate, prTemplate, handler := getRepositoryContent(client)
25+
26+
s.AddResourceTemplate(defaultTemplate, handler)
27+
s.AddResourceTemplate(branchTemplate, handler)
28+
s.AddResourceTemplate(tagTemplate, handler)
29+
s.AddResourceTemplate(shaTemplate, handler)
30+
s.AddResourceTemplate(prTemplate, handler)
31+
2332
// Add GitHub tools - Issues
2433
s.AddTool(getIssue(client))
2534
s.AddTool(addIssueComment(client))

0 commit comments

Comments
 (0)
0