Skip to content

Commit 80a5b77

Browse files
feature: repo resource
1 parent b2e869d commit 80a5b77

File tree

4 files changed

+142
-9
lines changed

4 files changed

+142
-9
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/migueleliasweb/go-github-mock v1.1.0
1010
github.com/sirupsen/logrus v1.9.3
1111
github.com/spf13/cobra v1.9.1
@@ -34,10 +34,11 @@ require (
3434
github.com/spf13/cast v1.6.0 // indirect
3535
github.com/spf13/pflag v1.0.6 // indirect
3636
github.com/subosito/gotenv v1.6.0 // indirect
37+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
3738
go.uber.org/atomic v1.9.0 // indirect
3839
go.uber.org/multierr v1.9.0 // indirect
39-
golang.org/x/sys v0.18.0 // indirect
40-
golang.org/x/text v0.19.0 // indirect
40+
golang.org/x/sys v0.28.0 // indirect
41+
golang.org/x/text v0.21.0 // indirect
4142
golang.org/x/time v0.5.0 // indirect
4243
gopkg.in/ini.v1 v1.67.0 // indirect
4344
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
3232
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
3333
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
3434
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
35-
github.com/mark3labs/mcp-go v0.11.2 h1:mCxWFUTrcXOtJIn9t7F8bxAL8rpE/ZZTTnx3PU/VNdA=
36-
github.com/mark3labs/mcp-go v0.11.2/go.mod h1:cjMlBU0cv/cj9kjlgmRhoJ5JREdS7YX83xeIG9Ko/jE=
35+
github.com/mark3labs/mcp-go v0.14.1 h1:NsieyFbuWQaeZSWSHPvJ5TwJdQwu+1jmivAIVljeouY=
36+
github.com/mark3labs/mcp-go v0.14.1/go.mod h1:xBB350hekQsJAK7gJAii8bcEoWemboLm2mRm5/+KBaU=
3737
github.com/migueleliasweb/go-github-mock v1.1.0 h1:GKaOBPsrPGkAKgtfuWY8MclS1xR6MInkx1SexJucMwE=
3838
github.com/migueleliasweb/go-github-mock v1.1.0/go.mod h1:pYe/XlGs4BGMfRY4vmeixVsODHnVDDhJ9zoi0qzSMHc=
3939
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -77,17 +77,19 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
7777
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7878
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
7979
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
80+
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
81+
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
8082
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
8183
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
8284
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
8385
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
8486
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
8587
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
8688
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
87-
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
88-
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
89-
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
90-
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
89+
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
90+
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
91+
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
92+
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
9193
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
9294
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
9395
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

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
@@ -22,6 +22,15 @@ func NewServer(client *github.Client) *server.MCPServer {
2222
server.WithResourceCapabilities(true, true),
2323
server.WithLogging())
2424

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

0 commit comments

Comments
 (0)