Skip to content

Commit 57facbc

Browse files
feature: repo resource
1 parent 09366fa commit 57facbc

File tree

4 files changed

+126
-1
lines changed

4 files changed

+126
-1
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
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.0
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,6 +28,7 @@ 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
3334
golang.org/x/sys v0.18.0 // indirect

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

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)