plane_v2_api_handlers.go 9.1 KB

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