From 80a5b775614146e066e991471db0256c411d1352 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Mon, 17 Mar 2025 21:42:05 +0100 Subject: [PATCH 1/4] feature: repo resource --- go.mod | 7 +- go.sum | 14 ++-- pkg/github/repository_resource.go | 121 ++++++++++++++++++++++++++++++ pkg/github/server.go | 9 +++ 4 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 pkg/github/repository_resource.go diff --git a/go.mod b/go.mod index 4338a69d3..0bb0ee9a9 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.7 require ( github.com/aws/smithy-go v1.22.3 github.com/google/go-github/v69 v69.2.0 - github.com/mark3labs/mcp-go v0.11.2 + github.com/mark3labs/mcp-go v0.14.1 github.com/migueleliasweb/go-github-mock v1.1.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.9.1 @@ -34,10 +34,11 @@ require ( github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 5aa0482da..24df4db99 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mark3labs/mcp-go v0.11.2 h1:mCxWFUTrcXOtJIn9t7F8bxAL8rpE/ZZTTnx3PU/VNdA= -github.com/mark3labs/mcp-go v0.11.2/go.mod h1:cjMlBU0cv/cj9kjlgmRhoJ5JREdS7YX83xeIG9Ko/jE= +github.com/mark3labs/mcp-go v0.14.1 h1:NsieyFbuWQaeZSWSHPvJ5TwJdQwu+1jmivAIVljeouY= +github.com/mark3labs/mcp-go v0.14.1/go.mod h1:xBB350hekQsJAK7gJAii8bcEoWemboLm2mRm5/+KBaU= github.com/migueleliasweb/go-github-mock v1.1.0 h1:GKaOBPsrPGkAKgtfuWY8MclS1xR6MInkx1SexJucMwE= github.com/migueleliasweb/go-github-mock v1.1.0/go.mod h1:pYe/XlGs4BGMfRY4vmeixVsODHnVDDhJ9zoi0qzSMHc= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -77,6 +77,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= @@ -84,10 +86,10 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/github/repository_resource.go b/pkg/github/repository_resource.go new file mode 100644 index 000000000..dd8597e38 --- /dev/null +++ b/pkg/github/repository_resource.go @@ -0,0 +1,121 @@ +package github + +import ( + "context" + "encoding/base64" + "mime" + "path/filepath" + "strings" + + "github.com/google/go-github/v69/github" + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" +) + +// getRepositoryContent defines the resource template and handler for the Repository Content API. +func getRepositoryContent(client *github.Client) (mainTemplate mcp.ResourceTemplate, reftemplate mcp.ResourceTemplate, shaTemplate mcp.ResourceTemplate, tagTemplate mcp.ResourceTemplate, prTemplate mcp.ResourceTemplate, handler server.ResourceTemplateHandlerFunc) { + + return mcp.NewResourceTemplate( + "repo://{owner}/{repo}/contents{/path*}", // Resource template + "Repository Content", // Description + ), mcp.NewResourceTemplate( + "repo://{owner}/{repo}/refs/heads/{branch}/contents{/path*}", // Resource template + "Repository Content for specific branch", // Description + ), mcp.NewResourceTemplate( + "repo://{owner}/{repo}/sha/{sha}/contents{/path*}", // Resource template + "Repository Content for specific commit", // Description + ), mcp.NewResourceTemplate( + "repo://{owner}/{repo}/refs/tags/{tag}/contents{/path*}", // Resource template + "Repository Content for specific tag", // Description + ), mcp.NewResourceTemplate( + "repo://{owner}/{repo}/refs/pull/{pr_number}/head/contents{/path*}", // Resource template + "Repository Content for specific pull request", // Description + ), func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) { + // Extract parameters from request.Params.URI + + owner := request.Params.Arguments["owner"].([]string)[0] + repo := request.Params.Arguments["repo"].([]string)[0] + // path should be a joined list of the path parts + path := strings.Join(request.Params.Arguments["path"].([]string), "/") + + opts := &github.RepositoryContentGetOptions{} + + sha, ok := request.Params.Arguments["sha"].([]string) + if ok { + opts.Ref = sha[0] + } + + branch, ok := request.Params.Arguments["branch"].([]string) + if ok { + opts.Ref = "refs/heads/" + branch[0] + } + + tag, ok := request.Params.Arguments["tag"].([]string) + if ok { + opts.Ref = "refs/tags/" + tag[0] + } + prNumber, ok := request.Params.Arguments["pr_number"].([]string) + if ok { + opts.Ref = "refs/pull/" + prNumber[0] + "/head" + } + + // Use the GitHub client to fetch repository content + fileContent, directoryContent, _, err := client.Repositories.GetContents(ctx, owner, repo, path, opts) + if err != nil { + return nil, err + } + + if directoryContent != nil { + // Process the directory content and return it as resource contents + var resources []mcp.ResourceContents + for _, entry := range directoryContent { + mimeType := "text/directory" + if entry.GetType() == "file" { + mimeType = mime.TypeByExtension(filepath.Ext(entry.GetName())) + } + resources = append(resources, mcp.TextResourceContents{ + URI: entry.GetHTMLURL(), + MIMEType: mimeType, + Text: entry.GetName(), + }) + + } + return resources, nil + + } else if fileContent != nil { + // Process the file content and return it as a binary resource + + if fileContent.Content != nil { + decodedContent, err := fileContent.GetContent() + if err != nil { + return nil, err + } + + mimeType := mime.TypeByExtension(filepath.Ext(fileContent.GetName())) + + // Check if the file is text-based + if strings.HasPrefix(mimeType, "text") { + // Return as TextResourceContents + return []mcp.ResourceContents{ + mcp.TextResourceContents{ + URI: request.Params.URI, + MIMEType: mimeType, + Text: decodedContent, + }, + }, nil + } + + // Otherwise, return as BlobResourceContents + return []mcp.ResourceContents{ + mcp.BlobResourceContents{ + URI: request.Params.URI, + MIMEType: mimeType, + Blob: base64.StdEncoding.EncodeToString([]byte(decodedContent)), // Encode content as Base64 + }, + }, nil + } + } + + return nil, nil + } +} diff --git a/pkg/github/server.go b/pkg/github/server.go index 0a90b4d1b..c804ca284 100644 --- a/pkg/github/server.go +++ b/pkg/github/server.go @@ -22,6 +22,15 @@ func NewServer(client *github.Client) *server.MCPServer { server.WithResourceCapabilities(true, true), server.WithLogging()) + // Add GitHub Resources + defaultTemplate, branchTemplate, tagTemplate, shaTemplate, prTemplate, handler := getRepositoryContent(client) + + s.AddResourceTemplate(defaultTemplate, handler) + s.AddResourceTemplate(branchTemplate, handler) + s.AddResourceTemplate(tagTemplate, handler) + s.AddResourceTemplate(shaTemplate, handler) + s.AddResourceTemplate(prTemplate, handler) + // Add GitHub tools - Issues s.AddTool(getIssue(client)) s.AddTool(addIssueComment(client)) From 9f436b9bf2fca714fe1cc647609f590d00403b64 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Tue, 18 Mar 2025 17:42:10 +0100 Subject: [PATCH 2/4] fix get-me --- script/get-me | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/get-me b/script/get-me index fa6675a8f..46339ae53 100755 --- a/script/get-me +++ b/script/get-me @@ -1,3 +1,3 @@ #!/bin/bash -echo '{"jsonrpc":"2.0","id":3,"params":{"name":"get_me"},"method":"tools/call"}' | go run cmd/server/main.go stdio | jq . +echo '{"jsonrpc":"2.0","id":3,"params":{"name":"get_me"},"method":"tools/call"}' | go run cmd/github-mcp-server/main.go stdio | jq . From 7d0684a045df05318ca4cae225c76198cfa872e6 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Tue, 18 Mar 2025 17:44:43 +0100 Subject: [PATCH 3/4] update readme --- README.md | 76 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c68eda2fb..63525e5e1 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,59 @@ and set it as the GITHUB_PERSONAL_ACCESS_TOKEN environment variable. - `state`: Alert state (string, optional) - `severity`: Alert severity (string, optional) +## Resources + +### Repository Content + +- **Get Repository Content** + Retrieves the content of a repository at a specific path. + + - **Template**: `repo://{owner}/{repo}/contents{/path*}` + - **Parameters**: + - `owner`: Repository owner (string, required) + - `repo`: Repository name (string, required) + - `path`: File or directory path (string, optional) + +- **Get Repository Content for a Specific Branch** + Retrieves the content of a repository at a specific path for a given branch. + + - **Template**: `repo://{owner}/{repo}/refs/heads/{branch}/contents{/path*}` + - **Parameters**: + - `owner`: Repository owner (string, required) + - `repo`: Repository name (string, required) + - `branch`: Branch name (string, required) + - `path`: File or directory path (string, optional) + +- **Get Repository Content for a Specific Commit** + Retrieves the content of a repository at a specific path for a given commit. + + - **Template**: `repo://{owner}/{repo}/sha/{sha}/contents{/path*}` + - **Parameters**: + - `owner`: Repository owner (string, required) + - `repo`: Repository name (string, required) + - `sha`: Commit SHA (string, required) + - `path`: File or directory path (string, optional) + +- **Get Repository Content for a Specific Tag** + Retrieves the content of a repository at a specific path for a given tag. + + - **Template**: `repo://{owner}/{repo}/refs/tags/{tag}/contents{/path*}` + - **Parameters**: + - `owner`: Repository owner (string, required) + - `repo`: Repository name (string, required) + - `tag`: Tag name (string, required) + - `path`: File or directory path (string, optional) + +- **Get Repository Content for a Specific Pull Request** + Retrieves the content of a repository at a specific path for a given pull request. + + - **Template**: `repo://{owner}/{repo}/refs/pull/{pr_number}/head/contents{/path*}` + - **Parameters**: + - `owner`: Repository owner (string, required) + - `repo`: Repository name (string, required) + - `pr_number`: Pull request number (number, required) + - `path`: File or directory path (string, optional) + ## Standard input/output server ```sh @@ -216,7 +269,7 @@ GitHub MCP Server running on stdio ## Testing on VS Code Insiders -First of all, install `github-mcp-server` with: +First of all, install `github-mcp-server` with: ```bash go install ./cmd/github-mcp-server @@ -231,18 +284,16 @@ Go to settings, find the MCP related settings, and set them to: ```json { - "mcp": { - "inputs": [], - "servers": { - "mcp-github-server": { - "command": "path-to-your/github-mcp-server", - "args": [ - "stdio" - ], - "env": {} - } - } + "mcp": { + "inputs": [], + "servers": { + "mcp-github-server": { + "command": "path-to-your/github-mcp-server", + "args": ["stdio"], + "env": {} + } } + } } ``` @@ -255,7 +306,6 @@ Try something like the following prompt to verify that it works: I'd like to know more about my GitHub profile. ``` - ## TODO Lots of things! From 7e8e73024e067018380d4b618b73f1d233090d2c Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Tue, 18 Mar 2025 17:51:53 +0100 Subject: [PATCH 4/4] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 63525e5e1..16c534863 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ and set it as the GITHUB_PERSONAL_ACCESS_TOKEN environment variable. - **Parameters**: - `owner`: Repository owner (string, required) - `repo`: Repository name (string, required) - - `pr_number`: Pull request number (number, required) + - `pr_number`: Pull request number (string, required) - `path`: File or directory path (string, optional) ## Standard input/output server 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