Skip to content

Commit 24c2868

Browse files
committed
feat: Add support for git tag operations
Add git tag functionality including: - List repository tags - Get tag details - Support for tag-based content access This enables basic read-only tag management through the MCP server API.
1 parent 4e26dce commit 24c2868

File tree

3 files changed

+400
-0
lines changed

3 files changed

+400
-0
lines changed

pkg/github/repositories.go

+144
Original file line numberDiff line numberDiff line change
@@ -796,3 +796,147 @@ func PushFiles(getClient GetClientFn, t translations.TranslationHelperFunc) (too
796796
return mcp.NewToolResultText(string(r)), nil
797797
}
798798
}
799+
800+
// ListTags creates a tool to list tags in a GitHub repository.
801+
func ListTags(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
802+
return mcp.NewTool("list_tags",
803+
mcp.WithDescription(t("TOOL_LIST_TAGS_DESCRIPTION", "List tags in a GitHub repository")),
804+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
805+
Title: t("TOOL_LIST_TAGS_USER_TITLE", "List tags"),
806+
ReadOnlyHint: true,
807+
}),
808+
mcp.WithString("owner",
809+
mcp.Required(),
810+
mcp.Description("Repository owner"),
811+
),
812+
mcp.WithString("repo",
813+
mcp.Required(),
814+
mcp.Description("Repository name"),
815+
),
816+
WithPagination(),
817+
),
818+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
819+
owner, err := requiredParam[string](request, "owner")
820+
if err != nil {
821+
return mcp.NewToolResultError(err.Error()), nil
822+
}
823+
repo, err := requiredParam[string](request, "repo")
824+
if err != nil {
825+
return mcp.NewToolResultError(err.Error()), nil
826+
}
827+
pagination, err := OptionalPaginationParams(request)
828+
if err != nil {
829+
return mcp.NewToolResultError(err.Error()), nil
830+
}
831+
832+
opts := &github.ListOptions{
833+
Page: pagination.page,
834+
PerPage: pagination.perPage,
835+
}
836+
837+
client, err := getClient(ctx)
838+
if err != nil {
839+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
840+
}
841+
842+
tags, resp, err := client.Repositories.ListTags(ctx, owner, repo, opts)
843+
if err != nil {
844+
return nil, fmt.Errorf("failed to list tags: %w", err)
845+
}
846+
defer func() { _ = resp.Body.Close() }()
847+
848+
if resp.StatusCode != http.StatusOK {
849+
body, err := io.ReadAll(resp.Body)
850+
if err != nil {
851+
return nil, fmt.Errorf("failed to read response body: %w", err)
852+
}
853+
return mcp.NewToolResultError(fmt.Sprintf("failed to list tags: %s", string(body))), nil
854+
}
855+
856+
r, err := json.Marshal(tags)
857+
if err != nil {
858+
return nil, fmt.Errorf("failed to marshal response: %w", err)
859+
}
860+
861+
return mcp.NewToolResultText(string(r)), nil
862+
}
863+
}
864+
865+
// GetTag creates a tool to get details about a specific tag in a GitHub repository.
866+
func GetTag(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
867+
return mcp.NewTool("get_tag",
868+
mcp.WithDescription(t("TOOL_GET_TAG_DESCRIPTION", "Get details about a specific tag in a GitHub repository")),
869+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
870+
Title: t("TOOL_GET_TAG_USER_TITLE", "Get tag details"),
871+
ReadOnlyHint: true,
872+
}),
873+
mcp.WithString("owner",
874+
mcp.Required(),
875+
mcp.Description("Repository owner"),
876+
),
877+
mcp.WithString("repo",
878+
mcp.Required(),
879+
mcp.Description("Repository name"),
880+
),
881+
mcp.WithString("tag",
882+
mcp.Required(),
883+
mcp.Description("Tag name"),
884+
),
885+
),
886+
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
887+
owner, err := requiredParam[string](request, "owner")
888+
if err != nil {
889+
return mcp.NewToolResultError(err.Error()), nil
890+
}
891+
repo, err := requiredParam[string](request, "repo")
892+
if err != nil {
893+
return mcp.NewToolResultError(err.Error()), nil
894+
}
895+
tag, err := requiredParam[string](request, "tag")
896+
if err != nil {
897+
return mcp.NewToolResultError(err.Error()), nil
898+
}
899+
900+
client, err := getClient(ctx)
901+
if err != nil {
902+
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
903+
}
904+
905+
// First get the tag reference
906+
ref, resp, err := client.Git.GetRef(ctx, owner, repo, "refs/tags/"+tag)
907+
if err != nil {
908+
return nil, fmt.Errorf("failed to get tag reference: %w", err)
909+
}
910+
defer func() { _ = resp.Body.Close() }()
911+
912+
if resp.StatusCode != http.StatusOK {
913+
body, err := io.ReadAll(resp.Body)
914+
if err != nil {
915+
return nil, fmt.Errorf("failed to read response body: %w", err)
916+
}
917+
return mcp.NewToolResultError(fmt.Sprintf("failed to get tag reference: %s", string(body))), nil
918+
}
919+
920+
// Then get the tag object
921+
tagObj, resp, err := client.Git.GetTag(ctx, owner, repo, *ref.Object.SHA)
922+
if err != nil {
923+
return nil, fmt.Errorf("failed to get tag object: %w", err)
924+
}
925+
defer func() { _ = resp.Body.Close() }()
926+
927+
if resp.StatusCode != http.StatusOK {
928+
body, err := io.ReadAll(resp.Body)
929+
if err != nil {
930+
return nil, fmt.Errorf("failed to read response body: %w", err)
931+
}
932+
return mcp.NewToolResultError(fmt.Sprintf("failed to get tag object: %s", string(body))), nil
933+
}
934+
935+
r, err := json.Marshal(tagObj)
936+
if err != nil {
937+
return nil, fmt.Errorf("failed to marshal response: %w", err)
938+
}
939+
940+
return mcp.NewToolResultText(string(r)), nil
941+
}
942+
}

0 commit comments

Comments
 (0)