4b4b5ee247
Add 20+ new MCP tools for repository administration: - Repository: get_repo, repo_write (edit/delete/transfer), list_forks - Branch Protection: read (list/get) and write (create/edit/delete) - Webhooks: read (list/get) and write (create/edit/delete/test) - Collaborators: read (list) and write (add/delete) - Deploy Keys: read (list/get) and write (create/delete) - Topics: read (list) and write (add/delete/set) - Compare: get_compare for branch/tag/commit comparison - Organization: org_read (get/list_teams/get_team/list_team_members/list_team_repos) and org_write (create_team/delete_team/add_team_member/remove_team_member/add_team_repo/remove_team_repo) - PR enhancements: list_files and is_merged methods added to pull_request_read Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
207 lines
6.4 KiB
Go
207 lines
6.4 KiB
Go
package repo
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
|
"gitea.com/gitea/gitea-mcp/pkg/log"
|
|
"gitea.com/gitea/gitea-mcp/pkg/params"
|
|
"gitea.com/gitea/gitea-mcp/pkg/to"
|
|
|
|
gitea_sdk "code.gitea.io/sdk/gitea"
|
|
"github.com/mark3labs/mcp-go/mcp"
|
|
"github.com/mark3labs/mcp-go/server"
|
|
)
|
|
|
|
const (
|
|
DeployKeyReadToolName = "deploy_key_read"
|
|
DeployKeyWriteToolName = "deploy_key_write"
|
|
)
|
|
|
|
var (
|
|
DeployKeyReadTool = mcp.NewTool(
|
|
DeployKeyReadToolName,
|
|
mcp.WithDescription("Read deploy keys. Use method 'list' to list all, 'get' to get a specific key."),
|
|
mcp.WithString("method", mcp.Required(), mcp.Description("operation to perform"), mcp.Enum("list", "get")),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
|
mcp.WithNumber("id", mcp.Description("deploy key ID (required for 'get')")),
|
|
mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
|
|
mcp.WithNumber("perPage", mcp.Description("results per page"), mcp.DefaultNumber(30)),
|
|
)
|
|
|
|
DeployKeyWriteTool = mcp.NewTool(
|
|
DeployKeyWriteToolName,
|
|
mcp.WithDescription("Create or delete deploy keys. Use method 'create' to add a new key, 'delete' to remove."),
|
|
mcp.WithString("method", mcp.Required(), mcp.Description("operation to perform"), mcp.Enum("create", "delete")),
|
|
mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")),
|
|
mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")),
|
|
mcp.WithNumber("id", mcp.Description("deploy key ID (required for 'delete')")),
|
|
mcp.WithString("title", mcp.Description("key title (required for 'create')")),
|
|
mcp.WithString("key", mcp.Description("SSH public key content (required for 'create')")),
|
|
mcp.WithBoolean("read_only", mcp.Description("whether the key has read-only access (default true)")),
|
|
)
|
|
)
|
|
|
|
func init() {
|
|
Tool.RegisterRead(server.ServerTool{
|
|
Tool: DeployKeyReadTool,
|
|
Handler: deployKeyReadFn,
|
|
})
|
|
Tool.RegisterWrite(server.ServerTool{
|
|
Tool: DeployKeyWriteTool,
|
|
Handler: deployKeyWriteFn,
|
|
})
|
|
}
|
|
|
|
func deployKeyReadFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := req.GetArguments()
|
|
method, err := params.GetString(args, "method")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
switch method {
|
|
case "list":
|
|
return listDeployKeysFn(ctx, req)
|
|
case "get":
|
|
return getDeployKeyFn(ctx, req)
|
|
default:
|
|
return to.ErrorResult(fmt.Errorf("unknown method: %s", method))
|
|
}
|
|
}
|
|
|
|
func listDeployKeysFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called listDeployKeysFn")
|
|
args := req.GetArguments()
|
|
owner, err := params.GetString(args, "owner")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
repo, err := params.GetString(args, "repo")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
page, pageSize := params.GetPagination(args, 30)
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
keys, _, err := client.ListDeployKeys(owner, repo, gitea_sdk.ListDeployKeysOptions{
|
|
ListOptions: gitea_sdk.ListOptions{
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("list deploy keys %v/%v err: %v", owner, repo, err))
|
|
}
|
|
return to.TextResult(slimDeployKeys(keys))
|
|
}
|
|
|
|
func getDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called getDeployKeyFn")
|
|
args := req.GetArguments()
|
|
owner, err := params.GetString(args, "owner")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
repo, err := params.GetString(args, "repo")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
id, err := params.GetIndex(args, "id")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
key, _, err := client.GetDeployKey(owner, repo, id)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get deploy key %v in %v/%v err: %v", id, owner, repo, err))
|
|
}
|
|
return to.TextResult(slimDeployKey(key))
|
|
}
|
|
|
|
func deployKeyWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
args := req.GetArguments()
|
|
method, err := params.GetString(args, "method")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
switch method {
|
|
case "create":
|
|
return createDeployKeyFn(ctx, req)
|
|
case "delete":
|
|
return deleteDeployKeyFn(ctx, req)
|
|
default:
|
|
return to.ErrorResult(fmt.Errorf("unknown method: %s", method))
|
|
}
|
|
}
|
|
|
|
func createDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called createDeployKeyFn")
|
|
args := req.GetArguments()
|
|
owner, err := params.GetString(args, "owner")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
repo, err := params.GetString(args, "repo")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
title, err := params.GetString(args, "title")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
key, err := params.GetString(args, "key")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
readOnly := true
|
|
if v, ok := args["read_only"].(bool); ok {
|
|
readOnly = v
|
|
}
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
dk, _, err := client.CreateDeployKey(owner, repo, gitea_sdk.CreateKeyOption{
|
|
Title: title,
|
|
Key: key,
|
|
ReadOnly: readOnly,
|
|
})
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("create deploy key in %v/%v err: %v", owner, repo, err))
|
|
}
|
|
return to.TextResult(slimDeployKey(dk))
|
|
}
|
|
|
|
func deleteDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called deleteDeployKeyFn")
|
|
args := req.GetArguments()
|
|
owner, err := params.GetString(args, "owner")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
repo, err := params.GetString(args, "repo")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
id, err := params.GetIndex(args, "id")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
_, err = client.DeleteDeployKey(owner, repo, id)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("delete deploy key %v from %v/%v err: %v", id, owner, repo, err))
|
|
}
|
|
return mcp.NewToolResultText(fmt.Sprintf("Deploy key %d deleted from %s/%s", id, owner, repo)), nil
|
|
}
|