package issue 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" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) const ( ListPinnedIssuesToolName = "list_pinned_issues" PinIssueToolName = "pin_issue" UnpinIssueToolName = "unpin_issue" MoveIssuePinToolName = "move_issue_pin" ) var ( ListPinnedIssuesTool = mcp.NewTool( ListPinnedIssuesToolName, mcp.WithDescription("List pinned issues in a repository"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), ) PinIssueTool = mcp.NewTool( PinIssueToolName, mcp.WithDescription("Pin an issue in a repository"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")), ) UnpinIssueTool = mcp.NewTool( UnpinIssueToolName, mcp.WithDescription("Unpin an issue from a repository"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")), ) MoveIssuePinTool = mcp.NewTool( MoveIssuePinToolName, mcp.WithDescription("Move a pinned issue to a new position"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("index", mcp.Required(), mcp.Description("issue index")), mcp.WithNumber("position", mcp.Required(), mcp.Description("new position (1-based)")), ) ) func init() { Tool.RegisterRead(server.ServerTool{Tool: ListPinnedIssuesTool, Handler: ListPinnedIssuesFn}) Tool.RegisterWrite(server.ServerTool{Tool: PinIssueTool, Handler: PinIssueFn}) Tool.RegisterWrite(server.ServerTool{Tool: UnpinIssueTool, Handler: UnpinIssueFn}) Tool.RegisterWrite(server.ServerTool{Tool: MoveIssuePinTool, Handler: MoveIssuePinFn}) } func ListPinnedIssuesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListPinnedIssuesFn") 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)) } issues, _, err := client.ListRepoPinnedIssues(owner, repo) if err != nil { return to.ErrorResult(fmt.Errorf("list pinned issues err: %v", err)) } return to.TextResult(issues) } func PinIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called PinIssueFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) if owner == "" || repo == "" { return to.ErrorResult(errors.New("owner and repo are required")) } index, err := params.GetIndex(req.GetArguments(), "index") 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.PinIssue(owner, repo, index) if err != nil { return to.ErrorResult(fmt.Errorf("pin issue err: %v", err)) } return to.TextResult(map[string]string{"status": "pinned"}) } func UnpinIssueFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called UnpinIssueFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) if owner == "" || repo == "" { return to.ErrorResult(errors.New("owner and repo are required")) } index, err := params.GetIndex(req.GetArguments(), "index") 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.UnpinIssue(owner, repo, index) if err != nil { return to.ErrorResult(fmt.Errorf("unpin issue err: %v", err)) } return to.TextResult(map[string]string{"status": "unpinned"}) } func MoveIssuePinFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called MoveIssuePinFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) if owner == "" || repo == "" { return to.ErrorResult(errors.New("owner and repo are required")) } index, err := params.GetIndex(req.GetArguments(), "index") if err != nil { return to.ErrorResult(err) } position, ok := req.GetArguments()["position"].(float64) if !ok { return to.ErrorResult(errors.New("position is required")) } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } _, err = client.MoveIssuePin(owner, repo, index, int64(position)) if err != nil { return to.ErrorResult(fmt.Errorf("move issue pin err: %v", err)) } return to.TextResult(map[string]any{"status": "moved", "position": int64(position)}) }