377 lines
12 KiB
Go
377 lines
12 KiB
Go
package team
|
|
|
|
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 (
|
|
TeamReadToolName = "team_read"
|
|
TeamWriteToolName = "team_write"
|
|
)
|
|
|
|
var (
|
|
TeamReadTool = mcp.NewTool(
|
|
TeamReadToolName,
|
|
mcp.WithDescription("Read team information. Use method 'get' to get team details, 'list_members' to list team members, 'list_repos' to list team repositories."),
|
|
mcp.WithString("method", mcp.Required(), mcp.Description("operation to perform"), mcp.Enum("get", "list_members", "list_repos")),
|
|
mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
|
|
mcp.WithNumber("id", mcp.Description("team ID (required for 'get', 'list_members', 'list_repos')")),
|
|
mcp.WithNumber("page", mcp.Description("page number"), mcp.DefaultNumber(1)),
|
|
mcp.WithNumber("perPage", mcp.Description("results per page"), mcp.DefaultNumber(30)),
|
|
)
|
|
|
|
TeamWriteTool = mcp.NewTool(
|
|
TeamWriteToolName,
|
|
mcp.WithDescription("Create, update, or delete teams, manage team members."),
|
|
mcp.WithString("method", mcp.Required(), mcp.Description("operation to perform"), mcp.Enum("create", "edit", "delete", "add_member", "remove_member", "add_repo", "remove_repo")),
|
|
mcp.WithString("org", mcp.Required(), mcp.Description("organization name")),
|
|
mcp.WithNumber("id", mcp.Description("team ID (required for 'edit', 'delete', 'add_member', 'remove_member', 'add_repo', 'remove_repo')")),
|
|
mcp.WithString("name", mcp.Description("team name (required for 'create', optional for 'edit')")),
|
|
mcp.WithString("description", mcp.Description("team description")),
|
|
mcp.WithString("permission", mcp.Description("permission level"), mcp.Enum("read", "write", "admin", "owner")),
|
|
mcp.WithString("user", mcp.Description("username (required for 'add_member', 'remove_member')")),
|
|
mcp.WithString("repo", mcp.Description("repository name (required for 'add_repo', 'remove_repo')")),
|
|
)
|
|
)
|
|
|
|
func init() {
|
|
Tool.RegisterRead(server.ServerTool{
|
|
Tool: TeamReadTool,
|
|
Handler: teamReadFn,
|
|
})
|
|
Tool.RegisterWrite(server.ServerTool{
|
|
Tool: TeamWriteTool,
|
|
Handler: teamWriteFn,
|
|
})
|
|
}
|
|
|
|
func teamReadFn(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 getTeamFn(ctx, req)
|
|
case "list_members":
|
|
return listTeamMembersFn(ctx, req)
|
|
case "list_repos":
|
|
return listTeamReposFn(ctx, req)
|
|
default:
|
|
return to.ErrorResult(fmt.Errorf("unknown method: %s", method))
|
|
}
|
|
}
|
|
|
|
func teamWriteFn(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 createTeamFn(ctx, req)
|
|
case "edit":
|
|
return editTeamFn(ctx, req)
|
|
case "delete":
|
|
return deleteTeamFn(ctx, req)
|
|
case "add_member":
|
|
return addTeamMemberFn(ctx, req)
|
|
case "remove_member":
|
|
return removeTeamMemberFn(ctx, req)
|
|
case "add_repo":
|
|
return addTeamRepoFn(ctx, req)
|
|
case "remove_repo":
|
|
return removeTeamRepoFn(ctx, req)
|
|
default:
|
|
return to.ErrorResult(fmt.Errorf("unknown method: %s", method))
|
|
}
|
|
}
|
|
|
|
func getTeamFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called getTeamFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "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))
|
|
}
|
|
team, _, err := client.GetTeam(id)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get team err: %v", err))
|
|
}
|
|
return to.TextResult(slimTeam(team))
|
|
}
|
|
|
|
func listTeamMembersFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called listTeamMembersFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "id")
|
|
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.ListTeamMembersOptions{
|
|
ListOptions: gitea_sdk.ListOptions{
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
},
|
|
}
|
|
members, _, err := client.ListTeamMembers(id, opt)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("list team members err: %v", err))
|
|
}
|
|
return to.TextResult(slimUsers(members))
|
|
}
|
|
|
|
func listTeamReposFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called listTeamReposFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "id")
|
|
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.ListTeamRepositoriesOptions{
|
|
ListOptions: gitea_sdk.ListOptions{
|
|
Page: page,
|
|
PageSize: pageSize,
|
|
},
|
|
}
|
|
repos, _, err := client.ListTeamRepositories(id, opt)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("list team repos err: %v", err))
|
|
}
|
|
return to.TextResult(slimRepos(repos))
|
|
}
|
|
|
|
func createTeamFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called createTeamFn")
|
|
org, err := params.GetString(req.GetArguments(), "org")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
name, err := params.GetString(req.GetArguments(), "name")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
description, _ := req.GetArguments()["description"].(string)
|
|
permission, _ := req.GetArguments()["permission"].(string)
|
|
|
|
opt := gitea_sdk.CreateTeamOption{
|
|
Name: name,
|
|
Description: description,
|
|
Permission: gitea_sdk.AccessMode(permission),
|
|
}
|
|
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
team, _, err := client.CreateTeam(org, opt)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("create team err: %v", err))
|
|
}
|
|
return to.TextResult(slimTeam(team))
|
|
}
|
|
|
|
func editTeamFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called editTeamFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "id")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
|
|
opt := gitea_sdk.EditTeamOption{}
|
|
if name, ok := req.GetArguments()["name"].(string); ok && name != "" {
|
|
opt.Name = name
|
|
}
|
|
if description, ok := req.GetArguments()["description"].(string); ok && description != "" {
|
|
opt.Description = &description
|
|
}
|
|
if permission, ok := req.GetArguments()["permission"].(string); ok {
|
|
opt.Permission = gitea_sdk.AccessMode(permission)
|
|
}
|
|
|
|
client, err := gitea.ClientFromContext(ctx)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
|
}
|
|
_, err = client.EditTeam(id, opt)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("edit team err: %v", err))
|
|
}
|
|
return to.TextResult("Team updated successfully")
|
|
}
|
|
|
|
func deleteTeamFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called deleteTeamFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "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.DeleteTeam(id)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("delete team err: %v", err))
|
|
}
|
|
return to.TextResult("Team deleted successfully")
|
|
}
|
|
|
|
func addTeamMemberFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called addTeamMemberFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "id")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
user, err := params.GetString(req.GetArguments(), "user")
|
|
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.AddTeamMember(id, user)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("add team member err: %v", err))
|
|
}
|
|
return to.TextResult("Team member added successfully")
|
|
}
|
|
|
|
func removeTeamMemberFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called removeTeamMemberFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "id")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
user, err := params.GetString(req.GetArguments(), "user")
|
|
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.RemoveTeamMember(id, user)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("remove team member err: %v", err))
|
|
}
|
|
return to.TextResult("Team member removed successfully")
|
|
}
|
|
|
|
func addTeamRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called addTeamRepoFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "id")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
org, err := params.GetString(req.GetArguments(), "org")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
repo, err := params.GetString(req.GetArguments(), "repo")
|
|
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.AddTeamRepository(id, org, repo)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("add team repo err: %v", err))
|
|
}
|
|
return to.TextResult("Team repository added successfully")
|
|
}
|
|
|
|
func removeTeamRepoFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("Called removeTeamRepoFn")
|
|
id, err := params.GetIndex(req.GetArguments(), "id")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
org, err := params.GetString(req.GetArguments(), "org")
|
|
if err != nil {
|
|
return to.ErrorResult(err)
|
|
}
|
|
repo, err := params.GetString(req.GetArguments(), "repo")
|
|
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.RemoveTeamRepository(id, org, repo)
|
|
if err != nil {
|
|
return to.ErrorResult(fmt.Errorf("remove team repo err: %v", err))
|
|
}
|
|
return to.TextResult("Team repository removed successfully")
|
|
}
|
|
|
|
func slimTeam(t *gitea_sdk.Team) map[string]any {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
orgName := ""
|
|
if t.Organization != nil {
|
|
orgName = t.Organization.Name
|
|
}
|
|
return map[string]any{
|
|
"id": t.ID,
|
|
"name": t.Name,
|
|
"description": t.Description,
|
|
"permission": t.Permission,
|
|
"org_name": orgName,
|
|
}
|
|
}
|
|
|
|
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,
|
|
"avatar_url": u.AvatarURL,
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func slimRepos(repos []*gitea_sdk.Repository) []map[string]any {
|
|
out := make([]map[string]any, 0, len(repos))
|
|
for _, r := range repos {
|
|
out = append(out, map[string]any{
|
|
"id": r.ID,
|
|
"name": r.Name,
|
|
"full_name": r.FullName,
|
|
})
|
|
}
|
|
return out
|
|
}
|