package data import ( "context" "github.com/mark3labs/mcp-go/mcp" "fwh-toolbox-server/cmd/mcp/tools/helper" "fwh-toolbox-server/repositories" ) // planeRawResult 将 Plane API 返回的原始 JSON bytes 转为 MCP 结果。 // 原始字节先经流式 PII 脱敏(优化 B),脱敏失败(含非法 JSON)则返回错误、拒绝输出。 func planeRawResult(data []byte, err error, action string) (*mcp.CallToolResult, error) { if err != nil { return helper.ServiceError(action, err), nil } return helper.BytesResultOrFileMasked(data, "plane_"+action), nil } // === Project Handlers === func handlePlaneListProjects(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { data, err := repositories.PlaneListProjects() return planeRawResult(data, err, "列出项目") } func handlePlaneGetProject(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { projectID, err := req.RequireString("project_id") if err != nil { return helper.ParamError("project_id"), nil } data, apiErr := repositories.PlaneGetProject(projectID) return planeRawResult(data, apiErr, "获取项目详情") } func handlePlaneGetProjectSummary(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { projectID, err := req.RequireString("project_id") if err != nil { return helper.ParamError("project_id"), nil } fields := req.GetString("fields", "") data, apiErr := repositories.PlaneGetProjectSummary(projectID, fields) return planeRawResult(data, apiErr, "获取项目摘要") } // === Work Item Handlers === func handlePlaneListWorkItems(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { params := make(map[string]string) if v := req.GetString("state", ""); v != "" { params["state"] = v } if v := req.GetString("priority", ""); v != "" { params["priority"] = v } if v := req.GetString("assignee", ""); v != "" { params["assignee"] = v } if v := req.GetString("label", ""); v != "" { params["label"] = v } data, err := repositories.PlaneListWorkItems(params) return planeRawResult(data, err, "列出工作项") } func handlePlaneGetWorkItem(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("work_item_id") if err != nil { return helper.ParamError("work_item_id"), nil } data, apiErr := repositories.PlaneGetWorkItem(id) return planeRawResult(data, apiErr, "获取工作项详情") } func handlePlaneGetWorkItemByIdentifier(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { identifier, err := req.RequireString("identifier") if err != nil { return helper.ParamError("identifier"), nil } seqID := int(req.GetFloat("sequence_id", 0)) if seqID <= 0 { return helper.ParamError("sequence_id"), nil } data, apiErr := repositories.PlaneGetWorkItemByIdentifier(identifier, seqID) return planeRawResult(data, apiErr, "通过标识符获取工作项") } func handlePlaneSearchWorkItems(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { query, err := req.RequireString("query") if err != nil { return helper.ParamError("query"), nil } limit := int(req.GetFloat("limit", 20)) data, apiErr := repositories.PlaneSearchWorkItems(query, limit, "") return planeRawResult(data, apiErr, "搜索工作项") } // === Cycle Handlers === func handlePlaneListCycles(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { data, err := repositories.PlaneListCycles() return planeRawResult(data, err, "列出迭代") } func handlePlaneGetCycle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("cycle_id") if err != nil { return helper.ParamError("cycle_id"), nil } data, apiErr := repositories.PlaneGetCycle(id) return planeRawResult(data, apiErr, "获取迭代详情") } func handlePlaneListCycleIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("cycle_id") if err != nil { return helper.ParamError("cycle_id"), nil } data, apiErr := repositories.PlaneListCycleIssues(id) return planeRawResult(data, apiErr, "列出迭代工作项") } // === Module Handlers === func handlePlaneListModules(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { data, err := repositories.PlaneListModules() return planeRawResult(data, err, "列出模块") } func handlePlaneGetModule(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("module_id") if err != nil { return helper.ParamError("module_id"), nil } data, apiErr := repositories.PlaneGetModule(id) return planeRawResult(data, apiErr, "获取模块详情") } func handlePlaneListModuleIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("module_id") if err != nil { return helper.ParamError("module_id"), nil } data, apiErr := repositories.PlaneListModuleIssues(id) return planeRawResult(data, apiErr, "列出模块工作项") } // === State / Label / Member Handlers === func handlePlaneListStates(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { data, err := repositories.PlaneListStates() return planeRawResult(data, err, "列出状态") } func handlePlaneListLabels(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { data, err := repositories.PlaneListLabels() return planeRawResult(data, err, "列出标签") } func handlePlaneListWorkspaceMembers(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { data, err := repositories.PlaneListWorkspaceMembers() return planeRawResult(data, err, "列出工作空间成员") } func handlePlaneListProjectMembers(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { data, err := repositories.PlaneListProjectMembers() return planeRawResult(data, err, "列出项目成员") } // === Comment / Activity / Link / Attachment / Relation Handlers === func handlePlaneListWorkItemComments(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("work_item_id") if err != nil { return helper.ParamError("work_item_id"), nil } data, apiErr := repositories.PlaneListWorkItemComments(id) return planeRawResult(data, apiErr, "列出评论") } func handlePlaneListWorkItemActivities(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("work_item_id") if err != nil { return helper.ParamError("work_item_id"), nil } data, apiErr := repositories.PlaneListWorkItemActivities(id) return planeRawResult(data, apiErr, "列出活动历史") } func handlePlaneListWorkItemLinks(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("work_item_id") if err != nil { return helper.ParamError("work_item_id"), nil } data, apiErr := repositories.PlaneListWorkItemLinks(id) return planeRawResult(data, apiErr, "列出链接") } func handlePlaneListWorkItemAttachments(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("work_item_id") if err != nil { return helper.ParamError("work_item_id"), nil } data, apiErr := repositories.PlaneListWorkItemAttachments(id) return planeRawResult(data, apiErr, "列出附件") } func handlePlaneListWorkItemRelations(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("work_item_id") if err != nil { return helper.ParamError("work_item_id"), nil } data, apiErr := repositories.PlaneListWorkItemRelations(id) return planeRawResult(data, apiErr, "列出关系") } // === Intake Handlers === func handlePlaneGetIntakeIssueList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { data, err := repositories.PlaneGetIntakeIssueList() if err != nil { return helper.ServiceError("查询Intake列表", err), nil } return helper.JSONResult(data), nil } func handlePlaneGetIntakeIssueDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { id, err := req.RequireString("intake_id") if err != nil { return helper.ParamError("intake_id"), nil } data, apiErr := repositories.PlaneGetIntakeIssueDetail(id) return planeRawResult(data, apiErr, "获取Intake详情") } // === V2 Web API: Page Handlers (已迁移到 plane_v2_api_handlers.go) ===