package org import ( "context" "fmt" "gitea.com/gitea/gitea-mcp/pkg/gitea" "gitea.com/gitea/gitea-mcp/pkg/log" "gitea.com/gitea/gitea-mcp/pkg/params" "gitea.com/gitea/gitea-mcp/pkg/to" "gitea.com/gitea/gitea-mcp/pkg/tool" gitea_sdk "code.gitea.io/sdk/gitea" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) var Tool = tool.New() const ( OrgReadToolName = "org_read" OrgWriteToolName = "org_write" ) var ( OrgReadTool = mcp.NewTool( OrgReadToolName, mcp.WithDescription("Read organization information. Use method 'get' to get org details, 'list' to list orgs, 'list_members' to list org members, 'list_teams' to list org teams."), mcp.WithString("method", mcp.Required(), mcp.Description("operation to perform"), mcp.Enum("get", "list", "list_members", "list_teams")), mcp.WithString("org", mcp.Description("organization name (required for 'get', 'list_members', 'list_teams')")), mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)), mcp.WithNumber("perPage", mcp.Description("results per page"), mcp.DefaultNumber(30)), ) OrgWriteTool = mcp.NewTool( OrgWriteToolName, mcp.WithDescription("Create or update organizations."), mcp.WithString("method", mcp.Required(), mcp.Description("operation to perform"), mcp.Enum("create", "update", "delete")), mcp.WithString("org", mcp.Description("organization name (required for 'update', 'delete')")), mcp.WithString("description", mcp.Description("organization description")), mcp.WithString("full_name", mcp.Description("full name")), mcp.WithString("location", mcp.Description("location")), mcp.WithString("website", mcp.Description("website")), mcp.WithString("visibility", mcp.Description("visibility"), mcp.Enum("public", "private", "limited")), ) ) func init() { Tool.RegisterRead(server.ServerTool{ Tool: OrgReadTool, Handler: orgReadFn, }) Tool.RegisterWrite(server.ServerTool{ Tool: OrgWriteTool, Handler: orgWriteFn, }) } func orgReadFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { method, err := params.GetString(req.GetArguments(), "method") if err != nil { return to.ErrorResult(err) } switch method { case "get": return getOrgFn(ctx, req) case "list": return listOrgsFn(ctx, req) case "list_members": return listOrgMembersFn(ctx, req) case "list_teams": return listOrgTeamsFn(ctx, req) default: return to.ErrorResult(fmt.Errorf("unknown method: %s", method)) } } func orgWriteFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { method, err := params.GetString(req.GetArguments(), "method") if err != nil { return to.ErrorResult(err) } switch method { case "create": return createOrgFn(ctx, req) case "update": return updateOrgFn(ctx, req) case "delete": return deleteOrgFn(ctx, req) default: return to.ErrorResult(fmt.Errorf("unknown method: %s", method)) } } func getOrgFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called getOrgFn") org, err := params.GetString(req.GetArguments(), "org") 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)) } orgData, _, err := client.GetOrg(org) if err != nil { return to.ErrorResult(fmt.Errorf("get org err: %v", err)) } return to.TextResult(slimOrg(orgData)) } func listOrgsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called listOrgsFn") page, pageSize := params.GetPagination(req.GetArguments(), 30) opt := gitea_sdk.ListOrgsOptions{ ListOptions: gitea_sdk.ListOptions{ Page: page, PageSize: pageSize, }, } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } orgs, _, err := client.ListMyOrgs(opt) if err != nil { return to.ErrorResult(fmt.Errorf("list orgs err: %v", err)) } return to.TextResult(slimOrgs(orgs)) } func listOrgMembersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called listOrgMembersFn") org, err := params.GetString(req.GetArguments(), "org") if err != nil { return to.ErrorResult(err) } page, pageSize := params.GetPagination(req.GetArguments(), 30) client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } opt := gitea_sdk.ListOrgMembershipOption{ ListOptions: gitea_sdk.ListOptions{ Page: page, PageSize: pageSize, }, } members, _, err := client.ListOrgMembership(org, opt) if err != nil { return to.ErrorResult(fmt.Errorf("list org members err: %v", err)) } return to.TextResult(slimUsers(members)) } func listOrgTeamsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called listOrgTeamsFn") org, err := params.GetString(req.GetArguments(), "org") if err != nil { return to.ErrorResult(err) } page, pageSize := params.GetPagination(req.GetArguments(), 30) client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } opt := gitea_sdk.ListTeamsOptions{ ListOptions: gitea_sdk.ListOptions{ Page: page, PageSize: pageSize, }, } teams, _, err := client.ListOrgTeams(org, opt) if err != nil { return to.ErrorResult(fmt.Errorf("list org teams err: %v", err)) } return to.TextResult(slimTeams(teams)) } func createOrgFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called createOrgFn") name, err := params.GetString(req.GetArguments(), "org") if err != nil { return to.ErrorResult(err) } description, _ := req.GetArguments()["description"].(string) fullName, _ := req.GetArguments()["full_name"].(string) location, _ := req.GetArguments()["location"].(string) website, _ := req.GetArguments()["website"].(string) visibility, _ := req.GetArguments()["visibility"].(string) opt := gitea_sdk.CreateOrgOption{ Name: name, Description: description, FullName: fullName, Location: location, Website: website, Visibility: gitea_sdk.VisibleType(visibility), } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } org, _, err := client.CreateOrg(opt) if err != nil { return to.ErrorResult(fmt.Errorf("create org err: %v", err)) } return to.TextResult(slimOrg(org)) } func updateOrgFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called updateOrgFn") org, err := params.GetString(req.GetArguments(), "org") if err != nil { return to.ErrorResult(err) } opt := gitea_sdk.EditOrgOption{} if description, ok := req.GetArguments()["description"].(string); ok { opt.Description = description } if fullName, ok := req.GetArguments()["full_name"].(string); ok { opt.FullName = fullName } if location, ok := req.GetArguments()["location"].(string); ok { opt.Location = location } if website, ok := req.GetArguments()["website"].(string); ok { opt.Website = website } if visibility, ok := req.GetArguments()["visibility"].(string); ok { opt.Visibility = gitea_sdk.VisibleType(visibility) } client, err := gitea.ClientFromContext(ctx) if err != nil { return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err)) } _, err = client.EditOrg(org, opt) if err != nil { return to.ErrorResult(fmt.Errorf("update org err: %v", err)) } return to.TextResult("Organization updated successfully") } func deleteOrgFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { log.Debugf("Called deleteOrgFn") org, err := params.GetString(req.GetArguments(), "org") 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.DeleteOrg(org) if err != nil { return to.ErrorResult(fmt.Errorf("delete org err: %v", err)) } return to.TextResult("Organization deleted successfully") } func slimOrg(o *gitea_sdk.Organization) map[string]any { if o == nil { return nil } return map[string]any{ "id": o.ID, "name": o.Name, "full_name": o.FullName, "description": o.Description, "avatar_url": o.AvatarURL, "website": o.Website, "location": o.Location, "visibility": o.Visibility, } } func slimOrgs(orgs []*gitea_sdk.Organization) []map[string]any { out := make([]map[string]any, 0, len(orgs)) for _, o := range orgs { out = append(out, slimOrg(o)) } return out } func slimUsers(users []*gitea_sdk.User) []map[string]any { out := make([]map[string]any, 0, len(users)) for _, u := range users { out = append(out, map[string]any{ "id": u.ID, "login": u.UserName, "full_name": u.FullName, "email": u.Email, "avatar_url": u.AvatarURL, }) } return out } func slimTeams(teams []*gitea_sdk.Team) []map[string]any { out := make([]map[string]any, 0, len(teams)) for _, t := range teams { out = append(out, map[string]any{ "id": t.ID, "name": t.Name, "description": t.Description, "permission": t.Permission, }) } return out }