Skip to content

Commit 9e8d8b4

Browse files
committed
move into git.go
1 parent 8297183 commit 9e8d8b4

File tree

3 files changed

+161
-141
lines changed

3 files changed

+161
-141
lines changed

pkg/github/git.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"strings"
8+
9+
ghErrors "github.com/github/github-mcp-server/pkg/errors"
10+
"github.com/github/github-mcp-server/pkg/translations"
11+
"github.com/google/go-github/v74/github"
12+
"github.com/mark3labs/mcp-go/mcp"
13+
"github.com/mark3labs/mcp-go/server"
14+
)
15+
16+
// TreeEntryResponse represents a single entry in a Git tree.
17+
type TreeEntryResponse struct {
18+
Path string `json:"path"`
19+
Type string `json:"type"`
20+
Size *int `json:"size,omitempty"`
21+
Mode string `json:"mode"`
22+
SHA string `json:"sha"`
23+
URL string `json:"url"`
24+
}
25+
26+
// TreeResponse represents the response structure for a Git tree.
27+
type TreeResponse struct {
28+
SHA string `json:"sha"`
29+
Truncated bool `json:"truncated"`
30+
Tree []TreeEntryResponse `json:"tree"`
31+
TreeSHA string `json:"tree_sha"`
32+
Owner string `json:"owner"`
33+
Repo string `json:"repo"`
34+
Recursive bool `json:"recursive"`
35+
Count int `json:"count"`
36+
}
37+
38+
// GetRepositoryTree creates a tool to get the tree structure of a GitHub repository.
39+
func GetRepositoryTree(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
40+
return mcp.NewTool("get_repository_tree",
41+
mcp.WithDescription(t("TOOL_GET_REPOSITORY_TREE_DESCRIPTION", "Get the tree structure (files and directories) of a GitHub repository at a specific ref or SHA")),
42+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
43+
Title: t("TOOL_GET_REPOSITORY_TREE_USER_TITLE", "Get repository tree"),
44+
ReadOnlyHint: ToBoolPtr(true),
45+
}),
46+
mcp.WithString("owner",
47+
mcp.Required(),
48+
mcp.Description("Repository owner (username or organization)"),
49+
),
50+
mcp.WithString("repo",
51+
mcp.Required(),
52+
mcp.Description("Repository name"),
53+
),
54+
mcp.WithString("tree_sha",
55+
mcp.Description("The SHA1 value or ref (branch or tag) name of the tree. Defaults to the repository's default branch"),
56+
),
57+
mcp.WithBoolean("recursive",
58+
mcp.Description("Setting this parameter to true returns the objects or subtrees referenced by the tree. Default is false"),
59+
mcp.DefaultBool(false),
60+
),
61+
mcp.WithString("path_filter",
62+
mcp.Description("Optional path prefix to filter the tree results (e.g., 'src/' to only show files in the src directory)"),
63+
),
64+
),
65+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
66+
owner, err := RequiredParam[string](request, "owner")
67+
if err != nil {
68+
return mcp.NewToolResultError(err.Error()), nil
69+
}
70+
repo, err := RequiredParam[string](request, "repo")
71+
if err != nil {
72+
return mcp.NewToolResultError(err.Error()), nil
73+
}
74+
treeSHA, err := OptionalParam[string](request, "tree_sha")
75+
if err != nil {
76+
return mcp.NewToolResultError(err.Error()), nil
77+
}
78+
recursive, err := OptionalBoolParamWithDefault(request, "recursive", false)
79+
if err != nil {
80+
return mcp.NewToolResultError(err.Error()), nil
81+
}
82+
pathFilter, err := OptionalParam[string](request, "path_filter")
83+
if err != nil {
84+
return mcp.NewToolResultError(err.Error()), nil
85+
}
86+
87+
client, err := getClient(ctx)
88+
if err != nil {
89+
return mcp.NewToolResultError("failed to get GitHub client"), nil
90+
}
91+
92+
// If no tree_sha is provided, use the repository's default branch
93+
if treeSHA == "" {
94+
repoInfo, _, err := client.Repositories.Get(ctx, owner, repo)
95+
if err != nil {
96+
return mcp.NewToolResultError(fmt.Sprintf("failed to get repository info: %s", err)), nil
97+
}
98+
treeSHA = *repoInfo.DefaultBranch
99+
}
100+
101+
// Get the tree using the GitHub Git Tree API
102+
tree, resp, err := client.Git.GetTree(ctx, owner, repo, treeSHA, recursive)
103+
if err != nil {
104+
return ghErrors.NewGitHubAPIErrorResponse(ctx,
105+
"failed to get repository tree",
106+
resp,
107+
err,
108+
), nil
109+
}
110+
defer func() { _ = resp.Body.Close() }()
111+
112+
// Filter tree entries if path_filter is provided
113+
var filteredEntries []*github.TreeEntry
114+
if pathFilter != "" {
115+
for _, entry := range tree.Entries {
116+
if strings.HasPrefix(entry.GetPath(), pathFilter) {
117+
filteredEntries = append(filteredEntries, entry)
118+
}
119+
}
120+
} else {
121+
filteredEntries = tree.Entries
122+
}
123+
124+
treeEntries := make([]TreeEntryResponse, len(filteredEntries))
125+
for i, entry := range filteredEntries {
126+
treeEntries[i] = TreeEntryResponse{
127+
Path: entry.GetPath(),
128+
Type: entry.GetType(),
129+
Mode: entry.GetMode(),
130+
SHA: entry.GetSHA(),
131+
URL: entry.GetURL(),
132+
}
133+
if entry.Size != nil {
134+
treeEntries[i].Size = entry.Size
135+
}
136+
}
137+
138+
response := TreeResponse{
139+
SHA: *tree.SHA,
140+
Truncated: *tree.Truncated,
141+
Tree: treeEntries,
142+
TreeSHA: treeSHA,
143+
Owner: owner,
144+
Repo: repo,
145+
Recursive: recursive,
146+
Count: len(filteredEntries),
147+
}
148+
149+
r, err := json.Marshal(response)
150+
if err != nil {
151+
return nil, fmt.Errorf("failed to marshal response: %w", err)
152+
}
153+
154+
return mcp.NewToolResultText(string(r)), nil
155+
}
156+
}

