feat: expand MCP tool coverage from 93 to 299 tools
Some checks failed
release-nightly / release-image (push) Failing after 2m17s

Fork the official gitea-mcp and massively extend API coverage:

- Organization: orgs, members, teams, hooks, blocks, activity (34 tools)
- User: profile, followers, keys, emails, repos, blocks (30 tools)
- Repository: collaborators, webhooks, branch/tag protection, deploy keys,
  topics, git refs/trees/notes, commit status, stars/watchers, forks,
  transfers, mirrors, templates (53 tools)
- Issue: reactions, pins, subscriptions, timeline, templates (16 tools)
- Notifications: list, check, read, repo-scoped (7 tools)
- Settings: API, attachment, repo, UI settings (4 tools)
- Packages: list, get, delete, files, latest, link/unlink (7 tools)
- Miscellaneous: server version, gitignore/label/license templates,
  markdown/markup rendering, node info, signing keys (12 tools)
- Admin: user/org/repo CRUD, system webhooks, cron tasks, unadopted
  repos, emails, badges (23 tools)

Module path updated to git.lethalbits.com/lethalbits/gitea-mcp.
This commit is contained in:
Andrew Miller
2026-03-05 11:03:20 -05:00
parent 9ce5604e4c
commit df25d328d1
66 changed files with 6976 additions and 122 deletions

167
operation/repo/transfer.go Normal file
View File

@@ -0,0 +1,167 @@
package repo
import (
"context"
"errors"
"fmt"
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/gitea"
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/log"
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
gitea_sdk "code.gitea.io/sdk/gitea"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
const (
TransferRepoToolName = "transfer_repo"
AcceptRepoTransferToolName = "accept_repo_transfer"
RejectRepoTransferToolName = "reject_repo_transfer"
CreateFromTemplateToolName = "create_repo_from_template"
)
var (
TransferRepoTool = mcp.NewTool(
TransferRepoToolName,
mcp.WithDescription("Transfer a repository to a new owner"),
mcp.WithString("owner", mcp.Required(), mcp.Description("current repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
mcp.WithString("new_owner", mcp.Required(), mcp.Description("new owner username or org")),
)
AcceptRepoTransferTool = mcp.NewTool(
AcceptRepoTransferToolName,
mcp.WithDescription("Accept a pending repository transfer"),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
)
RejectRepoTransferTool = mcp.NewTool(
RejectRepoTransferToolName,
mcp.WithDescription("Reject a pending repository transfer"),
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
)
CreateFromTemplateTool = mcp.NewTool(
CreateFromTemplateToolName,
mcp.WithDescription("Create a repository from a template"),
mcp.WithString("template_owner", mcp.Required(), mcp.Description("template repository owner")),
mcp.WithString("template_repo", mcp.Required(), mcp.Description("template repository name")),
mcp.WithString("name", mcp.Required(), mcp.Description("name for the new repository")),
mcp.WithString("owner", mcp.Required(), mcp.Description("owner of the new repository (user or org)")),
mcp.WithString("description", mcp.Description("description for the new repository")),
mcp.WithBoolean("private", mcp.Description("whether the new repository is private")),
mcp.WithBoolean("git_content", mcp.Description("copy git content from template (default: true)")),
mcp.WithBoolean("topics", mcp.Description("copy topics from template")),
mcp.WithBoolean("labels", mcp.Description("copy labels from template")),
mcp.WithBoolean("webhooks", mcp.Description("copy webhooks from template")),
)
)
func init() {
Tool.RegisterWrite(server.ServerTool{Tool: TransferRepoTool, Handler: TransferRepoFn})
Tool.RegisterWrite(server.ServerTool{Tool: AcceptRepoTransferTool, Handler: AcceptRepoTransferFn})
Tool.RegisterWrite(server.ServerTool{Tool: RejectRepoTransferTool, Handler: RejectRepoTransferFn})
Tool.RegisterWrite(server.ServerTool{Tool: CreateFromTemplateTool, Handler: CreateFromTemplateFn})
}
func TransferRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called TransferRepoFn")
owner, _ := req.GetArguments()["owner"].(string)
repo, _ := req.GetArguments()["repo"].(string)
newOwner, _ := req.GetArguments()["new_owner"].(string)
if owner == "" || repo == "" || newOwner == "" {
return to.ErrorResult(errors.New("owner, repo, and new_owner are required"))
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
repository, _, err := client.TransferRepo(owner, repo, gitea_sdk.TransferRepoOption{
NewOwner: newOwner,
})
if err != nil {
return to.ErrorResult(fmt.Errorf("transfer repo err: %v", err))
}
return to.TextResult(repository)
}
func AcceptRepoTransferFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called AcceptRepoTransferFn")
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))
}
repository, _, err := client.AcceptRepoTransfer(owner, repo)
if err != nil {
return to.ErrorResult(fmt.Errorf("accept repo transfer err: %v", err))
}
return to.TextResult(repository)
}
func RejectRepoTransferFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called RejectRepoTransferFn")
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))
}
repository, _, err := client.RejectRepoTransfer(owner, repo)
if err != nil {
return to.ErrorResult(fmt.Errorf("reject repo transfer err: %v", err))
}
return to.TextResult(repository)
}
func CreateFromTemplateFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called CreateFromTemplateFn")
templateOwner, _ := req.GetArguments()["template_owner"].(string)
templateRepo, _ := req.GetArguments()["template_repo"].(string)
name, _ := req.GetArguments()["name"].(string)
owner, _ := req.GetArguments()["owner"].(string)
if templateOwner == "" || templateRepo == "" || name == "" || owner == "" {
return to.ErrorResult(errors.New("template_owner, template_repo, name, and owner are required"))
}
opt := gitea_sdk.CreateRepoFromTemplateOption{
Name: name,
Owner: owner,
}
if v, ok := req.GetArguments()["description"].(string); ok {
opt.Description = v
}
if v, ok := req.GetArguments()["private"].(bool); ok {
opt.Private = v
}
if v, ok := req.GetArguments()["git_content"].(bool); ok {
opt.GitContent = v
}
if v, ok := req.GetArguments()["topics"].(bool); ok {
opt.Topics = v
}
if v, ok := req.GetArguments()["labels"].(bool); ok {
opt.Labels = v
}
if v, ok := req.GetArguments()["webhooks"].(bool); ok {
opt.Webhooks = v
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
repository, _, err := client.CreateRepoFromTemplate(templateOwner, templateRepo, opt)
if err != nil {
return to.ErrorResult(fmt.Errorf("create from template err: %v", err))
}
return to.TextResult(repository)
}