package organization 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 ( ListOrgMembersToolName = "list_org_members" CheckOrgMembershipToolName = "check_org_membership" RemoveOrgMemberToolName = "remove_org_member" ListOrgPublicMembersToolName = "list_org_public_members" CheckOrgPublicMemberToolName = "check_org_public_member" SetOrgPublicMemberToolName = "set_org_public_member" GetOrgPermissionsToolName = "get_org_permissions" ) var ( ListOrgMembersTool = mcp.NewTool( ListOrgMembersToolName, mcp.WithDescription("List an organization's members"), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) CheckOrgMembershipTool = mcp.NewTool( CheckOrgMembershipToolName, mcp.WithDescription("Check if a user is a member of an organization"), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithString("username", mcp.Required(), mcp.Description("username to check")), ) RemoveOrgMemberTool = mcp.NewTool( RemoveOrgMemberToolName, mcp.WithDescription("Remove a member from an organization"), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithString("username", mcp.Required(), mcp.Description("username to remove")), ) ListOrgPublicMembersTool = mcp.NewTool( ListOrgPublicMembersToolName, mcp.WithDescription("List an organization's public members"), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("pageSize", mcp.Description("page size"), mcp.DefaultNumber(100)), ) CheckOrgPublicMemberTool = mcp.NewTool( CheckOrgPublicMemberToolName, mcp.WithDescription("Check if a user is a public member of an organization"), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithString("username", mcp.Required(), mcp.Description("username to check")), ) SetOrgPublicMemberTool = mcp.NewTool( SetOrgPublicMemberToolName, mcp.WithDescription("Set or remove a user's public membership in an organization"), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithString("username", mcp.Required(), mcp.Description("username")), mcp.WithBoolean("visible", mcp.Required(), mcp.Description("true to make public, false to make private")), ) GetOrgPermissionsTool = mcp.NewTool( GetOrgPermissionsToolName, mcp.WithDescription("Get a user's permissions in an organization"), mcp.WithString("org", mcp.Required(), mcp.Description("organization name")), mcp.WithString("username", mcp.Required(), mcp.Description("username")), ) ) func init() { Tool.RegisterRead(server.ServerTool{Tool: ListOrgMembersTool, Handler: ListOrgMembersFn}) Tool.RegisterRead(server.ServerTool{Tool: CheckOrgMembershipTool, Handler: CheckOrgMembershipFn}) Tool.RegisterRead(server.ServerTool{Tool: ListOrgPublicMembersTool, Handler: ListOrgPublicMembersFn}) Tool.RegisterRead(server.ServerTool{Tool: CheckOrgPublicMemberTool, Handler: CheckOrgPublicMemberFn}) Tool.RegisterRead(server.ServerTool{Tool: GetOrgPermissionsTool, Handler: GetOrgPermissionsFn}) Tool.RegisterWrite(server.ServerTool{Tool: RemoveOrgMemberTool, Handler: RemoveOrgMemberFn}) Tool.RegisterWrite(server.ServerTool{Tool: SetOrgPublicMemberTool, Handler: SetOrgPublicMemberFn}) } func ListOrgMembersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListOrgMembersFn") org, ok := req.GetArguments()["org"].(string) if !ok { return to.ErrorResult(errors.New("org 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)) } members, _, err := client.ListOrgMembership(org, gitea_sdk.ListOrgMembershipOption{ ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list org %s members err: %v", org, err)) } return to.TextResult(members) } func CheckOrgMembershipFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called CheckOrgMembershipFn") org, ok := req.GetArguments()["org"].(string) if !ok { return to.ErrorResult(errors.New("org is required")) } 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)) } isMember, _, err := client.CheckOrgMembership(org, username) if err != nil { return to.ErrorResult(fmt.Errorf("check org %s membership for %s err: %v", org, username, err)) } return to.TextResult(map[string]any{"org": org, "username": username, "is_member": isMember}) } func RemoveOrgMemberFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called RemoveOrgMemberFn") org, ok := req.GetArguments()["org"].(string) if !ok { return to.ErrorResult(errors.New("org is required")) } 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.DeleteOrgMembership(org, username) if err != nil { return to.ErrorResult(fmt.Errorf("remove member %s from org %s err: %v", username, org, err)) } return to.TextResult(map[string]string{"status": "removed", "org": org, "username": username}) } func ListOrgPublicMembersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called ListOrgPublicMembersFn") org, ok := req.GetArguments()["org"].(string) if !ok { return to.ErrorResult(errors.New("org 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)) } members, _, err := client.ListPublicOrgMembership(org, gitea_sdk.ListOrgMembershipOption{ ListOptions: gitea_sdk.ListOptions{Page: int(page), PageSize: int(pageSize)}, }) if err != nil { return to.ErrorResult(fmt.Errorf("list org %s public members err: %v", org, err)) } return to.TextResult(members) } func CheckOrgPublicMemberFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called CheckOrgPublicMemberFn") org, ok := req.GetArguments()["org"].(string) if !ok { return to.ErrorResult(errors.New("org is required")) } 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)) } isPublic, _, err := client.CheckPublicOrgMembership(org, username) if err != nil { return to.ErrorResult(fmt.Errorf("check org %s public membership for %s err: %v", org, username, err)) } return to.TextResult(map[string]any{"org": org, "username": username, "is_public_member": isPublic}) } func SetOrgPublicMemberFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called SetOrgPublicMemberFn") org, ok := req.GetArguments()["org"].(string) if !ok { return to.ErrorResult(errors.New("org is required")) } username, ok := req.GetArguments()["username"].(string) if !ok { return to.ErrorResult(errors.New("username is required")) } visible, ok := req.GetArguments()["visible"].(bool) if !ok { return to.ErrorResult(errors.New("visible is required")) } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } _, err = client.SetPublicOrgMembership(org, username, visible) if err != nil { return to.ErrorResult(fmt.Errorf("set public membership for %s in org %s err: %v", username, org, err)) } return to.TextResult(map[string]any{"org": org, "username": username, "visible": visible}) } func GetOrgPermissionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called GetOrgPermissionsFn") org, ok := req.GetArguments()["org"].(string) if !ok { return to.ErrorResult(errors.New("org is required")) } 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)) } perms, _, err := client.GetOrgPermissions(org, username) if err != nil { return to.ErrorResult(fmt.Errorf("get permissions for %s in org %s err: %v", username, org, err)) } return to.TextResult(perms) }