pkg/github/repositories.go

Lines changed: 0 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -677,146 +677,6 @@ func GetFileContents(getClient GetClientFn, getRawClient raw.GetRawClientFn, t t
677677
}
678678
}
679679

680-
// GetRepositoryTree creates a tool to get the tree structure of a GitHub repository.
681-
func GetRepositoryTree(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
682-
return mcp.NewTool("get_repository_tree",
683-
mcp.WithDescription(t("TOOL_GET_REPOSITORY_TREE_DESCRIPTION", "Get the tree structure (files and directories) of a GitHub repository at a specific ref or SHA")),
684-
mcp.WithToolAnnotation(mcp.ToolAnnotation{
685-
Title: t("TOOL_GET_REPOSITORY_TREE_USER_TITLE", "Get repository tree"),
686-
ReadOnlyHint: ToBoolPtr(true),
687-
}),
688-
mcp.WithString("owner",
689-
mcp.Required(),
690-
mcp.Description("Repository owner (username or organization)"),
691-
),
692-
mcp.WithString("repo",
693-
mcp.Required(),
694-
mcp.Description("Repository name"),
695-
),
696-
mcp.WithString("tree_sha",
697-
mcp.Description("The SHA1 value or ref (branch or tag) name of the tree. Defaults to the repository's default branch"),
698-
),
699-
mcp.WithBoolean("recursive",
700-
mcp.Description("Setting this parameter to true returns the objects or subtrees referenced by the tree. Default is false"),
701-
mcp.DefaultBool(false),
702-
),
703-
mcp.WithString("path_filter",
704-
mcp.Description("Optional path prefix to filter the tree results (e.g., 'src/' to only show files in the src directory)"),
705-
),
706-
),
707-
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
708-
owner, err := RequiredParam[string](request, "owner")
709-
if err != nil {
710-
return mcp.NewToolResultError(err.Error()), nil
711-
}
712-
repo, err := RequiredParam[string](request, "repo")
713-
if err != nil {
714-
return mcp.NewToolResultError(err.Error()), nil
715-
}
716-
treeSHA, err := OptionalParam[string](request, "tree_sha")
717-
if err != nil {
718-
return mcp.NewToolResultError(err.Error()), nil
719-
}
720-
recursive, err := OptionalBoolParamWithDefault(request, "recursive", false)
721-
if err != nil {
722-
return mcp.NewToolResultError(err.Error()), nil
723-
}
724-
pathFilter, err := OptionalParam[string](request, "path_filter")
725-
if err != nil {
726-
return mcp.NewToolResultError(err.Error()), nil
727-
}
728-
729-
client, err := getClient(ctx)
730-
if err != nil {
731-
return mcp.NewToolResultError("failed to get GitHub client"), nil
732-
}
733-
734-
// If no tree_sha is provided, use the repository's default branch
735-
if treeSHA == "" {
736-
repoInfo, _, err := client.Repositories.Get(ctx, owner, repo)
737-
if err != nil {
738-
return mcp.NewToolResultError(fmt.Sprintf("failed to get repository info: %s", err)), nil
739-
}
740-
treeSHA = *repoInfo.DefaultBranch
741-
}
742-
743-
// Get the tree using the GitHub Git Tree API
744-
tree, resp, err := client.Git.GetTree(ctx, owner, repo, treeSHA, recursive)
745-
if err != nil {
746-
return ghErrors.NewGitHubAPIErrorResponse(ctx,
747-
"failed to get repository tree",
748-
resp,
749-
err,
750-
), nil
751-
}
752-
defer func() { _ = resp.Body.Close() }()
753-
754-
// Filter tree entries if path_filter is provided
755-
var filteredEntries []*github.TreeEntry
756-
if pathFilter != "" {
757-
for _, entry := range tree.Entries {
758-
if strings.HasPrefix(entry.GetPath(), pathFilter) {
759-
filteredEntries = append(filteredEntries, entry)
760-
}
761-
}
762-
} else {
763-
filteredEntries = tree.Entries
764-
}
765-
766-
type TreeEntryResponse struct {
767-
Path string `json:"path"`
768-
Type string `json:"type"`
769-
Size *int `json:"size,omitempty"`
770-
Mode string `json:"mode"`
771-
SHA string `json:"sha"`
772-
URL string `json:"url"`
773-
}
774-
775-
type TreeResponse struct {
776-
SHA string `json:"sha"`
777-
Truncated bool `json:"truncated"`
778-
Tree []TreeEntryResponse `json:"tree"`
779-
TreeSHA string `json:"tree_sha"`
780-
Owner string `json:"owner"`
781-
Repo string `json:"repo"`
782-
Recursive bool `json:"recursive"`
783-
Count int `json:"count"`
784-
}
785-
786-
treeEntries := make([]TreeEntryResponse, len(filteredEntries))
787-
for i, entry := range filteredEntries {
788-
treeEntries[i] = TreeEntryResponse{
789-
Path: entry.GetPath(),
790-
Type: entry.GetType(),
791-
Mode: entry.GetMode(),
792-
SHA: entry.GetSHA(),
793-
URL: entry.GetURL(),
794-
}
795-
if entry.Size != nil {
796-
treeEntries[i].Size = entry.Size
797-
}
798-
}
799-
800-
response := TreeResponse{
801-
SHA: *tree.SHA,
802-
Truncated: *tree.Truncated,
803-
Tree: treeEntries,
804-
TreeSHA: treeSHA,
805-
Owner: owner,
806-
Repo: repo,
807-
Recursive: recursive,
808-
Count: len(filteredEntries),
809-
}
810-
811-
r, err := json.Marshal(response)
812-
if err != nil {
813-
return nil, fmt.Errorf("failed to marshal response: %w", err)
814-
}
815-
816-
return mcp.NewToolResultText(string(r)), nil
817-
}
818-
}
819-
820680
// ForkRepository creates a tool to fork a repository.
821681
func ForkRepository(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
822682
return mcp.NewTool("fork_repository",

pkg/github/tools.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ func DefaultToolsetGroup(readOnly bool, getClient GetClientFn, getGQLClient GetG
2525
AddReadTools(
2626
toolsets.NewServerTool(SearchRepositories(getClient, t)),
2727
toolsets.NewServerTool(GetFileContents(getClient, getRawClient, t)),
28-
toolsets.NewServerTool(GetRepositoryTree(getClient, t)),
2928
toolsets.NewServerTool(ListCommits(getClient, t)),
3029
toolsets.NewServerTool(SearchCode(getClient, t)),
3130
toolsets.NewServerTool(GetCommit(getClient, t)),
@@ -54,6 +53,10 @@ func DefaultToolsetGroup(readOnly bool, getClient GetClientFn, getGQLClient GetG
5453
toolsets.NewServerResourceTemplate(GetRepositoryResourceTagContent(getClient, getRawClient, t)),
5554
toolsets.NewServerResourceTemplate(GetRepositoryResourcePrContent(getClient, getRawClient, t)),
5655
)
56+
git := toolsets.NewToolset("git", "GitHub Git API related tools for low-level Git operations").
57+
AddReadTools(
58+
toolsets.NewServerTool(GetRepositoryTree(getClient, t)),
59+
)
5760
issues := toolsets.NewToolset("issues", "GitHub Issues related tools").
5861
AddReadTools(
5962
toolsets.NewServerTool(GetIssue(getClient, t)),
@@ -199,6 +202,7 @@ func DefaultToolsetGroup(readOnly bool, getClient GetClientFn, getGQLClient GetG
199202
// Add toolsets to the group
200203
tsg.AddToolset(contextTools)
201204
tsg.AddToolset(repos)
205+
tsg.AddToolset(git)
202206
tsg.AddToolset(issues)
203207
tsg.AddToolset(orgs)
204208
tsg.AddToolset(users)

0 commit comments

Comments
 (0)