From 57b19566efdf58d9bc9072baaad8b62695b8111f Mon Sep 17 00:00:00 2001 From: tonytrg Date: Fri, 20 Jun 2025 16:48:16 +0200 Subject: [PATCH 1/2] get_file_contents accepts a ref instead of just branch --- pkg/github/repositories.go | 61 +++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 3475167b..ee8e3879 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "net/url" + "strconv" "strings" "github.com/github/github-mcp-server/pkg/raw" @@ -432,8 +433,11 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t mcp.Required(), mcp.Description("Path to file/directory (directories must end with a slash '/')"), ), - mcp.WithString("branch", - mcp.Description("Branch to get contents from"), + mcp.WithString("ref", + mcp.Description("Accepts optional git refs such as `refs/tags/`, `refs/heads/` or `refs/pull//head`"), + ), + mcp.WithString("sha", + mcp.Description("Accepts optional git sha, if sha is specified it will be used instead of ref"), ), ), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -449,17 +453,44 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t if err != nil { return mcp.NewToolResultError(err.Error()), nil } - branch, err := OptionalParam[string](request, "branch") + ref, err := OptionalParam[string](request, "ref") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } + sha, err := OptionalParam[string](request, "sha") if err != nil { return mcp.NewToolResultError(err.Error()), nil } + rawOpts := &raw.RawContentOpts{} + + if strings.HasPrefix(path, "refs/pull/") { + prNumber, ok := strings.CutPrefix(path, "refs/pull/") + if ok && len(prNumber) > 0 { + // fetch the PR from the API to get the latest commit and use SHA + githubClient, err := getClient(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get GitHub client: %w", err) + } + prNum, err := strconv.Atoi(prNumber) + if err != nil { + return nil, fmt.Errorf("invalid pull request number: %w", err) + } + pr, _, err := githubClient.PullRequests.Get(ctx, owner, repo, prNum) + if err != nil { + return nil, fmt.Errorf("failed to get pull request: %w", err) + } + sha = pr.GetHead().GetSHA() + ref = "" + } + } + + rawOpts.SHA = sha + rawOpts.Ref = ref + // If the path is (most likely) not to be a directory, we will first try to get the raw content from the GitHub raw content API. if path != "" && !strings.HasSuffix(path, "/") { - rawOpts := &raw.RawContentOpts{} - if branch != "" { - rawOpts.Ref = "refs/heads/" + branch - } + rawClient, err := getRawClient(ctx) if err != nil { return mcp.NewToolResultError("failed to get GitHub raw content client"), nil @@ -483,14 +514,19 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t contentType := resp.Header.Get("Content-Type") var resourceURI string - if branch == "" { + if sha != "" { // do a safe url join - resourceURI, err = url.JoinPath("repo://", owner, repo, "contents", path) + resourceURI, err = url.JoinPath("repo://", owner, repo, "sha", sha, "contents", path) + if err != nil { + return nil, fmt.Errorf("failed to create resource URI: %w", err) + } + } else if ref != "" { + resourceURI, err = url.JoinPath("repo://", owner, repo, ref, "contents", path) if err != nil { return nil, fmt.Errorf("failed to create resource URI: %w", err) } } else { - resourceURI, err = url.JoinPath("repo://", owner, repo, "refs", "heads", branch, "contents", path) + resourceURI, err = url.JoinPath("repo://", owner, repo, "contents", path) if err != nil { return nil, fmt.Errorf("failed to create resource URI: %w", err) } @@ -517,8 +553,11 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t return mcp.NewToolResultError("failed to get GitHub client"), nil } + if sha != "" { + ref = sha + } if strings.HasSuffix(path, "/") { - opts := &github.RepositoryContentGetOptions{Ref: branch} + opts := &github.RepositoryContentGetOptions{Ref: ref} _, dirContent, resp, err := client.Repositories.GetContents(ctx, owner, repo, path, opts) if err != nil { return mcp.NewToolResultError("failed to get file contents"), nil From 785108e6882486a44366c306f21f3086696bd4ec Mon Sep 17 00:00:00 2001 From: tonytrg Date: Fri, 20 Jun 2025 16:51:05 +0200 Subject: [PATCH 2/2] fix linter --- pkg/github/repositories.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index ee8e3879..8660281b 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -514,23 +514,24 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t contentType := resp.Header.Get("Content-Type") var resourceURI string - if sha != "" { - // do a safe url join + switch { + case sha != "": resourceURI, err = url.JoinPath("repo://", owner, repo, "sha", sha, "contents", path) if err != nil { return nil, fmt.Errorf("failed to create resource URI: %w", err) } - } else if ref != "" { + case ref != "": resourceURI, err = url.JoinPath("repo://", owner, repo, ref, "contents", path) if err != nil { return nil, fmt.Errorf("failed to create resource URI: %w", err) } - } else { + default: resourceURI, err = url.JoinPath("repo://", owner, repo, "contents", path) if err != nil { return nil, fmt.Errorf("failed to create resource URI: %w", err) } } + if strings.HasPrefix(contentType, "application") || strings.HasPrefix(contentType, "text") { return mcp.NewToolResultResource("successfully downloaded text file", mcp.TextResourceContents{ URI: resourceURI,