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

View File

@@ -0,0 +1,242 @@
package packages
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/params"
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/to"
"git.lethalbits.com/lethalbits/gitea-mcp/pkg/tool"
gitea_sdk "code.gitea.io/sdk/gitea"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
var Tool = tool.New()
const (
ListPackagesToolName = "list_packages"
GetPackageToolName = "get_package"
DeletePackageToolName = "delete_package"
ListPackageFilesToolName = "list_package_files"
GetLatestPackageToolName = "get_latest_package"
LinkPackageToolName = "link_package"
UnlinkPackageToolName = "unlink_package"
)
var (
ListPackagesTool = mcp.NewTool(
ListPackagesToolName,
mcp.WithDescription("List packages for a user or organization"),
mcp.WithString("owner", mcp.Required(), mcp.Description("package owner (user or org)")),
mcp.WithString("type", mcp.Description("package type filter (e.g., generic, container, npm, pypi, rubygems, maven, nuget, debian, alpine, go, cargo, chef, composer, conan, conda, cran, pub, helm, rpm, swift, vagrant)")),
mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)),
)
GetPackageTool = mcp.NewTool(
GetPackageToolName,
mcp.WithDescription("Get a specific package by type, name, and version"),
mcp.WithString("owner", mcp.Required(), mcp.Description("package owner")),
mcp.WithString("type", mcp.Required(), mcp.Description("package type (e.g., generic, container, npm)")),
mcp.WithString("name", mcp.Required(), mcp.Description("package name")),
mcp.WithString("version", mcp.Required(), mcp.Description("package version")),
)
DeletePackageTool = mcp.NewTool(
DeletePackageToolName,
mcp.WithDescription("Delete a specific package version"),
mcp.WithString("owner", mcp.Required(), mcp.Description("package owner")),
mcp.WithString("type", mcp.Required(), mcp.Description("package type")),
mcp.WithString("name", mcp.Required(), mcp.Description("package name")),
mcp.WithString("version", mcp.Required(), mcp.Description("package version")),
)
ListPackageFilesTool = mcp.NewTool(
ListPackageFilesToolName,
mcp.WithDescription("List files of a package version"),
mcp.WithString("owner", mcp.Required(), mcp.Description("package owner")),
mcp.WithString("type", mcp.Required(), mcp.Description("package type")),
mcp.WithString("name", mcp.Required(), mcp.Description("package name")),
mcp.WithString("version", mcp.Required(), mcp.Description("package version")),
)
GetLatestPackageTool = mcp.NewTool(
GetLatestPackageToolName,
mcp.WithDescription("Get the latest version of a package"),
mcp.WithString("owner", mcp.Required(), mcp.Description("package owner")),
mcp.WithString("type", mcp.Required(), mcp.Description("package type")),
mcp.WithString("name", mcp.Required(), mcp.Description("package name")),
)
LinkPackageTool = mcp.NewTool(
LinkPackageToolName,
mcp.WithDescription("Link a package to a repository"),
mcp.WithString("owner", mcp.Required(), mcp.Description("package owner")),
mcp.WithString("type", mcp.Required(), mcp.Description("package type")),
mcp.WithString("name", mcp.Required(), mcp.Description("package name")),
mcp.WithString("repo_name", mcp.Required(), mcp.Description("repository name to link to")),
)
UnlinkPackageTool = mcp.NewTool(
UnlinkPackageToolName,
mcp.WithDescription("Unlink a package from its repository"),
mcp.WithString("owner", mcp.Required(), mcp.Description("package owner")),
mcp.WithString("type", mcp.Required(), mcp.Description("package type")),
mcp.WithString("name", mcp.Required(), mcp.Description("package name")),
)
)
func init() {
Tool.RegisterRead(server.ServerTool{Tool: ListPackagesTool, Handler: ListPackagesFn})
Tool.RegisterRead(server.ServerTool{Tool: GetPackageTool, Handler: GetPackageFn})
Tool.RegisterRead(server.ServerTool{Tool: ListPackageFilesTool, Handler: ListPackageFilesFn})
Tool.RegisterRead(server.ServerTool{Tool: GetLatestPackageTool, Handler: GetLatestPackageFn})
Tool.RegisterWrite(server.ServerTool{Tool: DeletePackageTool, Handler: DeletePackageFn})
Tool.RegisterWrite(server.ServerTool{Tool: LinkPackageTool, Handler: LinkPackageFn})
Tool.RegisterWrite(server.ServerTool{Tool: UnlinkPackageTool, Handler: UnlinkPackageFn})
}
func ListPackagesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListPackagesFn")
owner, _ := req.GetArguments()["owner"].(string)
if owner == "" {
return to.ErrorResult(errors.New("owner is required"))
}
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
opt := gitea_sdk.ListPackagesOptions{
ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)},
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
packages, _, err := client.ListPackages(owner, opt)
if err != nil {
return to.ErrorResult(fmt.Errorf("list packages err: %v", err))
}
return to.TextResult(packages)
}
func GetPackageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetPackageFn")
owner, _ := req.GetArguments()["owner"].(string)
pkgType, _ := req.GetArguments()["type"].(string)
name, _ := req.GetArguments()["name"].(string)
version, _ := req.GetArguments()["version"].(string)
if owner == "" || pkgType == "" || name == "" || version == "" {
return to.ErrorResult(errors.New("owner, type, name, and version are required"))
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
pkg, _, err := client.GetPackage(owner, pkgType, name, version)
if err != nil {
return to.ErrorResult(fmt.Errorf("get package err: %v", err))
}
return to.TextResult(pkg)
}
func DeletePackageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called DeletePackageFn")
owner, _ := req.GetArguments()["owner"].(string)
pkgType, _ := req.GetArguments()["type"].(string)
name, _ := req.GetArguments()["name"].(string)
version, _ := req.GetArguments()["version"].(string)
if owner == "" || pkgType == "" || name == "" || version == "" {
return to.ErrorResult(errors.New("owner, type, name, and version are required"))
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
_, err = client.DeletePackage(owner, pkgType, name, version)
if err != nil {
return to.ErrorResult(fmt.Errorf("delete package err: %v", err))
}
return to.TextResult(map[string]string{"status": "deleted"})
}
func ListPackageFilesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called ListPackageFilesFn")
owner, _ := req.GetArguments()["owner"].(string)
pkgType, _ := req.GetArguments()["type"].(string)
name, _ := req.GetArguments()["name"].(string)
version, _ := req.GetArguments()["version"].(string)
if owner == "" || pkgType == "" || name == "" || version == "" {
return to.ErrorResult(errors.New("owner, type, name, and version are required"))
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
files, _, err := client.ListPackageFiles(owner, pkgType, name, version)
if err != nil {
return to.ErrorResult(fmt.Errorf("list package files err: %v", err))
}
return to.TextResult(files)
}
func GetLatestPackageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called GetLatestPackageFn")
owner, _ := req.GetArguments()["owner"].(string)
pkgType, _ := req.GetArguments()["type"].(string)
name, _ := req.GetArguments()["name"].(string)
if owner == "" || pkgType == "" || name == "" {
return to.ErrorResult(errors.New("owner, type, and name are required"))
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
pkg, _, err := client.GetLatestPackage(owner, pkgType, name)
if err != nil {
return to.ErrorResult(fmt.Errorf("get latest package err: %v", err))
}
return to.TextResult(pkg)
}
func LinkPackageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called LinkPackageFn")
owner, _ := req.GetArguments()["owner"].(string)
pkgType, _ := req.GetArguments()["type"].(string)
name, _ := req.GetArguments()["name"].(string)
repoName, _ := req.GetArguments()["repo_name"].(string)
if owner == "" || pkgType == "" || name == "" || repoName == "" {
return to.ErrorResult(errors.New("owner, type, name, and repo_name are required"))
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
_, err = client.LinkPackage(owner, pkgType, name, repoName)
if err != nil {
return to.ErrorResult(fmt.Errorf("link package err: %v", err))
}
return to.TextResult(map[string]string{"status": "linked", "repo": repoName})
}
func UnlinkPackageFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("Called UnlinkPackageFn")
owner, _ := req.GetArguments()["owner"].(string)
pkgType, _ := req.GetArguments()["type"].(string)
name, _ := req.GetArguments()["name"].(string)
if owner == "" || pkgType == "" || name == "" {
return to.ErrorResult(errors.New("owner, type, and name are required"))
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
_, err = client.UnlinkPackage(owner, pkgType, name)
if err != nil {
return to.ErrorResult(fmt.Errorf("unlink package err: %v", err))
}
return to.TextResult(map[string]string{"status": "unlinked"})
}