Content-Length: 28456 | pFad | http://github.com/github/github-mcp-server/pull/650.patch

thub.com From 3269af42718e7c5c5d9ec7af679223f34d350576 Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Mon, 7 Jul 2025 16:56:24 +0100 Subject: [PATCH 1/9] add contingency to match path in git tree --- pkg/github/repositories.go | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index cf71a5839..2100c7f78 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -610,6 +610,31 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t } return mcp.NewToolResultText(string(r)), nil } + + // The path does not point to a file or directory. + // Instead let's try to find it in the Git Tree by matching the end of the path. + + // Step 1: Get Git Tree recursively + tree, resp, err := client.Git.GetTree(ctx, owner, repo, ref, true) + if err != nil { + return ghErrors.NewGitHubAPIErrorResponse(ctx, + "failed to get git tree", + resp, + err, + ), nil + } + + // Step 2: Filter tree for matching paths + const maxMatchingFiles = 3 + matchingFiles := filterPaths(tree.Entries, path, maxMatchingFiles) + if len(matchingFiles) > 0 { + matchingFilesJSON, err := json.Marshal(matchingFiles) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to marshal matching files: %s", err)), nil + } + return mcp.NewToolResultText(fmt.Sprintf("Provided path did not match a file or directory, but possible matches are: %s", matchingFilesJSON)), nil + } + return mcp.NewToolResultError("Failed to get file contents. The path does not point to a file or directory, or the file does not exist in the repository."), nil } } @@ -1293,3 +1318,22 @@ func GetTag(getClient GetClientFn, t translations.TranslationHelperFunc) (tool m return mcp.NewToolResultText(string(r)), nil } } + +// filterPaths filters the entries in a GitHub tree to find paths that +// match the given suffix. +func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []string { + matchedPaths := []string{} + for _, entry := range entries { + if len(matchedPaths) == maxResults { + break // Limit the number of results to maxResults + } + entryPath := entry.GetPath() + if entryPath == "" { + continue // Skip empty paths + } + if strings.HasSuffix(entryPath, path) { + matchedPaths = append(matchedPaths, entryPath) + } + } + return matchedPaths +} From be2f36f79e0c135808368f40b05d2275f6560f7e Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Tue, 8 Jul 2025 00:59:37 +0100 Subject: [PATCH 2/9] resolveGitReference helper --- pkg/github/repositories.go | 77 ++++++++++++---------- pkg/github/repositories_test.go | 110 +++++++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 34 deletions(-) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 2100c7f78..1dc63dc12 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -8,7 +8,6 @@ import ( "io" "net/http" "net/url" - "strconv" "strings" ghErrors "github.com/github/github-mcp-server/pkg/errors" @@ -495,33 +494,18 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t return mcp.NewToolResultError(err.Error()), nil } - rawOpts := &raw.ContentOpts{} - - if strings.HasPrefix(ref, "refs/pull/") { - prNumber := strings.TrimSuffix(strings.TrimPrefix(ref, "refs/pull/"), "/head") - if 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 = "" - } + client, err := getClient(ctx) + if err != nil { + return mcp.NewToolResultError("failed to get GitHub client"), nil } - rawOpts.SHA = sha - rawOpts.Ref = ref + rawOpts, err := resolveGitReference(ctx, client, owner, repo, ref, sha) + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } - // 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 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, "/") { rawClient, err := getRawClient(ctx) @@ -580,13 +564,8 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t } } - client, err := getClient(ctx) - if err != nil { - return mcp.NewToolResultError("failed to get GitHub client"), nil - } - - if sha != "" { - ref = sha + if rawOpts.SHA != "" { + ref = rawOpts.SHA } if strings.HasSuffix(path, "/") { opts := &github.RepositoryContentGetOptions{Ref: ref} @@ -632,7 +611,7 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t if err != nil { return mcp.NewToolResultError(fmt.Sprintf("failed to marshal matching files: %s", err)), nil } - return mcp.NewToolResultText(fmt.Sprintf("Provided path did not match a file or directory, but possible matches are: %s", matchingFilesJSON)), nil + return mcp.NewToolResultText(fmt.Sprintf("Provided path did not match a file or directory, but resolved ref to %s with possible path matches: %s", ref, matchingFilesJSON)), nil } return mcp.NewToolResultError("Failed to get file contents. The path does not point to a file or directory, or the file does not exist in the repository."), nil @@ -1337,3 +1316,35 @@ func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []str } return matchedPaths } + +// resolveGitReference resolves git references with the following logic: +// 1. If SHA is provided, it takes precedence +// 2. If neither is provided, use the default branch as ref +// 3. Get SHA from the ref +// Refs can look like `refs/tags/{tag}`, `refs/heads/{branch}` or `refs/pull/{pr_number}/head` +// The function returns the resolved ref, SHA and any error. +func resolveGitReference(ctx context.Context, githubClient *github.Client, owner, repo, ref, sha string) (*raw.ContentOpts, error) { + // 1. If SHA is provided, use it directly + if sha != "" { + return &raw.ContentOpts{Ref: "", SHA: sha}, nil + } + + // 2. If neither provided, use the default branch as ref + if ref == "" { + repoInfo, _, err := githubClient.Repositories.Get(ctx, owner, repo) + if err != nil { + return nil, fmt.Errorf("failed to get repository info: %w", err) + } + ref = fmt.Sprintf("refs/heads/%s", repoInfo.GetDefaultBranch()) + } + + // 3. Get the SHA from the ref + reference, _, err := githubClient.Git.GetRef(ctx, owner, repo, ref) + if err != nil { + return nil, fmt.Errorf("failed to get reference for default branch: %w", err) + } + sha = reference.GetObject().GetSHA() + + // Use provided ref, or it will be empty which defaults to the default branch + return &raw.ContentOpts{Ref: ref, SHA: sha}, nil +} diff --git a/pkg/github/repositories_test.go b/pkg/github/repositories_test.go index b621cec43..c81796899 100644 --- a/pkg/github/repositories_test.go +++ b/pkg/github/repositories_test.go @@ -69,6 +69,13 @@ func Test_GetFileContents(t *testing.T) { { name: "successful text content fetch", mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposGitRefByOwnerByRepoByRef, + http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"ref": "refs/heads/main", "object": {"sha": ""}}`)) + }), + ), mock.WithRequestMatchHandler( raw.GetRawReposContentsByOwnerByRepoByBranchByPath, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { @@ -93,6 +100,13 @@ func Test_GetFileContents(t *testing.T) { { name: "successful file blob content fetch", mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposGitRefByOwnerByRepoByRef, + http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"ref": "refs/heads/main", "object": {"sha": ""}}`)) + }), + ), mock.WithRequestMatchHandler( raw.GetRawReposContentsByOwnerByRepoByBranchByPath, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { @@ -117,6 +131,20 @@ func Test_GetFileContents(t *testing.T) { { name: "successful directory content fetch", mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposByOwnerByRepo, + http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"name": "repo", "default_branch": "main"}`)) + }), + ), + mock.WithRequestMatchHandler( + mock.GetReposGitRefByOwnerByRepoByRef, + http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"ref": "refs/heads/main", "object": {"sha": ""}}`)) + }), + ), mock.WithRequestMatchHandler( mock.GetReposContentsByOwnerByRepoByPath, expectQueryParams(t, map[string]string{}).andThen( @@ -143,6 +171,13 @@ func Test_GetFileContents(t *testing.T) { { name: "content fetch fails", mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposGitRefByOwnerByRepoByRef, + http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"ref": "refs/heads/main", "object": {"sha": ""}}`)) + }), + ), mock.WithRequestMatchHandler( mock.GetReposContentsByOwnerByRepoByPath, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { @@ -203,7 +238,7 @@ func Test_GetFileContents(t *testing.T) { textContent := getTextResult(t, result) var returnedContents []*github.RepositoryContent err = json.Unmarshal([]byte(textContent.Text), &returnedContents) - require.NoError(t, err) + require.NoError(t, err, "Failed to unmarshal directory content result: %v", textContent.Text) assert.Len(t, returnedContents, len(expected)) for i, content := range returnedContents { assert.Equal(t, *expected[i].Name, *content.Name) @@ -2049,3 +2084,76 @@ func Test_GetTag(t *testing.T) { }) } } + +func Test_ResolveGitReference(t *testing.T) { + + ctx := context.Background() + owner := "owner" + repo := "repo" + mockedClient := mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposByOwnerByRepo, + http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"name": "repo", "default_branch": "main"}`)) + }), + ), + mock.WithRequestMatchHandler( + mock.GetReposGitRefByOwnerByRepoByRef, + http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"ref": "refs/heads/main", "object": {"sha": "123sha456"}}`)) + }), + ), + ) + + tests := []struct { + name string + ref string + sha string + expectedOutput *raw.ContentOpts + }{ + { + name: "sha takes precedence over ref", + ref: "refs/heads/main", + sha: "123sha456", + expectedOutput: &raw.ContentOpts{ + SHA: "123sha456", + }, + }, + { + name: "use default branch if ref and sha both empty", + ref: "", + sha: "", + expectedOutput: &raw.ContentOpts{ + Ref: "refs/heads/main", + SHA: "123sha456", + }, + }, + { + name: "get SHA from ref", + ref: "refs/heads/main", + sha: "", + expectedOutput: &raw.ContentOpts{ + Ref: "refs/heads/main", + SHA: "123sha456", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Setup client with mock + client := github.NewClient(mockedClient) + opts, err := resolveGitReference(ctx, client, owner, repo, tc.ref, tc.sha) + require.NoError(t, err) + + if tc.expectedOutput.SHA != "" { + assert.Equal(t, tc.expectedOutput.SHA, opts.SHA) + } + if tc.expectedOutput.Ref != "" { + assert.Equal(t, tc.expectedOutput.Ref, opts.Ref) + } + }) + } +} From 37a6088f0bd8702580726e70d5ea1b60c05a4e22 Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Tue, 8 Jul 2025 01:34:39 +0100 Subject: [PATCH 3/9] fix: handling of directories --- pkg/github/repositories.go | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 1dc63dc12..44cbd2cf4 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -570,24 +570,14 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t if strings.HasSuffix(path, "/") { 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 - } - defer func() { _ = resp.Body.Close() }() - - if resp.StatusCode != 200 { - body, err := io.ReadAll(resp.Body) + if err == nil && resp.StatusCode == http.StatusOK { + defer func() { _ = resp.Body.Close() }() + r, err := json.Marshal(dirContent) if err != nil { - return mcp.NewToolResultError("failed to read response body"), nil + return mcp.NewToolResultError("failed to marshal response"), nil } - return mcp.NewToolResultError(fmt.Sprintf("failed to get file contents: %s", string(body))), nil + return mcp.NewToolResultText(string(r)), nil } - - r, err := json.Marshal(dirContent) - if err != nil { - return mcp.NewToolResultError("failed to marshal response"), nil - } - return mcp.NewToolResultText(string(r)), nil } // The path does not point to a file or directory. @@ -1301,6 +1291,8 @@ func GetTag(getClient GetClientFn, t translations.TranslationHelperFunc) (tool m // filterPaths filters the entries in a GitHub tree to find paths that // match the given suffix. func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []string { + path = strings.TrimSuffix(path, "/") // Normalize path to avoid double slashes + matchedPaths := []string{} for _, entry := range entries { if len(matchedPaths) == maxResults { @@ -1311,6 +1303,9 @@ func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []str continue // Skip empty paths } if strings.HasSuffix(entryPath, path) { + if entry.GetType() == "tree" { + entryPath += "/" // show directories with a trailing slash + } matchedPaths = append(matchedPaths, entryPath) } } From 588866f031be4395b7404d9c734649e1ee9e3807 Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Tue, 8 Jul 2025 09:28:32 +0100 Subject: [PATCH 4/9] Test_filterPaths --- pkg/github/repositories.go | 4 +++ pkg/github/repositories_test.go | 43 ++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 44cbd2cf4..869d3c162 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -1290,6 +1290,10 @@ func GetTag(getClient GetClientFn, t translations.TranslationHelperFunc) (tool m // filterPaths filters the entries in a GitHub tree to find paths that // match the given suffix. +// maxResults limits the number of results returned to first maxResults entries, +// a maxResults of -1 means no limit. +// It returns a slice of strings containing the matching paths. +// Directories are returned with a trailing slash. func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []string { path = strings.TrimSuffix(path, "/") // Normalize path to avoid double slashes diff --git a/pkg/github/repositories_test.go b/pkg/github/repositories_test.go index c81796899..55c3aa088 100644 --- a/pkg/github/repositories_test.go +++ b/pkg/github/repositories_test.go @@ -2085,8 +2085,49 @@ func Test_GetTag(t *testing.T) { } } -func Test_ResolveGitReference(t *testing.T) { +func Test_filterPaths(t *testing.T) { + tests := []struct { + name string + tree []*github.TreeEntry + path string + maxResults int + expected []string + }{ + { + name: "file name", + tree: []*github.TreeEntry{ + {Path: github.Ptr("folder/foo.txt"), Type: github.Ptr("blob")}, + {Path: github.Ptr("bar.txt"), Type: github.Ptr("blob")}, + {Path: github.Ptr("nested/folder/foo.txt"), Type: github.Ptr("blob")}, + {Path: github.Ptr("nested/folder/baz.txt"), Type: github.Ptr("blob")}, + }, + path: "foo.txt", + maxResults: -1, + expected: []string{"folder/foo.txt", "nested/folder/foo.txt"}, + }, + { + name: "dir name", + tree: []*github.TreeEntry{ + {Path: github.Ptr("folder"), Type: github.Ptr("tree")}, + {Path: github.Ptr("bar.txt"), Type: github.Ptr("blob")}, + {Path: github.Ptr("nested/folder"), Type: github.Ptr("tree")}, + {Path: github.Ptr("nested/folder/baz.txt"), Type: github.Ptr("blob")}, + }, + path: "folder/", + maxResults: -1, + expected: []string{"folder/", "nested/folder/"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result := filterPaths(tc.tree, tc.path, tc.maxResults) + assert.Equal(t, tc.expected, result) + }) + } +} +func Test_resolveGitReference(t *testing.T) { ctx := context.Background() owner := "owner" repo := "repo" From 1cc6f7c5b9f62aebb715c5426d72b5688e6ec1ef Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Tue, 8 Jul 2025 09:36:32 +0100 Subject: [PATCH 5/9] filterPaths - trailing slashes --- pkg/github/repositories.go | 13 +++++++++++-- pkg/github/repositories_test.go | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 869d3c162..ffcd1c7c7 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -1295,20 +1295,29 @@ func GetTag(getClient GetClientFn, t translations.TranslationHelperFunc) (tool m // It returns a slice of strings containing the matching paths. // Directories are returned with a trailing slash. func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []string { - path = strings.TrimSuffix(path, "/") // Normalize path to avoid double slashes + // Remove trailing slash for matching purposes, but flag whether we + // only want directories. + dirOnly := false + if strings.HasSuffix(path, "/") { + dirOnly = true + path = strings.TrimSuffix(path, "/") + } matchedPaths := []string{} for _, entry := range entries { if len(matchedPaths) == maxResults { break // Limit the number of results to maxResults } + if dirOnly && entry.GetType() != "tree" { + continue // Skip non-directory entries if dirOnly is true + } entryPath := entry.GetPath() if entryPath == "" { continue // Skip empty paths } if strings.HasSuffix(entryPath, path) { if entry.GetType() == "tree" { - entryPath += "/" // show directories with a trailing slash + entryPath += "/" // Return directories with a trailing slash } matchedPaths = append(matchedPaths, entryPath) } diff --git a/pkg/github/repositories_test.go b/pkg/github/repositories_test.go index 55c3aa088..81d80a2e5 100644 --- a/pkg/github/repositories_test.go +++ b/pkg/github/repositories_test.go @@ -2117,6 +2117,26 @@ func Test_filterPaths(t *testing.T) { maxResults: -1, expected: []string{"folder/", "nested/folder/"}, }, + { + name: "dir and file match", + tree: []*github.TreeEntry{ + {Path: github.Ptr("name"), Type: github.Ptr("tree")}, + {Path: github.Ptr("name"), Type: github.Ptr("blob")}, + }, + path: "name", // No trailing slash can match both files and directories + maxResults: -1, + expected: []string{"name/", "name"}, + }, + { + name: "dir only match", + tree: []*github.TreeEntry{ + {Path: github.Ptr("name"), Type: github.Ptr("tree")}, + {Path: github.Ptr("name"), Type: github.Ptr("blob")}, + }, + path: "name/", // Trialing slash ensures only directories are matched + maxResults: -1, + expected: []string{"name/"}, + }, } for _, tc := range tests { From 12c2cfe59ec8401759cc1d1e7b83a2279456c705 Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Tue, 8 Jul 2025 12:50:56 +0100 Subject: [PATCH 6/9] fix: close response body, improve error messages, docs --- 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 ffcd1c7c7..d33b51dae 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -501,7 +501,7 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t rawOpts, err := resolveGitReference(ctx, client, owner, repo, ref, sha) if err != nil { - return mcp.NewToolResultError(err.Error()), nil + return mcp.NewToolResultError(fmt.Sprintf("failed to resolve git reference: %s", err)), nil } // If the path is (most likely) not to be a directory, we will @@ -592,6 +592,7 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t err, ), nil } + defer func() { _ = resp.Body.Close() }() // Step 2: Filter tree for matching paths const maxMatchingFiles = 3 @@ -601,7 +602,7 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t if err != nil { return mcp.NewToolResultError(fmt.Sprintf("failed to marshal matching files: %s", err)), nil } - return mcp.NewToolResultText(fmt.Sprintf("Provided path did not match a file or directory, but resolved ref to %s with possible path matches: %s", ref, matchingFilesJSON)), nil + return mcp.NewToolResultText(fmt.Sprintf("Path did not point to a file or directory, but resolved ref to %s with possible path matches: %s", ref, matchingFilesJSON)), nil } return mcp.NewToolResultError("Failed to get file contents. The path does not point to a file or directory, or the file does not exist in the repository."), nil @@ -1328,9 +1329,9 @@ func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []str // resolveGitReference resolves git references with the following logic: // 1. If SHA is provided, it takes precedence // 2. If neither is provided, use the default branch as ref -// 3. Get SHA from the ref +// 3. Get commit SHA from the ref // Refs can look like `refs/tags/{tag}`, `refs/heads/{branch}` or `refs/pull/{pr_number}/head` -// The function returns the resolved ref, SHA and any error. +// The function returns the resolved ref, commit SHA and any error. func resolveGitReference(ctx context.Context, githubClient *github.Client, owner, repo, ref, sha string) (*raw.ContentOpts, error) { // 1. If SHA is provided, use it directly if sha != "" { From 8c40155f5ce07826587fc70f2101ea7e37d2ca76 Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Tue, 8 Jul 2025 13:07:16 +0100 Subject: [PATCH 7/9] update tool result message about resolved git ref --- pkg/github/repositories.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index d33b51dae..f32d40fe5 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -602,7 +602,11 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t if err != nil { return mcp.NewToolResultError(fmt.Sprintf("failed to marshal matching files: %s", err)), nil } - return mcp.NewToolResultText(fmt.Sprintf("Path did not point to a file or directory, but resolved ref to %s with possible path matches: %s", ref, matchingFilesJSON)), nil + resolvedRefs, err := json.Marshal(rawOpts) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("failed to marshal resolved refs: %s", err)), nil + } + return mcp.NewToolResultText(fmt.Sprintf("Path did not point to a file or directory, but resolved git ref to %s with possible path matches: %s", resolvedRefs, matchingFilesJSON)), nil } return mcp.NewToolResultError("Failed to get file contents. The path does not point to a file or directory, or the file does not exist in the repository."), nil From bf673f8d07a108c4b3276b9a2beec1f93fe4d8ae Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Tue, 8 Jul 2025 13:37:58 +0100 Subject: [PATCH 8/9] unit test cases for filterPaths maxResults param --- pkg/github/repositories_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pkg/github/repositories_test.go b/pkg/github/repositories_test.go index 81d80a2e5..0b9c5d9f9 100644 --- a/pkg/github/repositories_test.go +++ b/pkg/github/repositories_test.go @@ -2137,6 +2137,39 @@ func Test_filterPaths(t *testing.T) { maxResults: -1, expected: []string{"name/"}, }, + { + name: "max results limit 2", + tree: []*github.TreeEntry{ + {Path: github.Ptr("folder"), Type: github.Ptr("tree")}, + {Path: github.Ptr("nested/folder"), Type: github.Ptr("tree")}, + {Path: github.Ptr("nested/nested/folder"), Type: github.Ptr("tree")}, + }, + path: "folder/", + maxResults: 2, + expected: []string{"folder/", "nested/folder/"}, + }, + { + name: "max results limit 1", + tree: []*github.TreeEntry{ + {Path: github.Ptr("folder"), Type: github.Ptr("tree")}, + {Path: github.Ptr("nested/folder"), Type: github.Ptr("tree")}, + {Path: github.Ptr("nested/nested/folder"), Type: github.Ptr("tree")}, + }, + path: "folder/", + maxResults: 1, + expected: []string{"folder/"}, + }, + { + name: "max results limit 0", + tree: []*github.TreeEntry{ + {Path: github.Ptr("folder"), Type: github.Ptr("tree")}, + {Path: github.Ptr("nested/folder"), Type: github.Ptr("tree")}, + {Path: github.Ptr("nested/nested/folder"), Type: github.Ptr("tree")}, + }, + path: "folder/", + maxResults: 0, + expected: []string{}, + }, } for _, tc := range tests { From 3aea320515696610aabdb207ce4c687023cf1a06 Mon Sep 17 00:00:00 2001 From: LuluBeatson Date: Fri, 11 Jul 2025 16:04:21 +0100 Subject: [PATCH 9/9] resolveGitReference - NewGitHubAPIErrorToCtx --- pkg/github/repositories.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index b1fce6beb..732f20ab1 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -1344,17 +1344,19 @@ func resolveGitReference(ctx context.Context, githubClient *github.Client, owner // 2. If neither provided, use the default branch as ref if ref == "" { - repoInfo, _, err := githubClient.Repositories.Get(ctx, owner, repo) + repoInfo, resp, err := githubClient.Repositories.Get(ctx, owner, repo) if err != nil { + _, _ = ghErrors.NewGitHubAPIErrorToCtx(ctx, "failed to get repository info", resp, err) return nil, fmt.Errorf("failed to get repository info: %w", err) } ref = fmt.Sprintf("refs/heads/%s", repoInfo.GetDefaultBranch()) } // 3. Get the SHA from the ref - reference, _, err := githubClient.Git.GetRef(ctx, owner, repo, ref) + reference, resp, err := githubClient.Git.GetRef(ctx, owner, repo, ref) if err != nil { - return nil, fmt.Errorf("failed to get reference for default branch: %w", err) + _, _ = ghErrors.NewGitHubAPIErrorToCtx(ctx, "failed to get reference", resp, err) + return nil, fmt.Errorf("failed to get reference: %w", err) } sha = reference.GetObject().GetSHA()








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/github/github-mcp-server/pull/650.patch

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy