194 lines
6.4 KiB
Go
194 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
func registerRefTools(server *mcp.Server, api *giteaAPI) {
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "list_branches",
|
|
Description: "List branches in a repository.",
|
|
}, listBranchesHandler(api))
|
|
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "get_branch",
|
|
Description: "Get details for a single branch including its tip commit SHA.",
|
|
}, getBranchHandler(api))
|
|
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "list_tags",
|
|
Description: "List git tags in a repository, newest first.",
|
|
}, listTagsHandler(api))
|
|
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "get_tag",
|
|
Description: "Get details for a single tag.",
|
|
}, getTagHandler(api))
|
|
}
|
|
|
|
type branch struct {
|
|
Name string `json:"name"`
|
|
CommitSHA string `json:"commit_sha"`
|
|
CommitURL string `json:"commit_url,omitempty"`
|
|
Protected bool `json:"protected,omitempty"`
|
|
RequiredApprov int64 `json:"required_approvals,omitempty"`
|
|
}
|
|
type tag struct {
|
|
Name string `json:"name"`
|
|
CommitSHA string `json:"commit_sha"`
|
|
Message string `json:"message,omitempty"`
|
|
ZipballURL string `json:"zipball_url,omitempty"`
|
|
TarballURL string `json:"tarball_url,omitempty"`
|
|
}
|
|
|
|
type listBranchesInput struct {
|
|
Owner string `json:"owner"`
|
|
Repo string `json:"repo"`
|
|
Limit int `json:"limit,omitempty"`
|
|
}
|
|
type listBranchesOutput struct {
|
|
Branches []branch `json:"branches"`
|
|
}
|
|
|
|
func listBranchesHandler(api *giteaAPI) mcp.ToolHandlerFor[listBranchesInput, listBranchesOutput] {
|
|
return func(ctx context.Context, _ *mcp.CallToolRequest, in listBranchesInput) (*mcp.CallToolResult, listBranchesOutput, error) {
|
|
if in.Owner == "" || in.Repo == "" {
|
|
return nil, listBranchesOutput{}, fmt.Errorf("owner and repo are required")
|
|
}
|
|
limit := clampInt(in.Limit, 1, 50, 50)
|
|
q := url.Values{"limit": []string{strconv.Itoa(limit)}}
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/branches",
|
|
url.PathEscape(in.Owner), url.PathEscape(in.Repo))
|
|
var raw []struct {
|
|
Name string `json:"name"`
|
|
Commit struct{ ID, URL string } `json:"commit"`
|
|
Protected bool `json:"protected"`
|
|
RequiredApprovals int64 `json:"required_approvals"`
|
|
}
|
|
if err := api.get(ctx, path, q, &raw); err != nil {
|
|
return nil, listBranchesOutput{}, err
|
|
}
|
|
out := listBranchesOutput{Branches: make([]branch, 0, len(raw))}
|
|
for _, b := range raw {
|
|
out.Branches = append(out.Branches, branch{
|
|
Name: b.Name, CommitSHA: b.Commit.ID, CommitURL: b.Commit.URL,
|
|
Protected: b.Protected, RequiredApprov: b.RequiredApprovals,
|
|
})
|
|
}
|
|
return nil, out, nil
|
|
}
|
|
}
|
|
|
|
type getBranchInput struct {
|
|
Owner string `json:"owner"`
|
|
Repo string `json:"repo"`
|
|
Branch string `json:"branch"`
|
|
}
|
|
type getBranchOutput struct {
|
|
Branch branch `json:"branch"`
|
|
}
|
|
|
|
func getBranchHandler(api *giteaAPI) mcp.ToolHandlerFor[getBranchInput, getBranchOutput] {
|
|
return func(ctx context.Context, _ *mcp.CallToolRequest, in getBranchInput) (*mcp.CallToolResult, getBranchOutput, error) {
|
|
if in.Owner == "" || in.Repo == "" || in.Branch == "" {
|
|
return nil, getBranchOutput{}, fmt.Errorf("owner, repo, and branch are required")
|
|
}
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/branches/%s",
|
|
url.PathEscape(in.Owner), url.PathEscape(in.Repo), url.PathEscape(in.Branch))
|
|
var raw struct {
|
|
Name string `json:"name"`
|
|
Commit struct{ ID, URL string } `json:"commit"`
|
|
Protected bool `json:"protected"`
|
|
RequiredApprovals int64 `json:"required_approvals"`
|
|
}
|
|
if err := api.get(ctx, path, nil, &raw); err != nil {
|
|
return nil, getBranchOutput{}, err
|
|
}
|
|
return nil, getBranchOutput{Branch: branch{
|
|
Name: raw.Name, CommitSHA: raw.Commit.ID, CommitURL: raw.Commit.URL,
|
|
Protected: raw.Protected, RequiredApprov: raw.RequiredApprovals,
|
|
}}, nil
|
|
}
|
|
}
|
|
|
|
type listTagsInput struct {
|
|
Owner string `json:"owner"`
|
|
Repo string `json:"repo"`
|
|
Limit int `json:"limit,omitempty"`
|
|
}
|
|
type listTagsOutput struct {
|
|
Tags []tag `json:"tags"`
|
|
}
|
|
|
|
func listTagsHandler(api *giteaAPI) mcp.ToolHandlerFor[listTagsInput, listTagsOutput] {
|
|
return func(ctx context.Context, _ *mcp.CallToolRequest, in listTagsInput) (*mcp.CallToolResult, listTagsOutput, error) {
|
|
if in.Owner == "" || in.Repo == "" {
|
|
return nil, listTagsOutput{}, fmt.Errorf("owner and repo are required")
|
|
}
|
|
limit := clampInt(in.Limit, 1, 50, 50)
|
|
q := url.Values{"limit": []string{strconv.Itoa(limit)}}
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/tags",
|
|
url.PathEscape(in.Owner), url.PathEscape(in.Repo))
|
|
var raw []struct {
|
|
Name string `json:"name"`
|
|
Message string `json:"message"`
|
|
ZipballURL string `json:"zipball_url"`
|
|
TarballURL string `json:"tarball_url"`
|
|
Commit struct {
|
|
SHA string `json:"sha"`
|
|
} `json:"commit"`
|
|
}
|
|
if err := api.get(ctx, path, q, &raw); err != nil {
|
|
return nil, listTagsOutput{}, err
|
|
}
|
|
out := listTagsOutput{Tags: make([]tag, 0, len(raw))}
|
|
for _, t := range raw {
|
|
out.Tags = append(out.Tags, tag{
|
|
Name: t.Name, CommitSHA: t.Commit.SHA, Message: t.Message,
|
|
ZipballURL: t.ZipballURL, TarballURL: t.TarballURL,
|
|
})
|
|
}
|
|
return nil, out, nil
|
|
}
|
|
}
|
|
|
|
type getTagInput struct {
|
|
Owner string `json:"owner"`
|
|
Repo string `json:"repo"`
|
|
Tag string `json:"tag"`
|
|
}
|
|
type getTagOutput struct {
|
|
Tag tag `json:"tag"`
|
|
}
|
|
|
|
func getTagHandler(api *giteaAPI) mcp.ToolHandlerFor[getTagInput, getTagOutput] {
|
|
return func(ctx context.Context, _ *mcp.CallToolRequest, in getTagInput) (*mcp.CallToolResult, getTagOutput, error) {
|
|
if in.Owner == "" || in.Repo == "" || in.Tag == "" {
|
|
return nil, getTagOutput{}, fmt.Errorf("owner, repo, and tag are required")
|
|
}
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/tags/%s",
|
|
url.PathEscape(in.Owner), url.PathEscape(in.Repo), url.PathEscape(in.Tag))
|
|
var raw struct {
|
|
Name string `json:"name"`
|
|
Message string `json:"message"`
|
|
ZipballURL string `json:"zipball_url"`
|
|
TarballURL string `json:"tarball_url"`
|
|
Commit struct {
|
|
SHA string `json:"sha"`
|
|
} `json:"commit"`
|
|
}
|
|
if err := api.get(ctx, path, nil, &raw); err != nil {
|
|
return nil, getTagOutput{}, err
|
|
}
|
|
return nil, getTagOutput{Tag: tag{
|
|
Name: raw.Name, CommitSHA: raw.Commit.SHA, Message: raw.Message,
|
|
ZipballURL: raw.ZipballURL, TarballURL: raw.TarballURL,
|
|
}}, nil
|
|
}
|
|
}
|