@desplega.ai/agent-swarm 1.123.0 → 1.124.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +2 -1
  2. package/dist/{actions-e4c9f0xy.js → actions-z3d0fk7z.js} +6 -6
  3. package/dist/{app-vmvmj2v3.js → app-q2rc9tdr.js} +4 -4
  4. package/dist/{assistant-yn7a792a.js → assistant-3stjq7fz.js} +9 -9
  5. package/dist/{boot-reembed-xg1r86h2.js → boot-reembed-5867whnb.js} +3 -3
  6. package/dist/{boot-reembed-chxty50a.js → boot-reembed-v6r1df32.js} +4 -4
  7. package/dist/{boot-scrub-logs-exwddvp5.js → boot-scrub-logs-8b8jegk3.js} +2 -2
  8. package/dist/{cli-1592nfd6.js → cli-0925phzv.js} +1 -1
  9. package/dist/{cli-hbbxmhec.js → cli-2yr6edr7.js} +4 -4
  10. package/dist/{cli-8cq4z4x7.js → cli-3z4q09tn.js} +182 -19
  11. package/dist/{cli-vz9drmce.js → cli-5jn6wnd9.js} +17 -8
  12. package/dist/{cli-5arfywtm.js → cli-5ncdb7ff.js} +19 -9
  13. package/dist/{cli-3tv9z4wx.js → cli-92bqgbgy.js} +1 -1
  14. package/dist/{cli-fjf04hpa.js → cli-dq2ssyf0.js} +48 -56
  15. package/dist/{cli-mngsj9fx.js → cli-gkvf8d83.js} +1 -1
  16. package/dist/{cli-mepk1j0t.js → cli-gzjsamwc.js} +2 -2
  17. package/dist/{cli-c9m59f83.js → cli-h89g0qrd.js} +1 -1
  18. package/dist/{cli-n8mf7x4b.js → cli-jp4mdj9d.js} +2 -2
  19. package/dist/{cli-822d4bjk.js → cli-kk58tg33.js} +7 -7
  20. package/dist/{cli-9hj1adgp.js → cli-mcez7z4p.js} +1 -1
  21. package/dist/{cli-byv3720g.js → cli-mmk5vagg.js} +2 -2
  22. package/dist/{cli-zdp11t8m.js → cli-p0ec4vv6.js} +1 -1
  23. package/dist/{cli-h2say6xk.js → cli-p0zgv8pj.js} +3 -1
  24. package/dist/{cli-8ef5ky75.js → cli-perb20ma.js} +1 -1
  25. package/dist/{cli-zkj6f2sk.js → cli-qf7acvyr.js} +90 -4
  26. package/dist/{cli-kmm1rwmp.js → cli-qwsagsmg.js} +1 -1
  27. package/dist/{cli-we91ywvy.js → cli-sqv6c9b8.js} +6 -6
  28. package/dist/{cli-np0yhmmk.js → cli-vm20sh8d.js} +3 -3
  29. package/dist/{cli-p11rf59d.js → cli-wn3m6q54.js} +3 -3
  30. package/dist/{cli-mkpf4ndf.js → cli-x268say8.js} +2976 -5621
  31. package/dist/{cli-ngbsk4yx.js → cli-y43n987v.js} +1 -1
  32. package/dist/{cli-4509mwm5.js → cli-ykthjw29.js} +8 -8
  33. package/dist/{cli-9eka6jwv.js → cli-zxb72ctk.js} +2 -2
  34. package/dist/cli.js +12 -10
  35. package/dist/{commands-sjk3zfyr.js → commands-sda76yn8.js} +2 -2
  36. package/dist/{db-2je5agjb.js → db-vn9e4rqj.js} +6 -2
  37. package/dist/{e2b-2s4psnbf.js → e2b-q345y8s4.js} +0 -4
  38. package/dist/{handlers-yzv369hf.js → handlers-xfja18ac.js} +9 -9
  39. package/dist/{hook-rfsj2r8x.js → hook-2medrp47.js} +1 -40
  40. package/dist/{http-v9rr144m.js → http-937pyjfc.js} +99 -44
  41. package/dist/{index-nr2199m6.js → index-2bqd2q9v.js} +11 -11
  42. package/dist/{index-3xtk777g.js → index-gw4en7jg.js} +9 -9
  43. package/dist/{index-c3tan44f.js → index-knqczmnm.js} +10 -10
  44. package/dist/{index-smjdc3h9.js → index-vah60vxp.js} +8 -8
  45. package/dist/{keepalive-5ds53bjd.js → keepalive-20rd6hxh.js} +5 -5
  46. package/dist/{lead-5xhez25b.js → lead-7w2k3a49.js} +21 -21
  47. package/dist/{maintenance-b43d24q6.js → maintenance-p3x918yw.js} +4 -4
  48. package/dist/{oauth-refresh-sweep-qe286vn2.js → oauth-refresh-sweep-shm0nzm4.js} +4 -4
  49. package/dist/{onboard-c4r3ds3z.js → onboard-py9dnf54.js} +6 -2
  50. package/dist/{otel-impl-nv3rjw61.js → otel-impl-7yjkfg68.js} +1 -1
  51. package/dist/{pi-mono-adapter-2syx1x4d.js → pi-mono-adapter-z6h98fva.js} +4 -38
  52. package/dist/{pricing-refresh-5ct2wp4k.js → pricing-refresh-3snb6370.js} +4 -4
  53. package/dist/{rbac-roles-m502w0qv.js → rbac-roles-5e72bbpk.js} +3 -3
  54. package/dist/{rbac-roles-b82cxpxn.js → rbac-roles-gqxdp8st.js} +4 -4
  55. package/dist/{seed-pricing-jhjphbf0.js → seed-pricing-bg42d7h2.js} +3 -3
  56. package/dist/{setup-zbaspydq.js → setup-nzk3kbbq.js} +2 -2
  57. package/dist/{worker-snvhpax5.js → worker-0p1jdzkm.js} +21 -21
  58. package/openapi.json +146 -1
  59. package/package.json +3 -1
  60. package/src/be/db.ts +81 -4
  61. package/src/be/models-catalog.ts +156 -0
  62. package/src/be/modelsdev-cache.ts +14 -1
  63. package/src/be/pricing-refresh.ts +2 -0
  64. package/src/be/scripts/typecheck.ts +17 -4
  65. package/src/be/seed-scripts/catalog/task-context-gathering.ts +1 -1
  66. package/src/commands/onboard/env-generator.ts +12 -0
  67. package/src/e2b/env.ts +0 -4
  68. package/src/hooks/hook.ts +0 -67
  69. package/src/http/all-routes.ts +1 -0
  70. package/src/http/index.ts +2 -0
  71. package/src/http/models-catalog.ts +65 -0
  72. package/src/http/workflows.ts +16 -4
  73. package/src/prompts/base-prompt.ts +8 -7
  74. package/src/prompts/session-templates.ts +106 -12
  75. package/src/providers/pi-mono-adapter.ts +12 -2
  76. package/src/providers/pi-mono-extension.ts +0 -64
  77. package/src/providers/pricing-sources.md +16 -1
  78. package/src/scripts-runtime/eval-harness.ts +18 -1
  79. package/src/scripts-runtime/swarm-sdk.ts +10 -0
  80. package/src/scripts-runtime/types/stdlib.d.ts +13 -0
  81. package/src/scripts-runtime/types/swarm-sdk.d.ts +13 -0
  82. package/src/server-user.ts +4 -7
  83. package/src/slack/blocks.ts +21 -8
  84. package/src/slack/responses.ts +27 -51
  85. package/src/slack/watcher.ts +22 -5
  86. package/src/telemetry.ts +169 -3
  87. package/src/tests/base-prompt.test.ts +79 -0
  88. package/src/tests/mcp-tools-user.test.ts +62 -16
  89. package/src/tests/mcp-tools.test.ts +6 -3
  90. package/src/tests/memory-edit.test.ts +21 -4
  91. package/src/tests/model-groups-live-catalog.test.ts +65 -0
  92. package/src/tests/models-catalog.test.ts +119 -0
  93. package/src/tests/oauth-access-token-tool.test.ts +71 -1
  94. package/src/tests/pricing-refresh.test.ts +25 -0
  95. package/src/tests/prompt-template-session.test.ts +142 -5
  96. package/src/tests/rbac-charact-skills.test.ts +81 -14
  97. package/src/tests/script-apis-mcp.test.ts +56 -8
  98. package/src/tests/scripts-mcp-e2e.test.ts +157 -8
  99. package/src/tests/scripts-typecheck.test.ts +17 -0
  100. package/src/tests/sdk-allowlist.test.ts +34 -1
  101. package/src/tests/seed-scripts.test.ts +43 -0
  102. package/src/tests/slack-blocks.test.ts +2 -1
  103. package/src/tests/slack-inline-output.test.ts +24 -30
  104. package/src/tests/slack-watcher.test.ts +42 -0
  105. package/src/tests/steer-task-tool.test.ts +37 -8
  106. package/src/tests/steering-transport.test.ts +18 -3
  107. package/src/tests/swarm-tool-result-gate.test.ts +399 -0
  108. package/src/tests/task-tools-ctx.test.ts +49 -17
  109. package/src/tests/task-tools-ownership.test.ts +41 -13
  110. package/src/tests/telemetry-init.test.ts +376 -2
  111. package/src/tests/tool-output-agent-id.test.ts +14 -2
  112. package/src/tests/workflow-http-v2.test.ts +116 -0
  113. package/src/tools/accept-steer.ts +45 -53
  114. package/src/tools/cancel-task.ts +31 -41
  115. package/src/tools/context-diff.ts +19 -63
  116. package/src/tools/context-history.ts +22 -51
  117. package/src/tools/create-channel.ts +24 -39
  118. package/src/tools/create-metric.ts +19 -82
  119. package/src/tools/create-page.ts +20 -81
  120. package/src/tools/credential-bindings/tool.ts +125 -246
  121. package/src/tools/db-query.ts +13 -24
  122. package/src/tools/delete-channel.ts +25 -71
  123. package/src/tools/delete-page.ts +27 -75
  124. package/src/tools/get-metrics.ts +29 -22
  125. package/src/tools/get-swarm.ts +44 -15
  126. package/src/tools/get-task-details.ts +150 -32
  127. package/src/tools/get-tasks.ts +63 -29
  128. package/src/tools/inject-learning.ts +8 -38
  129. package/src/tools/join-swarm.ts +45 -47
  130. package/src/tools/kv/kv-delete.ts +13 -37
  131. package/src/tools/kv/kv-get.ts +38 -29
  132. package/src/tools/kv/kv-incr.ts +28 -56
  133. package/src/tools/kv/kv-list.ts +40 -31
  134. package/src/tools/kv/kv-set.ts +34 -76
  135. package/src/tools/list-channels.ts +24 -17
  136. package/src/tools/list-services.ts +37 -45
  137. package/src/tools/manage-user.ts +53 -66
  138. package/src/tools/mcp-servers/mcp-server-create.ts +19 -55
  139. package/src/tools/mcp-servers/mcp-server-delete.ts +9 -39
  140. package/src/tools/mcp-servers/mcp-server-get.ts +9 -35
  141. package/src/tools/mcp-servers/mcp-server-install.ts +13 -60
  142. package/src/tools/mcp-servers/mcp-server-list.ts +28 -26
  143. package/src/tools/mcp-servers/mcp-server-uninstall.ts +10 -36
  144. package/src/tools/mcp-servers/mcp-server-update.ts +13 -57
  145. package/src/tools/memory-delete.ts +16 -46
  146. package/src/tools/memory-edit.ts +48 -49
  147. package/src/tools/memory-get.ts +40 -36
  148. package/src/tools/memory-rate.ts +14 -44
  149. package/src/tools/memory-search.ts +46 -52
  150. package/src/tools/my-agent-info.ts +39 -33
  151. package/src/tools/oauth-access-token.ts +9 -24
  152. package/src/tools/poll-task.ts +50 -77
  153. package/src/tools/post-message.ts +24 -38
  154. package/src/tools/prompt-templates/delete.ts +16 -48
  155. package/src/tools/prompt-templates/get.ts +45 -42
  156. package/src/tools/prompt-templates/list.ts +29 -44
  157. package/src/tools/prompt-templates/preview.ts +14 -40
  158. package/src/tools/prompt-templates/set.ts +30 -52
  159. package/src/tools/read-messages.ts +45 -54
  160. package/src/tools/register-agentmail-inbox.ts +28 -84
  161. package/src/tools/register-kapso-number.ts +20 -50
  162. package/src/tools/register-service.ts +36 -44
  163. package/src/tools/repos/get-repos.ts +29 -21
  164. package/src/tools/repos/update-repo.ts +30 -27
  165. package/src/tools/request-human-input.ts +10 -33
  166. package/src/tools/resolve-user.ts +59 -16
  167. package/src/tools/schedules/create-schedule.ts +58 -173
  168. package/src/tools/schedules/delete-schedule.ts +16 -56
  169. package/src/tools/schedules/list-schedules.ts +41 -67
  170. package/src/tools/schedules/patch-schedule.ts +57 -153
  171. package/src/tools/schedules/run-schedule-now.ts +18 -59
  172. package/src/tools/schedules/update-schedule.ts +57 -153
  173. package/src/tools/script-apis.ts +46 -28
  174. package/src/tools/script-common.ts +129 -34
  175. package/src/tools/script-connections/tool.ts +59 -154
  176. package/src/tools/script-query-types.ts +43 -3
  177. package/src/tools/script-run.ts +41 -2
  178. package/src/tools/script-runs.ts +69 -1
  179. package/src/tools/script-search.ts +24 -1
  180. package/src/tools/script-upsert.ts +18 -3
  181. package/src/tools/send-task.ts +32 -81
  182. package/src/tools/skills/skill-create.ts +14 -39
  183. package/src/tools/skills/skill-delete.ts +14 -42
  184. package/src/tools/skills/skill-get-file.ts +10 -43
  185. package/src/tools/skills/skill-get.ts +9 -32
  186. package/src/tools/skills/skill-install-remote.ts +16 -52
  187. package/src/tools/skills/skill-install.ts +14 -52
  188. package/src/tools/skills/skill-list.ts +24 -26
  189. package/src/tools/skills/skill-publish.ts +18 -54
  190. package/src/tools/skills/skill-search.ts +21 -18
  191. package/src/tools/skills/skill-sync-remote.ts +12 -24
  192. package/src/tools/skills/skill-uninstall.ts +10 -26
  193. package/src/tools/skills/skill-update.ts +24 -81
  194. package/src/tools/slack-delete.ts +8 -32
  195. package/src/tools/slack-download-file.ts +17 -57
  196. package/src/tools/slack-list-channels.ts +16 -43
  197. package/src/tools/slack-post.ts +11 -40
  198. package/src/tools/slack-read.ts +33 -103
  199. package/src/tools/slack-reply.ts +16 -53
  200. package/src/tools/slack-start-thread.ts +14 -55
  201. package/src/tools/slack-update.ts +8 -35
  202. package/src/tools/slack-upload-file.ts +22 -122
  203. package/src/tools/steer-task.ts +22 -35
  204. package/src/tools/store-progress.ts +11 -26
  205. package/src/tools/swarm-config/delete-config.ts +19 -57
  206. package/src/tools/swarm-config/get-config.ts +36 -44
  207. package/src/tools/swarm-config/list-config.ts +36 -44
  208. package/src/tools/swarm-config/set-config.ts +35 -73
  209. package/src/tools/swarm-x.ts +20 -26
  210. package/src/tools/task-action.ts +26 -46
  211. package/src/tools/task-tool-ctx.ts +5 -13
  212. package/src/tools/tracker/tracker-link-task.ts +8 -22
  213. package/src/tools/tracker/tracker-map-agent.ts +8 -22
  214. package/src/tools/tracker/tracker-status.ts +27 -19
  215. package/src/tools/tracker/tracker-sync-status.ts +8 -18
  216. package/src/tools/tracker/tracker-unlink.ts +4 -16
  217. package/src/tools/unregister-service.ts +19 -59
  218. package/src/tools/update-profile.ts +60 -100
  219. package/src/tools/update-service-status.ts +42 -67
  220. package/src/tools/utils.ts +189 -8
  221. package/src/tools/whatsapp-message.ts +19 -32
  222. package/src/tools/workflows/cancel-workflow-run.ts +4 -16
  223. package/src/tools/workflows/create-workflow.ts +9 -38
  224. package/src/tools/workflows/delete-workflow.ts +5 -17
  225. package/src/tools/workflows/get-workflow-run.ts +41 -31
  226. package/src/tools/workflows/get-workflow.ts +10 -21
  227. package/src/tools/workflows/list-workflow-runs.ts +107 -34
  228. package/src/tools/workflows/list-workflows.ts +5 -17
  229. package/src/tools/workflows/patch-workflow-node.ts +11 -39
  230. package/src/tools/workflows/patch-workflow.ts +11 -36
  231. package/src/tools/workflows/retry-workflow-run.ts +4 -16
  232. package/src/tools/workflows/trigger-workflow.ts +22 -58
  233. package/src/tools/workflows/update-workflow.ts +10 -42
  234. package/templates/skills/swarm-scripts/SKILL.md +1 -1
