package issue 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 ( ListIssueReactionsToolName = "list_issue_reactions" PostIssueReactionToolName = "post_issue_reaction" DeleteIssueReactionToolName = "delete_issue_reaction" GetIssueCommentReactionsToolName = "get_issue_comment_reactions" PostIssueCommentReactionToolName = "post_issue_comment_reaction" DeleteIssueCommentReactionToolName = "delete_issue_comment_reaction" ) var ( ListIssueReactionsTool = mcp.NewTool( ListIssueReactionsToolName, mcp.WithDescription("List reactions on an issue"), 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")), ) PostIssueReactionTool = mcp.NewTool( PostIssueReactionToolName, mcp.WithDescription("Add a reaction to an issue"), 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.WithString("reaction", mcp.Required(), mcp.Description("reaction emoji (e.g., +1, -1, laugh, confused, heart, hooray, rocket, eyes)")), ) DeleteIssueReactionTool = mcp.NewTool( DeleteIssueReactionToolName, mcp.WithDescription("Remove a reaction from an issue"), 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.WithString("reaction", mcp.Required(), mcp.Description("reaction emoji to remove")), ) GetIssueCommentReactionsTool = mcp.NewTool( GetIssueCommentReactionsToolName, mcp.WithDescription("List reactions on an issue comment"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("comment_id", mcp.Required(), mcp.Description("comment ID")), ) PostIssueCommentReactionTool = mcp.NewTool( PostIssueCommentReactionToolName, mcp.WithDescription("Add a reaction to an issue comment"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("comment_id", mcp.Required(), mcp.Description("comment ID")), mcp.WithString("reaction", mcp.Required(), mcp.Description("reaction emoji")), ) DeleteIssueCommentReactionTool = mcp.NewTool( DeleteIssueCommentReactionToolName, mcp.WithDescription("Remove a reaction from an issue comment"), mcp.WithString("owner", mcp.Required(), mcp.Description("repository owner")), mcp.WithString("repo", mcp.Required(), mcp.Description("repository name")), mcp.WithNumber("comment_id", mcp.Required(), mcp.Description("comment ID")), mcp.WithString("reaction", mcp.Required(), mcp.Description("reaction emoji to remove")), ) ) func init() { Tool.RegisterRead(server.ServerTool{Tool: ListIssueReactionsTool, Handler: ListIssueReactionsFn}) Tool.RegisterRead(server.ServerTool{Tool: GetIssueCommentReactionsTool, Handler: GetIssueCommentReactionsFn}) Tool.RegisterWrite(server.ServerTool{Tool: PostIssueReactionTool, Handler: PostIssueReactionFn}) Tool.RegisterWrite(server.ServerTool{Tool: DeleteIssueReactionTool, Handler: DeleteIssueReactionFn}) Tool.RegisterWrite(server.ServerTool{Tool: PostIssueCommentReactionTool, Handler: PostIssueCommentReactionFn}) Tool.RegisterWrite(server.ServerTool{Tool: DeleteIssueCommentReactionTool, Handler: DeleteIssueCommentReactionFn}) } func ListIssueReactionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListIssueReactionsFn") 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)) } reactions, _, err := client.ListIssueReactions(owner, repo, index, gitea_sdk.ListIssueReactionsOptions{}) if err != nil { return to.ErrorResult(fmt.Errorf("list issue reactions err: %v", err)) } return to.TextResult(reactions) } func PostIssueReactionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called PostIssueReactionFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) reaction, _ := req.GetArguments()["reaction"].(string) if owner == "" || repo == "" || reaction == "" { return to.ErrorResult(errors.New("owner, repo, and reaction 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)) } r, _, err := client.PostIssueReaction(owner, repo, index, reaction) if err != nil { return to.ErrorResult(fmt.Errorf("post issue reaction err: %v", err)) } return to.TextResult(r) } func DeleteIssueReactionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called DeleteIssueReactionFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) reaction, _ := req.GetArguments()["reaction"].(string) if owner == "" || repo == "" || reaction == "" { return to.ErrorResult(errors.New("owner, repo, and reaction 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.DeleteIssueReaction(owner, repo, index, reaction) if err != nil { return to.ErrorResult(fmt.Errorf("delete issue reaction err: %v", err)) } return to.TextResult(map[string]string{"status": "removed", "reaction": reaction}) } func GetIssueCommentReactionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called GetIssueCommentReactionsFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) if owner == "" || repo == "" { return to.ErrorResult(errors.New("owner and repo are required")) } commentID, err := params.GetIndex(req.GetArguments(), "comment_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)) } reactions, _, err := client.GetIssueCommentReactions(owner, repo, commentID) if err != nil { return to.ErrorResult(fmt.Errorf("get issue comment reactions err: %v", err)) } return to.TextResult(reactions) } func PostIssueCommentReactionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called PostIssueCommentReactionFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) reaction, _ := req.GetArguments()["reaction"].(string) if owner == "" || repo == "" || reaction == "" { return to.ErrorResult(errors.New("owner, repo, and reaction are required")) } commentID, err := params.GetIndex(req.GetArguments(), "comment_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)) } r, _, err := client.PostIssueCommentReaction(owner, repo, commentID, reaction) if err != nil { return to.ErrorResult(fmt.Errorf("post issue comment reaction err: %v", err)) } return to.TextResult(r) } func DeleteIssueCommentReactionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called DeleteIssueCommentReactionFn") owner, _ := req.GetArguments()["owner"].(string) repo, _ := req.GetArguments()["repo"].(string) reaction, _ := req.GetArguments()["reaction"].(string) if owner == "" || repo == "" || reaction == "" { return to.ErrorResult(errors.New("owner, repo, and reaction are required")) } commentID, err := params.GetIndex(req.GetArguments(), "comment_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.DeleteIssueCommentReaction(owner, repo, commentID, reaction) if err != nil { return to.ErrorResult(fmt.Errorf("delete issue comment reaction err: %v", err)) } return to.TextResult(map[string]string{"status": "removed", "reaction": reaction}) }