package repo import ( "context" "errors" "fmt" "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/params" "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 ( ListDeployKeysToolName = "list_deploy_keys" GetDeployKeyToolName = "get_deploy_key" CreateDeployKeyToolName = "create_deploy_key" DeleteDeployKeyToolName = "delete_deploy_key" ) var ( ListDeployKeysTool = mcp.NewTool( ListDeployKeysToolName, mcp.WithDescription("List a repository's deploy keys"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) GetDeployKeyTool = mcp.NewTool( GetDeployKeyToolName, mcp.WithDescription("Get a deploy key by ID"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("key_id", mcp.Required(), mcp.Description("deploy key ID")), ) CreateDeployKeyTool = mcp.NewTool( CreateDeployKeyToolName, mcp.WithDescription("Add a deploy key to a repository"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("title", mcp.Required(), mcp.Description("key title")), mcp.WithString("key", mcp.Required(), mcp.Description("public key content")), mcp.WithBoolean("read_only", mcp.Description("whether the key has read-only access (default: true)")), ) DeleteDeployKeyTool = mcp.NewTool( DeleteDeployKeyToolName, mcp.WithDescription("Remove a deploy key from a repository"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("key_id", mcp.Required(), mcp.Description("deploy key ID")), ) ) func init() { Tool.RegisterRead(server.ServerTool{Tool: ListDeployKeysTool, Handler: ListDeployKeysFn}) Tool.RegisterRead(server.ServerTool{Tool: GetDeployKeyTool, Handler: GetDeployKeyFn}) Tool.RegisterWrite(server.ServerTool{Tool: CreateDeployKeyTool, Handler: CreateDeployKeyFn}) Tool.RegisterWrite(server.ServerTool{Tool: DeleteDeployKeyTool, Handler: DeleteDeployKeyFn}) } func ListDeployKeysFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListDeployKeysFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) if owner == "" || repo == "" { return to.ErrorResult(errors.New("owner and repo are required")) } page := params.GetOptionalInt(req.GetArguments(), "page", 1) pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100) 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: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list deploy keys err: %v", err)) } return to.TextResult(keys) } func GetDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called GetDeployKeyFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) if owner == "" || repo == "" { return to.ErrorResult(errors.New("owner and repo are required")) } keyID, err := params.GetIndex(req.GetArguments(), "key_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, keyID) if err != nil { return to.ErrorResult(fmt.Errorf("get deploy key err: %v", err)) } return to.TextResult(key) } func CreateDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called CreateDeployKeyFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) title, _ := req.GetArguments()["title"].(string) key, _ := req.GetArguments()["key"].(string) if owner == "" || repo == "" || title == "" || key == "" { return to.ErrorResult(errors.New("owner, repo, title, and key are required")) } opt := gitea_sdk.CreateKeyOption{ Title: title, Key: key, ReadOnly: true, } if v, ok := req.GetArguments()["read_only"].(bool); ok { opt.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, opt) if err != nil { return to.ErrorResult(fmt.Errorf("create deploy key err: %v", err)) } return to.TextResult(dk) } func DeleteDeployKeyFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called DeleteDeployKeyFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) if owner == "" || repo == "" { return to.ErrorResult(errors.New("owner and repo are required")) } keyID, err := params.GetIndex(req.GetArguments(), "key_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, keyID) if err != nil { return to.ErrorResult(fmt.Errorf("delete deploy key err: %v", err)) } return to.TextResult(map[string]string{"status": "deleted"}) }