mirror of
https://gitea.com/gitea/gitea-mcp.git
synced 2026-08-30 03:57:45 +00:00
feat(project): add project management tools
Add read and write tools for Gitea projects, including project and column CRUD and issue-to-project-column management. Bump gitea.dev/sdk to include ProjectsService. Assisted-by: Codet:codet-internal
This commit is contained in:
@@ -0,0 +1,608 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/gitea-mcp/pkg/annotation"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/gitea"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/params"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/to"
|
||||
"gitea.com/gitea/gitea-mcp/pkg/tool"
|
||||
|
||||
gitea_sdk "gitea.dev/sdk"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
var Tool = tool.New("project")
|
||||
|
||||
const (
|
||||
ProjectReadToolName = "project_read"
|
||||
ProjectWriteToolName = "project_write"
|
||||
)
|
||||
|
||||
var (
|
||||
ProjectReadTool = tool.NewDefinition(
|
||||
ProjectReadToolName,
|
||||
"Read projects: list projects, get a project, list columns, get a column, or list issues in a column.",
|
||||
annotation.ReadOnly("Read projects"),
|
||||
tool.String("method", tool.Required(), tool.Enum("list", "get", "list_columns", "get_column", "list_column_issues")),
|
||||
tool.String("scope", tool.Required(), tool.Enum("repo", "org", "user", "current_user")),
|
||||
tool.String("owner", tool.Description("repo owner for 'repo' scope, org name for 'org' scope, username for 'user' scope")),
|
||||
tool.String("repo", tool.Description("repo name for 'repo' scope")),
|
||||
tool.String("state", tool.Default("all"), tool.Enum("open", "closed", "all")),
|
||||
tool.Number("project_id", tool.Description("project ID for 'get' and column methods")),
|
||||
tool.Number("column_id", tool.Description("column ID for 'get_column' and 'list_column_issues'")),
|
||||
tool.Number("page", tool.Description(params.PageDesc), tool.Default(1), tool.Minimum(1)),
|
||||
tool.Number("per_page", tool.Description(params.PaginationDesc), tool.Default(30), tool.Minimum(1)),
|
||||
)
|
||||
|
||||
ProjectWriteTool = tool.NewDefinition(
|
||||
ProjectWriteToolName,
|
||||
"Write projects: create, update, or delete projects and columns; reorder columns; add, remove, or move issues.",
|
||||
annotation.Destructive("Create, update, or delete projects and project columns"),
|
||||
tool.String("method", tool.Required(), tool.Enum("create", "update", "delete", "create_column", "update_column", "delete_column", "set_default_column", "move_columns", "add_issue", "remove_issue", "move_issue")),
|
||||
tool.String("scope", tool.Required(), tool.Enum("repo", "org", "current_user")),
|
||||
tool.String("owner", tool.Description("repo owner for 'repo' scope, org name for 'org' scope")),
|
||||
tool.String("repo", tool.Description("repo name for 'repo' scope")),
|
||||
tool.Number("project_id", tool.Description("project ID (required except for 'create')")),
|
||||
tool.Number("column_id", tool.Description("column ID (required for column and issue methods)")),
|
||||
tool.Number("issue_id", tool.Description("global issue ID (required for 'add_issue', 'remove_issue', and 'move_issue')")),
|
||||
tool.String("title", tool.Description("for 'create', 'update', 'create_column', and 'update_column'")),
|
||||
tool.String("description", tool.Description("for 'create' and 'update'")),
|
||||
tool.String("template_type", tool.Enum("none", "basic_kanban", "bug_triage")),
|
||||
tool.String("card_type", tool.Enum("text_only", "images_and_text")),
|
||||
tool.String("state", tool.Enum("open", "closed")),
|
||||
tool.String("color", tool.Description("6-digit hex column color, e.g. #FF0000")),
|
||||
tool.Number("sorting", tool.Description("position for 'update_column' and 'move_issue'")),
|
||||
tool.Array("column_ids", tool.Description("ordered column IDs for 'move_columns'"), tool.Items(map[string]any{"type": "number"})),
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Tool.RegisterRead(tool.ServerTool{
|
||||
Tool: ProjectReadTool,
|
||||
Handler: projectReadFn,
|
||||
})
|
||||
Tool.RegisterWrite(tool.ServerTool{
|
||||
Tool: ProjectWriteTool,
|
||||
Handler: projectWriteFn,
|
||||
})
|
||||
}
|
||||
|
||||
type projectScopeInfo struct {
|
||||
scope gitea_sdk.ProjectScope
|
||||
kind string
|
||||
}
|
||||
|
||||
func getProjectScope(args map[string]any, writable bool) (projectScopeInfo, error) {
|
||||
scopeName := params.GetOptionalString(args, "scope", "")
|
||||
switch scopeName {
|
||||
case "repo":
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
return projectScopeInfo{}, err
|
||||
}
|
||||
repo, err := params.GetString(args, "repo")
|
||||
if err != nil {
|
||||
return projectScopeInfo{}, err
|
||||
}
|
||||
return projectScopeInfo{scope: gitea_sdk.RepoProjectScope(owner, repo), kind: "repo"}, nil
|
||||
case "org":
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
return projectScopeInfo{}, err
|
||||
}
|
||||
return projectScopeInfo{scope: gitea_sdk.OrgProjectScope(owner), kind: "org"}, nil
|
||||
case "user":
|
||||
owner, err := params.GetString(args, "owner")
|
||||
if err != nil {
|
||||
return projectScopeInfo{}, err
|
||||
}
|
||||
if writable {
|
||||
return projectScopeInfo{}, errors.New("scope 'user' only supports listing projects")
|
||||
}
|
||||
return projectScopeInfo{scope: gitea_sdk.UserProjectScope(owner), kind: "user"}, nil
|
||||
case "current_user":
|
||||
return projectScopeInfo{scope: gitea_sdk.CurrentUserProjectScope(), kind: "current_user"}, nil
|
||||
default:
|
||||
if scopeName == "" {
|
||||
return projectScopeInfo{}, errors.New("scope is required")
|
||||
}
|
||||
return projectScopeInfo{}, fmt.Errorf("unknown scope: %s", scopeName)
|
||||
}
|
||||
}
|
||||
|
||||
func projectReadFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
method, err := params.GetString(args, "method")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
switch method {
|
||||
case "list":
|
||||
return listProjectsFn(ctx, args)
|
||||
case "get":
|
||||
return getProjectFn(ctx, args)
|
||||
case "list_columns":
|
||||
return listProjectColumnsFn(ctx, args)
|
||||
case "get_column":
|
||||
return getProjectColumnFn(ctx, args)
|
||||
case "list_column_issues":
|
||||
return listProjectColumnIssuesFn(ctx, args)
|
||||
default:
|
||||
return to.ErrorResult(fmt.Errorf("unknown method: %s", method))
|
||||
}
|
||||
}
|
||||
|
||||
func projectWriteFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
method, err := params.GetString(args, "method")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
switch method {
|
||||
case "create":
|
||||
return createProjectFn(ctx, args)
|
||||
case "update":
|
||||
return updateProjectFn(ctx, args)
|
||||
case "delete":
|
||||
return deleteProjectFn(ctx, args)
|
||||
case "create_column":
|
||||
return createProjectColumnFn(ctx, args)
|
||||
case "update_column":
|
||||
return updateProjectColumnFn(ctx, args)
|
||||
case "delete_column":
|
||||
return deleteProjectColumnFn(ctx, args)
|
||||
case "set_default_column":
|
||||
return setDefaultProjectColumnFn(ctx, args)
|
||||
case "move_columns":
|
||||
return moveProjectColumnsFn(ctx, args)
|
||||
case "add_issue":
|
||||
return addIssueToProjectColumnFn(ctx, args)
|
||||
case "remove_issue":
|
||||
return removeIssueFromProjectColumnFn(ctx, args)
|
||||
case "move_issue":
|
||||
return moveProjectIssueFn(ctx, args)
|
||||
default:
|
||||
return to.ErrorResult(fmt.Errorf("unknown method: %s", method))
|
||||
}
|
||||
}
|
||||
|
||||
func listProjectsFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, false)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
state := params.GetOptionalString(args, "state", "all")
|
||||
page, pageSize := params.GetPagination(args, 30)
|
||||
opt := gitea_sdk.ListProjectsOptions{
|
||||
State: gitea_sdk.StateType(state),
|
||||
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))
|
||||
}
|
||||
projects, _, err := client.Projects.ListProjects(ctx, info.scope, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list projects err: %v", err))
|
||||
}
|
||||
return to.TextResult(slimProjects(projects))
|
||||
}
|
||||
|
||||
func getProjectFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, false)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_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))
|
||||
}
|
||||
project, _, err := client.Projects.GetProject(ctx, info.scope, projectID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get project/%d err: %v", projectID, err))
|
||||
}
|
||||
return to.TextResult(slimProject(project))
|
||||
}
|
||||
|
||||
func listProjectColumnsFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, false)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(args, 30)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
columns, _, err := client.Projects.ListProjectColumns(ctx, info.scope, projectID, gitea_sdk.ListOptions{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list project/%d/columns err: %v", projectID, err))
|
||||
}
|
||||
return to.TextResult(slimProjectColumns(columns))
|
||||
}
|
||||
|
||||
func getProjectColumnFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, false)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnID, err := params.GetIndex(args, "column_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))
|
||||
}
|
||||
column, _, err := client.Projects.GetProjectColumn(ctx, info.scope, projectID, columnID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get project/%d/columns/%d err: %v", projectID, columnID, err))
|
||||
}
|
||||
return to.TextResult(slimProjectColumn(column))
|
||||
}
|
||||
|
||||
func listProjectColumnIssuesFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, false)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnID, err := params.GetIndex(args, "column_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
page, pageSize := params.GetPagination(args, 30)
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
issues, _, err := client.Projects.ListProjectColumnIssues(ctx, info.scope, projectID, columnID, gitea_sdk.ListOptions{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("list project/%d/columns/%d/issues err: %v", projectID, columnID, err))
|
||||
}
|
||||
return to.TextResult(slimProjectIssues(issues))
|
||||
}
|
||||
|
||||
func createProjectFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
title, err := params.GetString(args, "title")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
opt := gitea_sdk.CreateProjectOption{Title: title}
|
||||
if description, ok := args["description"].(string); ok {
|
||||
opt.Description = description
|
||||
}
|
||||
if templateType, ok := args["template_type"].(string); ok {
|
||||
opt.TemplateType = gitea_sdk.ProjectTemplateType(templateType)
|
||||
}
|
||||
if cardType, ok := args["card_type"].(string); ok {
|
||||
opt.CardType = gitea_sdk.ProjectCardType(cardType)
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
project, _, err := client.Projects.CreateProject(ctx, info.scope, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create project err: %v", err))
|
||||
}
|
||||
return to.TextResult(slimProject(project))
|
||||
}
|
||||
|
||||
func updateProjectFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
opt := gitea_sdk.EditProjectOption{
|
||||
Title: params.GetPresentStringPtr(args, "title"),
|
||||
Description: params.GetPresentStringPtr(args, "description"),
|
||||
}
|
||||
if cardType, ok := args["card_type"].(string); ok {
|
||||
value := gitea_sdk.ProjectCardType(cardType)
|
||||
opt.CardType = &value
|
||||
}
|
||||
if state, ok := args["state"].(string); ok {
|
||||
value := gitea_sdk.StateType(state)
|
||||
opt.State = &value
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
project, _, err := client.Projects.EditProject(ctx, info.scope, projectID, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("update project/%d err: %v", projectID, err))
|
||||
}
|
||||
return to.TextResult(slimProject(project))
|
||||
}
|
||||
|
||||
func deleteProjectFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_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.Projects.DeleteProject(ctx, info.scope, projectID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete project/%d err: %v", projectID, err))
|
||||
}
|
||||
return to.TextResult("Project deleted successfully")
|
||||
}
|
||||
|
||||
func createProjectColumnFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
title, err := params.GetString(args, "title")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
opt := gitea_sdk.CreateProjectColumnOption{Title: title}
|
||||
if color, ok := args["color"].(string); ok {
|
||||
opt.Color = color
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
column, _, err := client.Projects.CreateProjectColumn(ctx, info.scope, projectID, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("create project/%d/column err: %v", projectID, err))
|
||||
}
|
||||
return to.TextResult(slimProjectColumn(column))
|
||||
}
|
||||
|
||||
func updateProjectColumnFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnID, err := params.GetIndex(args, "column_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
opt := gitea_sdk.EditProjectColumnOption{
|
||||
Title: params.GetPresentStringPtr(args, "title"),
|
||||
Color: params.GetPresentStringPtr(args, "color"),
|
||||
}
|
||||
if sorting, ok := optionalIntPtr(args, "sorting"); ok {
|
||||
opt.Sorting = sorting
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
column, _, err := client.Projects.EditProjectColumn(ctx, info.scope, projectID, columnID, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("update project/%d/columns/%d err: %v", projectID, columnID, err))
|
||||
}
|
||||
return to.TextResult(slimProjectColumn(column))
|
||||
}
|
||||
|
||||
func deleteProjectColumnFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnID, err := params.GetIndex(args, "column_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.Projects.DeleteProjectColumn(ctx, info.scope, projectID, columnID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("delete project/%d/columns/%d err: %v", projectID, columnID, err))
|
||||
}
|
||||
return to.TextResult("Project column deleted successfully")
|
||||
}
|
||||
|
||||
func setDefaultProjectColumnFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnID, err := params.GetIndex(args, "column_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.Projects.SetDefaultProjectColumn(ctx, info.scope, projectID, columnID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("set project/%d default column/%d err: %v", projectID, columnID, err))
|
||||
}
|
||||
return to.TextResult("Project default column set successfully")
|
||||
}
|
||||
|
||||
func moveProjectColumnsFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnIDs, err := params.GetInt64Slice(args, "column_ids")
|
||||
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.Projects.MoveProjectColumns(ctx, info.scope, projectID, gitea_sdk.MoveProjectColumnsOption{ColumnIDs: columnIDs})
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("move project/%d columns err: %v", projectID, err))
|
||||
}
|
||||
return to.TextResult("Project columns moved successfully")
|
||||
}
|
||||
|
||||
func addIssueToProjectColumnFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnID, err := params.GetIndex(args, "column_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
issueID, err := params.GetIndex(args, "issue_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.Projects.AddIssueToProjectColumn(ctx, info.scope, projectID, columnID, issueID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("add issue/%d to project/%d/columns/%d err: %v", issueID, projectID, columnID, err))
|
||||
}
|
||||
return to.TextResult("Issue added to project column successfully")
|
||||
}
|
||||
|
||||
func removeIssueFromProjectColumnFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnID, err := params.GetIndex(args, "column_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
issueID, err := params.GetIndex(args, "issue_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.Projects.RemoveIssueFromProjectColumn(ctx, info.scope, projectID, columnID, issueID)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("remove issue/%d from project/%d/columns/%d err: %v", issueID, projectID, columnID, err))
|
||||
}
|
||||
return to.TextResult("Issue removed from project column successfully")
|
||||
}
|
||||
|
||||
func moveProjectIssueFn(ctx context.Context, args map[string]any) (*mcp.CallToolResult, error) {
|
||||
info, err := getProjectScope(args, true)
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
projectID, err := params.GetIndex(args, "project_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
issueID, err := params.GetIndex(args, "issue_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
columnID, err := params.GetIndex(args, "column_id")
|
||||
if err != nil {
|
||||
return to.ErrorResult(err)
|
||||
}
|
||||
opt := gitea_sdk.MoveProjectIssueOption{ColumnID: columnID}
|
||||
if sorting, ok := optionalInt64Ptr(args, "sorting"); ok {
|
||||
opt.Sorting = sorting
|
||||
}
|
||||
client, err := gitea.ClientFromContext(ctx)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
|
||||
}
|
||||
_, err = client.Projects.MoveProjectIssue(ctx, info.scope, projectID, issueID, opt)
|
||||
if err != nil {
|
||||
return to.ErrorResult(fmt.Errorf("move issue/%d in project/%d err: %v", issueID, projectID, err))
|
||||
}
|
||||
return to.TextResult("Project issue moved successfully")
|
||||
}
|
||||
|
||||
func optionalIntPtr(args map[string]any, key string) (*int, bool) {
|
||||
value, ok := params.ToInt64(args[key])
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
converted := int(value)
|
||||
return &converted, true
|
||||
}
|
||||
|
||||
func optionalInt64Ptr(args map[string]any, key string) (*int64, bool) {
|
||||
value, ok := params.ToInt64(args[key])
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return &value, true
|
||||
}
|
||||
Reference in New Issue
Block a user