@@ -4,7 +4,7 @@ import { getAgentById } from "@/be/db";
4
4
  import { can } from "@/rbac";
5
5
  import { getSlackApp } from "@/slack/app";
6
6
  import { parseSlackTs } from "@/slack/message-text";
7
- import { createToolRegistrar } from "@/tools/utils";
7
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
8
8
 
9
9
  export const registerSlackDeleteTool = (server: McpServer) => {
10
10
  createToolRegistrar(server)(
@@ -24,25 +24,16 @@ export const registerSlackDeleteTool = (server: McpServer) => {
24
24
  "Timestamp of the message to delete. Accepts the dotted form (1783411554.596189), the 'p' deep-link form (p1783411554596189), or a full Slack permalink URL.",
25
25
  ),
26
26
  }),
27
- outputSchema: z.object({
28
- success: z.boolean(),
29
- message: z.string(),
30
- }),
27
+ outputSchema: swarmToolOutputSchema(),
31
28
  },
32
29
  async ({ channelId, messageTs }, requestInfo, _meta) => {
33
30
  if (!requestInfo.agentId) {
34
- return {
35
- content: [{ type: "text", text: "Agent ID not found." }],
36
- structuredContent: { success: false, message: "Agent ID not found." },
37
- };
31
+ return toolErr("Agent ID not found.");
38
32
  }
39
33
 
40
34
  const agent = getAgentById(requestInfo.agentId);
41
35
  if (!agent) {
42
- return {
43
- content: [{ type: "text", text: "Agent not found." }],
44
- structuredContent: { success: false, message: "Agent not found." },
45
- };
36
+ return toolErr("Agent not found.");
46
37
  }
47
38
 
48
39
  const decision = can({
@@ -52,31 +43,19 @@ export const registerSlackDeleteTool = (server: McpServer) => {
52
43
  source: "mcp",
53
44
  });
54
45
  if (!decision.allow) {
55
- return {
56
- content: [{ type: "text", text: "Deleting Slack messages requires lead privileges." }],
57
- structuredContent: {
58
- success: false,
59
- message: "Deleting Slack messages requires lead privileges.",
60
- },
61
- };
46
+ return toolErr("Deleting Slack messages requires lead privileges.");
62
47
  }
63
48
 
64
49
  const app = getSlackApp();
65
50
  if (!app) {
66
- return {
67
- content: [{ type: "text", text: "Slack not configured." }],
68
- structuredContent: { success: false, message: "Slack not configured." },
69
- };
51
+ return toolErr("Slack not configured.");
70
52
  }
71
53
 
72
54
  try {
73
55
  const ts = parseSlackTs(messageTs);
74
56
  await app.client.chat.delete({ channel: channelId, ts });
75
57
 
76
- return {
77
- content: [{ type: "text", text: "Message deleted successfully." }],
78
- structuredContent: { success: true, message: "Message deleted successfully." },
79
- };
58
+ return toolOk("Message deleted successfully.");
80
59
  } catch (error) {
81
60
  const errorCode = (error as { data?: { error?: string } } | undefined)?.data?.error;
82
61
  const errorMsg = error instanceof Error ? error.message : String(error);
@@ -99,10 +78,7 @@ export const registerSlackDeleteTool = (server: McpServer) => {
99
78
  message = `Failed to delete message: ${errorMsg}`;
100
79
  }
101
80
 
102
- return {
103
- content: [{ type: "text", text: message }],
104
- structuredContent: { success: false, message },
105
- };
81
+ return toolErr(message);
106
82
  }
107
83
  },
108
84
  );
