diff --git a/README.md b/README.md index c68eda2fb..16c534863 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 (string, 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! 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)) 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 . 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