plane_v2_api_handlers.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package data
  2. import (
  3. "context"
  4. "github.com/mark3labs/mcp-go/mcp"
  5. "fwh-toolbox-server/cmd/mcp/tools/helper"
  6. )
  7. // planeV2RawResult 将 Plane V2 API 返回的原始 JSON bytes 转为 MCP 结果。
  8. // 原始字节先经流式 PII 脱敏(优化 B),脱敏失败(含非法 JSON)则返回错误、拒绝输出。
  9. // 结果较大时自动落盘到本地文件(/tmp/fwh-toolbox-agent/results/),
  10. // 返回 localPath + 预览,避免大数据占用 LLM 上下文。
  11. func planeV2RawResult(data []byte, err error, action string) (*mcp.CallToolResult, error) {
  12. if err != nil {
  13. return helper.ServiceError(action, err), nil
  14. }
  15. return helper.BytesResultOrFileMasked(data, "plane_v2_"+action), nil
  16. }
  17. // === Workspace Handlers ===
  18. func handlePlaneV2ListWorkspaces(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  19. data, err := planeV2Svc.ListWorkspaces()
  20. return planeV2RawResult(data, err, "列出工作空间")
  21. }
  22. func handlePlaneV2GetWorkspace(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  23. slug, err := req.RequireString("workspace_slug")
  24. if err != nil {
  25. return helper.ParamError("workspace_slug"), nil
  26. }
  27. data, apiErr := planeV2Svc.GetWorkspace(slug)
  28. return planeV2RawResult(data, apiErr, "获取工作空间详情")
  29. }
  30. // === Project Handlers ===
  31. func handlePlaneV2ListProjects(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  32. slug, err := req.RequireString("workspace_slug")
  33. if err != nil {
  34. return helper.ParamError("workspace_slug"), nil
  35. }
  36. data, apiErr := planeV2Svc.ListProjects(slug)
  37. return planeV2RawResult(data, apiErr, "列出项目")
  38. }
  39. func handlePlaneV2GetProject(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  40. slug, _ := req.RequireString("workspace_slug")
  41. projectId, err := req.RequireString("project_id")
  42. if err != nil {
  43. return helper.ParamError("project_id"), nil
  44. }
  45. data, apiErr := planeV2Svc.GetProject(slug, projectId)
  46. return planeV2RawResult(data, apiErr, "获取项目详情")
  47. }
  48. // === Issue Handlers ===
  49. func handlePlaneV2ListIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  50. slug, _ := req.RequireString("workspace_slug")
  51. projectId, _ := req.RequireString("project_id")
  52. data, err := planeV2Svc.ListIssues(slug, projectId)
  53. return planeV2RawResult(data, err, "列出工作项")
  54. }
  55. func handlePlaneV2GetIssue(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  56. slug, _ := req.RequireString("workspace_slug")
  57. projectId, _ := req.RequireString("project_id")
  58. issueId, err := req.RequireString("issue_id")
  59. if err != nil {
  60. return helper.ParamError("issue_id"), nil
  61. }
  62. data, apiErr := planeV2Svc.GetIssue(slug, projectId, issueId)
  63. return planeV2RawResult(data, apiErr, "获取工作项详情")
  64. }
  65. func handlePlaneV2GetSubIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  66. slug, _ := req.RequireString("workspace_slug")
  67. projectId, _ := req.RequireString("project_id")
  68. issueId, err := req.RequireString("issue_id")
  69. if err != nil {
  70. return helper.ParamError("issue_id"), nil
  71. }
  72. data, apiErr := planeV2Svc.GetSubIssues(slug, projectId, issueId)
  73. return planeV2RawResult(data, apiErr, "获取子工作项")
  74. }
  75. func handlePlaneV2GetIssueActivity(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  76. slug, _ := req.RequireString("workspace_slug")
  77. projectId, _ := req.RequireString("project_id")
  78. issueId, err := req.RequireString("issue_id")
  79. if err != nil {
  80. return helper.ParamError("issue_id"), nil
  81. }
  82. data, apiErr := planeV2Svc.GetIssueActivity(slug, projectId, issueId)
  83. return planeV2RawResult(data, apiErr, "获取工作项活动历史")
  84. }
  85. // === Intake Handlers ===
  86. func handlePlaneV2ListIntakeIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  87. slug, _ := req.RequireString("workspace_slug")
  88. projectId, _ := req.RequireString("project_id")
  89. status := req.GetString("status", "")
  90. data, err := planeV2Svc.ListIntakeIssues(slug, projectId, status)
  91. return planeV2RawResult(data, err, "列出收件箱工作项")
  92. }
  93. func handlePlaneV2GetIntakeIssue(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  94. slug, _ := req.RequireString("workspace_slug")
  95. projectId, _ := req.RequireString("project_id")
  96. issueId, err := req.RequireString("issue_id")
  97. if err != nil {
  98. return helper.ParamError("issue_id"), nil
  99. }
  100. data, apiErr := planeV2Svc.GetIntakeIssue(slug, projectId, issueId)
  101. return planeV2RawResult(data, apiErr, "获取收件箱工作项详情")
  102. }
  103. // === Cycle Handlers ===
  104. func handlePlaneV2ListCycles(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  105. slug, _ := req.RequireString("workspace_slug")
  106. projectId, _ := req.RequireString("project_id")
  107. data, err := planeV2Svc.ListCycles(slug, projectId)
  108. return planeV2RawResult(data, err, "列出周期")
  109. }
  110. func handlePlaneV2GetCycle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  111. slug, _ := req.RequireString("workspace_slug")
  112. projectId, _ := req.RequireString("project_id")
  113. cycleId, err := req.RequireString("cycle_id")
  114. if err != nil {
  115. return helper.ParamError("cycle_id"), nil
  116. }
  117. data, apiErr := planeV2Svc.GetCycle(slug, projectId, cycleId)
  118. return planeV2RawResult(data, apiErr, "获取周期详情")
  119. }
  120. // === Module Handlers ===
  121. func handlePlaneV2ListModules(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  122. slug, _ := req.RequireString("workspace_slug")
  123. projectId, _ := req.RequireString("project_id")
  124. data, err := planeV2Svc.ListModules(slug, projectId)
  125. return planeV2RawResult(data, err, "列出模块")
  126. }
  127. func handlePlaneV2GetModule(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  128. slug, _ := req.RequireString("workspace_slug")
  129. projectId, _ := req.RequireString("project_id")
  130. moduleId, err := req.RequireString("module_id")
  131. if err != nil {
  132. return helper.ParamError("module_id"), nil
  133. }
  134. data, apiErr := planeV2Svc.GetModule(slug, projectId, moduleId)
  135. return planeV2RawResult(data, apiErr, "获取模块详情")
  136. }
  137. // === Label Handlers ===
  138. func handlePlaneV2ListLabels(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  139. slug, _ := req.RequireString("workspace_slug")
  140. projectId, _ := req.RequireString("project_id")
  141. data, err := planeV2Svc.ListLabels(slug, projectId)
  142. return planeV2RawResult(data, err, "列出标签")
  143. }
  144. // === State Handlers ===
  145. func handlePlaneV2ListStates(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  146. slug, _ := req.RequireString("workspace_slug")
  147. projectId, _ := req.RequireString("project_id")
  148. data, err := planeV2Svc.ListStates(slug, projectId)
  149. return planeV2RawResult(data, err, "列出状态")
  150. }
  151. // === Page Handlers ===
  152. func handlePlaneV2ListPages(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  153. slug, _ := req.RequireString("workspace_slug")
  154. projectId, _ := req.RequireString("project_id")
  155. data, err := planeV2Svc.ListPages(slug, projectId)
  156. return planeV2RawResult(data, err, "列出页面")
  157. }
  158. func handlePlaneV2GetPage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  159. slug, _ := req.RequireString("workspace_slug")
  160. projectId, _ := req.RequireString("project_id")
  161. pageId, err := req.RequireString("page_id")
  162. if err != nil {
  163. return helper.ParamError("page_id"), nil
  164. }
  165. data, apiErr := planeV2Svc.GetPage(slug, projectId, pageId)
  166. return planeV2RawResult(data, apiErr, "获取页面详情")
  167. }
  168. func handlePlaneV2GetPageDescription(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  169. slug, _ := req.RequireString("workspace_slug")
  170. projectId, _ := req.RequireString("project_id")
  171. pageId, err := req.RequireString("page_id")
  172. if err != nil {
  173. return helper.ParamError("page_id"), nil
  174. }
  175. data, apiErr := planeV2Svc.GetPageDescription(slug, projectId, pageId)
  176. return planeV2RawResult(data, apiErr, "获取页面描述内容")
  177. }
  178. // === Member Handlers ===
  179. func handlePlaneV2ListWorkspaceMembers(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  180. slug, err := req.RequireString("workspace_slug")
  181. if err != nil {
  182. return helper.ParamError("workspace_slug"), nil
  183. }
  184. data, apiErr := planeV2Svc.ListWorkspaceMembers(slug)
  185. return planeV2RawResult(data, apiErr, "列出工作空间成员")
  186. }
  187. func handlePlaneV2ListProjectMembers(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  188. slug, _ := req.RequireString("workspace_slug")
  189. projectId, _ := req.RequireString("project_id")
  190. data, err := planeV2Svc.ListProjectMembers(slug, projectId)
  191. return planeV2RawResult(data, err, "列出项目成员")
  192. }
  193. // === Sticky Handlers ===
  194. func handlePlaneV2ListStickies(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  195. slug, err := req.RequireString("workspace_slug")
  196. if err != nil {
  197. return helper.ParamError("workspace_slug"), nil
  198. }
  199. data, apiErr := planeV2Svc.ListStickies(slug)
  200. return planeV2RawResult(data, apiErr, "列出便签")
  201. }