@@ -3,7 +3,7 @@ import * as z from "zod";
3
3
  import { getAgentById } from "@/be/db";
4
4
  import { getSlackApp } from "@/slack/app";
5
5
  import { DEFAULT_DOWNLOAD_DIR, downloadFile, getFileInfo } from "@/slack/files";
6
- import { createToolRegistrar } from "@/tools/utils";
6
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
7
7
 
8
8
  export const registerSlackDownloadFileTool = (server: McpServer) => {
9
9
  createToolRegistrar(server)(
@@ -35,58 +35,41 @@ export const registerSlackDownloadFileTool = (server: McpServer) => {
35
35
  .optional()
36
36
  .describe("Filename to use when saving. Only used if savePath is a directory."),
37
37
  }),
38
- outputSchema: z.object({
39
- success: z.boolean(),
40
- message: z.string(),
38
+ outputSchema: swarmToolOutputSchema({
41
39
  savedPath: z.string().optional(),
42
40
  fileInfo: z
43
- .object({
44
- id: z.string(),
45
- name: z.string(),
46
- mimetype: z.string(),
47
- size: z.number(),
41
+ .looseObject({
42
+ id: z.string().optional(),
43
+ name: z.string().optional(),
44
+ mimetype: z.string().optional(),
45
+ size: z.number().optional(),
48
46
  })
49
47
  .optional(),
50
48
  }),
51
49
  },
52
50
  async ({ fileId, url, savePath, filename }, requestInfo, _meta) => {
53
51
  if (!requestInfo.agentId) {
54
- return {
55
- content: [{ type: "text", text: "Agent ID not found." }],
56
- structuredContent: { success: false, message: "Agent ID not found." },
57
- };
52
+ return toolErr("Agent ID not found.");
58
53
  }
59
54
 
60
55
  const agent = getAgentById(requestInfo.agentId);
61
56
  if (!agent) {
62
- return {
63
- content: [{ type: "text", text: "Agent not found." }],
64
- structuredContent: { success: false, message: "Agent not found." },
65
- };
57
+ return toolErr("Agent not found.");
66
58
  }
67
59
 
68
60
  // Must provide either fileId or url
69
61
  if (!fileId && !url) {
70
- return {
71
- content: [{ type: "text", text: "Must provide either fileId or url." }],
72
- structuredContent: { success: false, message: "Must provide either fileId or url." },
73
- };
62
+ return toolErr("Must provide either fileId or url.");
74
63
  }
75
64
 
76
65
  const app = getSlackApp();
77
66
  if (!app) {
78
- return {
79
- content: [{ type: "text", text: "Slack not configured." }],
80
- structuredContent: { success: false, message: "Slack not configured." },
81
- };
67
+ return toolErr("Slack not configured.");
82
68
  }
83
69
 
84
70
  const token = process.env.SLACK_BOT_TOKEN;
85
71
  if (!token) {
86
- return {
87
- content: [{ type: "text", text: "Slack bot token not configured." }],
88
- structuredContent: { success: false, message: "Slack bot token not configured." },
89
- };
72
+ return toolErr("Slack bot token not configured.");
90
73
  }
91
74
 
92
75
  try {
@@ -104,10 +87,7 @@ export const registerSlackDownloadFileTool = (server: McpServer) => {
104
87
  if (fileId) {
105
88
  const info = await getFileInfo(app.client, fileId);
106
89
  if (!info) {
107
- return {
108
- content: [{ type: "text", text: `File not found: ${fileId}` }],
109
- structuredContent: { success: false, message: `File not found: ${fileId}` },
110
- };
90
+ return toolErr(`File not found: ${fileId}`);
111
91
  }
112
92
 
113
93
  downloadUrl = info.url_private_download;
@@ -120,10 +100,7 @@ export const registerSlackDownloadFileTool = (server: McpServer) => {
120
100
  }
121
101
 
122
102
  if (!downloadUrl) {
123
- return {
124
- content: [{ type: "text", text: "No download URL available." }],
125
- structuredContent: { success: false, message: "No download URL available." },
126
- };
103
+ return toolErr("No download URL available.");
127
104
  }
128
105
 
129
106
  // Determine save path
@@ -145,31 +122,14 @@ export const registerSlackDownloadFileTool = (server: McpServer) => {
145
122
  });
146
123
 
147
124
  if (!result.success) {
148
- return {
149
- content: [{ type: "text", text: `Failed to download file: ${result.error}` }],
150
- structuredContent: {
151
- success: false,
152
- message: `Failed to download file: ${result.error}`,
153
- },
154
- };
125
+ return toolErr(`Failed to download file: ${result.error}`);
155
126
  }
156
127
 
157
128
  const successMsg = `File downloaded successfully to ${result.savedPath}`;
158
- return {
159
- content: [{ type: "text", text: successMsg }],
160
- structuredContent: {
161
- success: true,
162
- message: successMsg,
163
- savedPath: result.savedPath,
164
- fileInfo,
165
- },
166
- };
129
+ return toolOk(successMsg, { data: { savedPath: result.savedPath, fileInfo } });
167
130
  } catch (error) {
168
131
  const errorMsg = error instanceof Error ? error.message : String(error);
169
- return {
170
- content: [{ type: "text", text: `Failed to download file: ${errorMsg}` }],
171
- structuredContent: { success: false, message: `Failed to download file: ${errorMsg}` },
172
- };
132
+ return toolErr(`Failed to download file: ${errorMsg}`);
173
133
  }
174
134
  },
175
135
  );
@@ -2,13 +2,13 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { getAgentById } from "@/be/db";
4
4
  import { getSlackApp } from "@/slack/app";
5
- import { createToolRegistrar } from "@/tools/utils";
5
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
6
6
 
7
- const SlackChannelSchema = z.object({
8
- id: z.string(),
9
- name: z.string(),
10
- type: z.enum(["public", "private", "dm", "mpim"]),
11
- isMember: z.boolean(),
7
+ const SlackChannelSchema = z.looseObject({
8
+ id: z.string().optional(),
9
+ name: z.string().optional(),
10
+ type: z.enum(["public", "private", "dm", "mpim"]).optional(),
11
+ isMember: z.boolean().optional(),
12
12
  numMembers: z.number().optional(),
13
13
  });
14
14
 
@@ -36,34 +36,23 @@ export const registerSlackListChannelsTool = (server: McpServer) => {
36
36
  .default(100)
37
37
  .describe("Maximum number of channels to retrieve (default: 100, max: 200)."),
38
38
  }),
39
- outputSchema: z.object({
40
- success: z.boolean(),
41
- message: z.string(),
42
- channels: z.array(SlackChannelSchema),
39
+ outputSchema: swarmToolOutputSchema({
40
+ channels: z.array(SlackChannelSchema).optional(),
43
41
  }),
44
42
  },
45
43
  async ({ types, limit = 100 }, requestInfo, _meta) => {
46
44
  if (!requestInfo.agentId) {
47
- return {
48
- content: [{ type: "text", text: "Agent ID not found." }],
49
- structuredContent: { success: false, message: "Agent ID not found.", channels: [] },
50
- };
45
+ return toolErr("Agent ID not found.", { data: { channels: [] } });
51
46
  }
52
47
 
53
48
  const agent = getAgentById(requestInfo.agentId);
54
49
  if (!agent) {
55
- return {
56
- content: [{ type: "text", text: "Agent not found." }],
57
- structuredContent: { success: false, message: "Agent not found.", channels: [] },
58
- };
50
+ return toolErr("Agent not found.", { data: { channels: [] } });
59
51
  }
60
52
 
61
53
  const app = getSlackApp();
62
54
  if (!app) {
63
- return {
64
- content: [{ type: "text", text: "Slack not configured." }],
65
- structuredContent: { success: false, message: "Slack not configured.", channels: [] },
66
- };
55
+ return toolErr("Slack not configured.", { data: { channels: [] } });
67
56
  }
68
57
 
69
58
  try {
@@ -141,29 +130,13 @@ export const registerSlackListChannelsTool = (server: McpServer) => {
141
130
  })
142
131
  .join("\n");
143
132
 
144
- return {
145
- content: [
146
- {
147
- type: "text",
148
- text: `Found ${channels.length} channel(s):\n\n${textOutput}`,
149
- },
150
- ],
151
- structuredContent: {
152
- success: true,
153
- message: `Found ${channels.length} channel(s).`,
154
- channels,
155
- },
156
- };
133
+ return toolOk(`Found ${channels.length} channel(s).`, {
134
+ details: textOutput || undefined,
135
+ data: { channels },
136
+ });
157
137
  } catch (error) {
158
138
  const errorMsg = error instanceof Error ? error.message : String(error);
159
- return {
160
- content: [{ type: "text", text: `Failed to list Slack channels: ${errorMsg}` }],
161
- structuredContent: {
162
- success: false,
163
- message: `Failed to list Slack channels: ${errorMsg}`,
164
- channels: [],
165
- },
166
- };
139
+ return toolErr(`Failed to list Slack channels: ${errorMsg}`, { data: { channels: [] } });
167
140
  }
168
141
  },
169
142
  );
@@ -5,7 +5,7 @@ import { can } from "@/rbac";
5
5
  import { getSlackApp } from "@/slack/app";
6
6
  import { withAutoJoin } from "@/slack/channel-join";
7
7
  import { markdownToSlack } from "@/slack/responses";
8
- import { createToolRegistrar } from "@/tools/utils";
8
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
9
9
 
10
10
  export const registerSlackPostTool = (server: McpServer) => {
11
11
  createToolRegistrar(server)(
@@ -26,26 +26,18 @@ export const registerSlackPostTool = (server: McpServer) => {
26
26
  "Optional parent message ts to thread under. Obtain via `slack-start-thread`. When omitted, posts as a new top-level message.",
27
27
  ),
28
28
  }),
29
- outputSchema: z.object({
30
- success: z.boolean(),
31
- message: z.string(),
29
+ outputSchema: swarmToolOutputSchema({
32
30
  messageTs: z.string().optional(),
33
31
  }),
34
32
  },
35
33
  async ({ channelId, message, threadTs }, requestInfo, _meta) => {
36
34
  if (!requestInfo.agentId) {
37
- return {
38
- content: [{ type: "text", text: "Agent ID not found." }],
39
- structuredContent: { success: false, message: "Agent ID not found." },
40
- };
35
+ return toolErr("Agent ID not found.");
41
36
  }
42
37
 
43
38
  const agent = getAgentById(requestInfo.agentId);
44
39
  if (!agent) {
45
- return {
46
- content: [{ type: "text", text: "Agent not found." }],
47
- structuredContent: { success: false, message: "Agent not found." },
48
- };
40
+ return toolErr("Agent not found.");
49
41
  }
50
42
 
51
43
  // Require lead privileges to post directly to channels
@@ -56,21 +48,12 @@ export const registerSlackPostTool = (server: McpServer) => {
56
48
  source: "mcp",
57
49
  });
58
50
  if (!decision.allow) {
59
- return {
60
- content: [{ type: "text", text: "Posting to Slack channels requires lead privileges." }],
61
- structuredContent: {
62
- success: false,
63
- message: "Posting to Slack channels requires lead privileges.",
64
- },
65
- };
51
+ return toolErr("Posting to Slack channels requires lead privileges.");
66
52
  }
67
53
 
68
54
  const app = getSlackApp();
69
55
  if (!app) {
70
- return {
71
- content: [{ type: "text", text: "Slack not configured." }],
72
- structuredContent: { success: false, message: "Slack not configured." },
73
- };
56
+ return toolErr("Slack not configured.");
74
57
  }
75
58
 
76
59
  try {
@@ -97,25 +80,13 @@ export const registerSlackPostTool = (server: McpServer) => {
97
80
 
98
81
  const messageTs = result.ts;
99
82
 
100
- return {
101
- content: [
102
- {
103
- type: "text",
104
- text: `Message posted successfully.${messageTs ? ` Message timestamp: ${messageTs}` : ""}`,
105
- },
106
- ],
107
- structuredContent: {
108
- success: true,
109
- message: "Message posted successfully.",
110
- messageTs,
111
- },
112
- };
83
+ return toolOk("Message posted successfully.", {
84
+ details: messageTs ? `Message timestamp: ${messageTs}` : undefined,
85
+ data: { messageTs },
86
+ });
113
87
  } catch (error) {
114
88
  const errorMsg = error instanceof Error ? error.message : String(error);
115
- return {
116
- content: [{ type: "text", text: `Failed to post message: ${errorMsg}` }],
117
- structuredContent: { success: false, message: `Failed to post message: ${errorMsg}` },
118
- };
89
+ return toolErr(`Failed to post message: ${errorMsg}`);
119
90
  }
120
91
  },
121
92
  );
@@ -6,7 +6,7 @@ import { getSlackApp } from "@/slack/app";
6
6
  import { withAutoJoin } from "@/slack/channel-join";
7
7
  import { downloadFile } from "@/slack/files";
8
8
  import { extractSlackMessageText } from "@/slack/message-text";
9
- import { createToolRegistrar } from "@/tools/utils";
9
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
10
10
 
11
11
  /**
12
12
  * Default download directory for auto-downloaded Slack files (inside MCP container).
@@ -14,22 +14,22 @@ import { createToolRegistrar } from "@/tools/utils";
14
14
  */
15
15
  const AUTO_DOWNLOAD_DIR = "/app/shared/downloads/slack";
16
16
 
17
- const SlackFileSchema = z.object({
18
- id: z.string(),
19
- name: z.string(),
20
- mimetype: z.string(),
21
- filetype: z.string(),
22
- size: z.number(),
23
- url_private_download: z.string(),
17
+ const SlackFileSchema = z.looseObject({
18
+ id: z.string().optional(),
19
+ name: z.string().optional(),
20
+ mimetype: z.string().optional(),
21
+ filetype: z.string().optional(),
22
+ size: z.number().optional(),
23
+ url_private_download: z.string().optional(),
24
24
  localPath: z.string().optional(),
25
25
  });
26
26
 
27
- const SlackMessageSchema = z.object({
27
+ const SlackMessageSchema = z.looseObject({
28
28
  user: z.string().optional(),
29
29
  username: z.string().optional(),
30
- isBot: z.boolean(),
31
- text: z.string(),
32
- ts: z.string(),
30
+ isBot: z.boolean().optional(),
31
+ text: z.string().optional(),
32
+ ts: z.string().optional(),
33
33
  files: z.array(SlackFileSchema).optional(),
34
34
  });
35
35
 
@@ -65,12 +65,10 @@ export const registerSlackReadTool = (server: McpServer) => {
65
65
  .default(true)
66
66
  .describe("Include file attachments in the response (default: true)."),
67
67
  }),
68
- outputSchema: z.object({
69
- success: z.boolean(),
70
- message: z.string(),
68
+ outputSchema: swarmToolOutputSchema({
71
69
  channelId: z.string().optional(),
72
70
  threadTs: z.string().optional(),
73
- messages: z.array(SlackMessageSchema),
71
+ messages: z.array(SlackMessageSchema).optional(),
74
72
  }),
75
73
  },
76
74
  async (
@@ -79,18 +77,12 @@ export const registerSlackReadTool = (server: McpServer) => {
79
77
  _meta,
80
78
  ) => {
81
79
  if (!requestInfo.agentId) {
82
- return {
83
- content: [{ type: "text", text: "Agent ID not found." }],
84
- structuredContent: { success: false, message: "Agent ID not found.", messages: [] },
85
- };
80
+ return toolErr("Agent ID not found.", { data: { messages: [] } });
86
81
  }
87
82
 
88
83
  const agent = getAgentById(requestInfo.agentId);
89
84
  if (!agent) {
90
- return {
91
- content: [{ type: "text", text: "Agent not found." }],
92
- structuredContent: { success: false, message: "Agent not found.", messages: [] },
93
- };
85
+ return toolErr("Agent not found.", { data: { messages: [] } });
94
86
  }
95
87
 
96
88
  let slackChannelId: string | undefined = channelId;
@@ -100,45 +92,21 @@ export const registerSlackReadTool = (server: McpServer) => {
100
92
  if (inboxMessageId) {
101
93
  const inboxMsg = getInboxMessageById(inboxMessageId);
102
94
  if (!inboxMsg) {
103
- return {
104
- content: [{ type: "text", text: "Inbox message not found." }],
105
- structuredContent: {
106
- success: false,
107
- message: "Inbox message not found.",
108
- messages: [],
109
- },
110
- };
95
+ return toolErr("Inbox message not found.", { data: { messages: [] } });
111
96
  }
112
97
  if (inboxMsg.agentId !== requestInfo.agentId) {
113
- return {
114
- content: [{ type: "text", text: "This inbox message is not yours." }],
115
- structuredContent: {
116
- success: false,
117
- message: "This inbox message is not yours.",
118
- messages: [],
119
- },
120
- };
98
+ return toolErr("This inbox message is not yours.", { data: { messages: [] } });
121
99
  }
122
100
  slackChannelId = inboxMsg.slackChannelId;
123
101
  slackThreadTs = inboxMsg.slackThreadTs;
124
102
  } else if (taskId) {
125
103
  const task = getTaskById(taskId);
126
104
  if (!task) {
127
- return {
128
- content: [{ type: "text", text: "Task not found." }],
129
- structuredContent: { success: false, message: "Task not found.", messages: [] },
130
- };
105
+ return toolErr("Task not found.", { data: { messages: [] } });
131
106
  }
132
107
  // Verify agent has context for this task
133
108
  if (task.agentId !== requestInfo.agentId && task.creatorAgentId !== requestInfo.agentId) {
134
- return {
135
- content: [{ type: "text", text: "You don't have context for this task." }],
136
- structuredContent: {
137
- success: false,
138
- message: "You don't have context for this task.",
139
- messages: [],
140
- },
141
- };
109
+ return toolErr("You don't have context for this task.", { data: { messages: [] } });
142
110
  }
143
111
  slackChannelId = task.slackChannelId;
144
112
  slackThreadTs = task.slackThreadTs;
@@ -151,45 +119,25 @@ export const registerSlackReadTool = (server: McpServer) => {
151
119
  source: "mcp",
152
120
  });
153
121
  if (!decision.allow) {
154
- return {
155
- content: [{ type: "text", text: "Direct channel access requires lead privileges." }],
156
- structuredContent: {
157
- success: false,
158
- message: "Direct channel access requires lead privileges.",
159
- messages: [],
160
- },
161
- };
122
+ return toolErr("Direct channel access requires lead privileges.", {
123
+ data: { messages: [] },
124
+ });
162
125
  }
163
126
  slackChannelId = channelId;
164
127
  slackThreadTs = threadTs;
165
128
  } else {
166
- return {
167
- content: [{ type: "text", text: "Must provide inboxMessageId, taskId, or channelId." }],
168
- structuredContent: {
169
- success: false,
170
- message: "Must provide inboxMessageId, taskId, or channelId.",
171
- messages: [],
172
- },
173
- };
129
+ return toolErr("Must provide inboxMessageId, taskId, or channelId.", {
130
+ data: { messages: [] },
131
+ });
174
132
  }
175
133
 
176
134
  if (!slackChannelId) {
177
- return {
178
- content: [{ type: "text", text: "No Slack channel context available." }],
179
- structuredContent: {
180
- success: false,
181
- message: "No Slack channel context available.",
182
- messages: [],
183
- },
184
- };
135
+ return toolErr("No Slack channel context available.", { data: { messages: [] } });
185
136
  }
186
137
 
187
138
  const app = getSlackApp();
188
139
  if (!app) {
189
- return {
190
- content: [{ type: "text", text: "Slack not configured." }],
191
- structuredContent: { success: false, message: "Slack not configured.", messages: [] },
192
- };
140
+ return toolErr("Slack not configured.", { data: { messages: [] } });
193
141
  }
194
142
 
195
143
  try {
@@ -370,31 +318,13 @@ export const registerSlackReadTool = (server: McpServer) => {
370
318
  })
371
319
  .join("\n\n");
372
320
 
373
- return {
374
- content: [
375
- {
376
- type: "text",
377
- text: `Retrieved ${messages.length} message(s) from Slack.\n\n${textOutput}`,
378
- },
379
- ],
380
- structuredContent: {
381
- success: true,
382
- message: `Retrieved ${messages.length} message(s).`,
383
- channelId: slackChannelId,
384
- threadTs: slackThreadTs,
385
- messages,
386
- },
387
- };
321
+ return toolOk(`Retrieved ${messages.length} message(s).`, {
322
+ details: textOutput || undefined,
323
+ data: { channelId: slackChannelId, threadTs: slackThreadTs, messages },
324
+ });
388
325
  } catch (error) {
389
326
  const errorMsg = error instanceof Error ? error.message : String(error);
390
- return {
391
- content: [{ type: "text", text: `Failed to read Slack messages: ${errorMsg}` }],
392
- structuredContent: {
393
- success: false,
394
- message: `Failed to read Slack messages: ${errorMsg}`,
395
- messages: [],
396
- },
397
- };
327
+ return toolErr(`Failed to read Slack messages: ${errorMsg}`, { data: { messages: [] } });
398
328
  }
399
329
  },
400
330
  );