Rename the fork from gitea-mcp to gitea-mcp-extended to reflect the significantly expanded tool coverage (299 vs upstream's 93 tools). - Rename Go module path and all import references - Rename binary to gitea-mcp-extended in Makefile, Dockerfile, .gitignore - Point .goreleaser.yaml gitea_urls to git.lethalbits.com - Replace release-tag workflow with goreleaser + Generic Package Registry publishing - Replace release-nightly workflow with cross-platform build + nightly package publishing - Update CLAUDE.md project description and tool count
158 lines
5.5 KiB
Go
158 lines
5.5 KiB
Go
package repo
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.lethalbits.com/lethalbits/gitea-mcp-extended/pkg/gitea"
|
|
"git.lethalbits.com/lethalbits/gitea-mcp-extended/pkg/log"
|
|
"git.lethalbits.com/lethalbits/gitea-mcp-extended/pkg/to"
|
|
|
|
gitea_sdk "code.gitea.io/sdk/gitea"
|
|
"github.com/mark3labs/mcp-go/mcp"
|
|
"github.com/mark3labs/mcp-go/server"
|
|
)
|
|
|
|
const (
|
|
ListRepoTopicsToolName = "list_repo_topics"
|
|
SetRepoTopicsToolName = "set_repo_topics"
|
|
AddRepoTopicToolName = "add_repo_topic"
|
|
DeleteRepoTopicToolName = "delete_repo_topic"
|
|
)
|
|
|
|
var (
|
|
ListRepoTopicsTool = mcp.NewTool(
|
|
ListRepoTopicsToolName,
|
|
mcp.WithDescription("List a repository's topics"),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
|
)
|
|
|
|
SetRepoTopicsTool = mcp.NewTool(
|
|
SetRepoTopicsToolName,
|
|
mcp.WithDescription("Replace all topics of a repository with a new list"),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
|
mcp.WithString("topics", mcp.Required(), mcp.Description("comma-separated list of topics")),
|
|
)
|
|
|
|
AddRepoTopicTool = mcp.NewTool(
|
|
AddRepoTopicToolName,
|
|
mcp.WithDescription("Add a topic to a repository"),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
|
mcp.WithString("topic", mcp.Required(), mcp.Description("topic name to add")),
|
|
)
|
|
|
|
DeleteRepoTopicTool = mcp.NewTool(
|
|
DeleteRepoTopicToolName,
|
|
mcp.WithDescription("Remove a topic from a repository"),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
|
mcp.WithString("topic", mcp.Required(), mcp.Description("topic name to remove")),
|
|
)
|
|
)
|
|
|
|
func init() {
|
|
Tool.RegisterRead(server.ServerTool{Tool: ListRepoTopicsTool, Handler: ListRepoTopicsFn})
|
|
Tool.RegisterWrite(server.ServerTool{Tool: SetRepoTopicsTool, Handler: SetRepoTopicsFn})
|
|
Tool.RegisterWrite(server.ServerTool{Tool: AddRepoTopicTool, Handler: AddRepoTopicFn})
|
|
Tool.RegisterWrite(server.ServerTool{Tool: DeleteRepoTopicTool, Handler: DeleteRepoTopicFn})
|
|
}
|
|
|
|
func ListRepoTopicsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called ListRepoTopicsFn")
|
|
owner, _ := req.GetArguments()["owner"].(string)
|
|
repo, _ := req.GetArguments()["repo"].(string)
|
|
if owner == "" || repo == "" {
|
|
return to.ErrorResult(errors.New("owner and repo are required"))
|
|
}
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
topics, _, err := client.ListRepoTopics(owner, repo, gitea_sdk.ListRepoTopicsOptions{})
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("list repo topics err: %v", err))
|
|
}
|
|
return to.TextResult(topics)
|
|
}
|
|
|
|
func SetRepoTopicsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called SetRepoTopicsFn")
|
|
owner, _ := req.GetArguments()["owner"].(string)
|
|
repo, _ := req.GetArguments()["repo"].(string)
|
|
topicsStr, _ := req.GetArguments()["topics"].(string)
|
|
if owner == "" || repo == "" {
|
|
return to.ErrorResult(errors.New("owner and repo are required"))
|
|
}
|
|
var topics []string
|
|
if topicsStr != "" {
|
|
for _, t := range splitAndTrim(topicsStr) {
|
|
if t != "" {
|
|
topics = append(topics, t)
|
|
}
|
|
}
|
|
}
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
_, err = client.SetRepoTopics(owner, repo, topics)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("set repo topics err: %v", err))
|
|
}
|
|
return to.TextResult(map[string]any{"status": "updated", "topics": topics})
|
|
}
|
|
|
|
func AddRepoTopicFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called AddRepoTopicFn")
|
|
owner, _ := req.GetArguments()["owner"].(string)
|
|
repo, _ := req.GetArguments()["repo"].(string)
|
|
topic, _ := req.GetArguments()["topic"].(string)
|
|
if owner == "" || repo == "" || topic == "" {
|
|
return to.ErrorResult(errors.New("owner, repo, and topic are required"))
|
|
}
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
_, err = client.AddRepoTopic(owner, repo, topic)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("add repo topic err: %v", err))
|
|
}
|
|
return to.TextResult(map[string]string{"status": "added", "topic": topic})
|
|
}
|
|
|
|
func DeleteRepoTopicFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called DeleteRepoTopicFn")
|
|
owner, _ := req.GetArguments()["owner"].(string)
|
|
repo, _ := req.GetArguments()["repo"].(string)
|
|
topic, _ := req.GetArguments()["topic"].(string)
|
|
if owner == "" || repo == "" || topic == "" {
|
|
return to.ErrorResult(errors.New("owner, repo, and topic are required"))
|
|
}
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
_, err = client.DeleteRepoTopic(owner, repo, topic)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("delete repo topic err: %v", err))
|
|
}
|
|
return to.TextResult(map[string]string{"status": "deleted", "topic": topic})
|
|
}
|
|
|
|
func splitAndTrim(s string) []string {
|
|
var result []string
|
|
for _, part := range strings.Split(s, ",") {
|
|
trimmed := strings.TrimSpace(part)
|
|
if trimmed != "" {
|
|
result = append(result, trimmed)
|
|
}
|
|
}
|
|
return result
|
|
}
|