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/to" gitea_sdk "code.gitea.io/sdk/gitea" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) const ( CreatePushMirrorToolName = "create_push_mirror" ListPushMirrorsToolName = "list_push_mirrors" GetPushMirrorByRemoteNameToolName = "get_push_mirror" DeletePushMirrorToolName = "delete_push_mirror" ) var ( CreatePushMirrorTool = mcp.NewTool( CreatePushMirrorToolName, mcp.WithDescription("Create a push mirror for a repository"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("remote_address", mcp.Required(), mcp.Description("remote git URL to push to")), mcp.WithString("remote_username", mcp.Description("remote username for authentication")), mcp.WithString("remote_password", mcp.Description("remote password/token for authentication")), mcp.WithString("interval", mcp.Description("sync interval (e.g., 8h0m0s)")), mcp.WithBoolean("sync_on_commit", mcp.Description("sync on commit")), ) ListPushMirrorsTool = mcp.NewTool( ListPushMirrorsToolName, mcp.WithDescription("List push mirrors for a repository"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), ) GetPushMirrorByRemoteNameTool = mcp.NewTool( GetPushMirrorByRemoteNameToolName, mcp.WithDescription("Get a push mirror by remote name"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("remote_name", mcp.Required(), mcp.Description("remote name")), ) DeletePushMirrorTool = mcp.NewTool( DeletePushMirrorToolName, mcp.WithDescription("Delete a push mirror by remote name"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithString("remote_name", mcp.Required(), mcp.Description("remote name to delete")), ) ) func init() { Tool.RegisterRead(server.ServerTool{Tool: ListPushMirrorsTool, Handler: ListPushMirrorsFn}) Tool.RegisterRead(server.ServerTool{Tool: GetPushMirrorByRemoteNameTool, Handler: GetPushMirrorByRemoteNameFn}) Tool.RegisterWrite(server.ServerTool{Tool: CreatePushMirrorTool, Handler: CreatePushMirrorFn}) Tool.RegisterWrite(server.ServerTool{Tool: DeletePushMirrorTool, Handler: DeletePushMirrorFn}) } func CreatePushMirrorFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called CreatePushMirrorFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) remoteAddr, _ := req.GetArguments()["remote_address"].(string) if owner == "" || repo == "" || remoteAddr == "" { return to.ErrorResult(errors.New("owner, repo, and remote_address are required")) } opt := gitea_sdk.CreatePushMirrorOption{ RemoteAddress: remoteAddr, } if v, ok := req.GetArguments()["remote_username"].(string); ok { opt.RemoteUsername = v } if v, ok := req.GetArguments()["remote_password"].(string); ok { opt.RemotePassword = v } if v, ok := req.GetArguments()["interval"].(string); ok { opt.Interval = v } if v, ok := req.GetArguments()["sync_on_commit"].(bool); ok { opt.SyncONCommit = v } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } mirror, _, err := client.PushMirrors(owner, repo, opt) if err != nil { return to.ErrorResult(fmt.Errorf("create push mirror err: %v", err)) } return to.TextResult(mirror) } func ListPushMirrorsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListPushMirrorsFn") 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)) } mirrors, _, err := client.ListPushMirrors(owner, repo, gitea_sdk.ListOptions{}) if err != nil { return to.ErrorResult(fmt.Errorf("list push mirrors err: %v", err)) } return to.TextResult(mirrors) } func GetPushMirrorByRemoteNameFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called GetPushMirrorByRemoteNameFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) remoteName, _ := req.GetArguments()["remote_name"].(string) if owner == "" || repo == "" || remoteName == "" { return to.ErrorResult(errors.New("owner, repo, and remote_name are required")) } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } mirror, _, err := client.GetPushMirrorByRemoteName(owner, repo, remoteName) if err != nil { return to.ErrorResult(fmt.Errorf("get push mirror err: %v", err)) } return to.TextResult(mirror) } func DeletePushMirrorFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called DeletePushMirrorFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) remoteName, _ := req.GetArguments()["remote_name"].(string) if owner == "" || repo == "" || remoteName == "" { return to.ErrorResult(errors.New("owner, repo, and remote_name are required")) } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } _, err = client.DeletePushMirror(owner, repo, remoteName) if err != nil { return to.ErrorResult(fmt.Errorf("delete push mirror err: %v", err)) } return to.TextResult(map[string]string{"status": "deleted"}) }