mirror of
https://gitea.com/gitea/gitea-mcp.git
synced 2026-08-27 02:27:45 +00:00
380e548f36
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
609 lines
20 KiB
Go
609 lines
20 KiB
Go
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
|
|
}
|