package user 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 ( ListMyFollowersToolName = "list_my_followers" ListMyFollowingToolName = "list_my_following" ListUserFollowersToolName = "list_user_followers" ListUserFollowingToolName = "list_user_following" CheckFollowingToolName = "check_following" FollowUserToolName = "follow_user" UnfollowUserToolName = "unfollow_user" ) var ( ListMyFollowersTool = mcp.NewTool( ListMyFollowersToolName, mcp.WithDescription("List the authenticated user's followers"), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) ListMyFollowingTool = mcp.NewTool( ListMyFollowingToolName, mcp.WithDescription("List users the authenticated user is following"), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) ListUserFollowersTool = mcp.NewTool( ListUserFollowersToolName, mcp.WithDescription("List a user's followers"), mcp.WithString("username", mcp.Required(), mcp.Description("username")), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) ListUserFollowingTool = mcp.NewTool( ListUserFollowingToolName, mcp.WithDescription("List users a user is following"), mcp.WithString("username", mcp.Required(), mcp.Description("username")), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) CheckFollowingTool = mcp.NewTool( CheckFollowingToolName, mcp.WithDescription("Check if the authenticated user is following a user"), mcp.WithString("username", mcp.Required(), mcp.Description("username to check")), ) FollowUserTool = mcp.NewTool( FollowUserToolName, mcp.WithDescription("Follow a user"), mcp.WithString("username", mcp.Required(), mcp.Description("username to follow")), ) UnfollowUserTool = mcp.NewTool( UnfollowUserToolName, mcp.WithDescription("Unfollow a user"), mcp.WithString("username", mcp.Required(), mcp.Description("username to unfollow")), ) ) func init() { Tool.RegisterRead(server.ServerTool{Tool: ListMyFollowersTool, Handler: ListMyFollowersFn}) Tool.RegisterRead(server.ServerTool{Tool: ListMyFollowingTool, Handler: ListMyFollowingFn}) Tool.RegisterRead(server.ServerTool{Tool: ListUserFollowersTool, Handler: ListUserFollowersFn}) Tool.RegisterRead(server.ServerTool{Tool: ListUserFollowingTool, Handler: ListUserFollowingFn}) Tool.RegisterRead(server.ServerTool{Tool: CheckFollowingTool, Handler: CheckFollowingFn}) Tool.RegisterWrite(server.ServerTool{Tool: FollowUserTool, Handler: FollowUserFn}) Tool.RegisterWrite(server.ServerTool{Tool: UnfollowUserTool, Handler: UnfollowUserFn}) } func ListMyFollowersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListMyFollowersFn") 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)) } followers, _, err := client.ListMyFollowers(gitea_sdk.ListFollowersOptions{ ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list my followers err: %v", err)) } return to.TextResult(followers) } func ListMyFollowingFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListMyFollowingFn") 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)) } following, _, err := client.ListMyFollowing(gitea_sdk.ListFollowingOptions{ ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list my following err: %v", err)) } return to.TextResult(following) } func ListUserFollowersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListUserFollowersFn") username, ok := req.GetArguments()["username"].(string) if !ok { return to.ErrorResult(errors.New("username is 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)) } followers, _, err := client.ListFollowers(username, gitea_sdk.ListFollowersOptions{ ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list %s followers err: %v", username, err)) } return to.TextResult(followers) } func ListUserFollowingFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListUserFollowingFn") username, ok := req.GetArguments()["username"].(string) if !ok { return to.ErrorResult(errors.New("username is 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)) } following, _, err := client.ListFollowing(username, gitea_sdk.ListFollowingOptions{ ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list %s following err: %v", username, err)) } return to.TextResult(following) } func CheckFollowingFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called CheckFollowingFn") username, ok := req.GetArguments()["username"].(string) if !ok { return to.ErrorResult(errors.New("username is required")) } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } isFollowing, _ := client.IsFollowing(username) return to.TextResult(map[string]any{"username": username, "is_following": isFollowing}) } func FollowUserFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called FollowUserFn") username, ok := req.GetArguments()["username"].(string) if !ok { return to.ErrorResult(errors.New("username is required")) } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } _, err = client.Follow(username) if err != nil { return to.ErrorResult(fmt.Errorf("follow user %s err: %v", username, err)) } return to.TextResult(map[string]string{"status": "following", "username": username}) } func UnfollowUserFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called UnfollowUserFn") username, ok := req.GetArguments()["username"].(string) if !ok { return to.ErrorResult(errors.New("username is required")) } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } _, err = client.Unfollow(username) if err != nil { return to.ErrorResult(fmt.Errorf("unfollow user %s err: %v", username, err)) } return to.TextResult(map[string]string{"status": "unfollowed", "username": username}) }