@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
@@ -10,7 +10,7 @@ import {
10
10
  import { getSlackApp } from "@/slack/app";
11
11
  import { withAutoJoin } from "@/slack/channel-join";
12
12
  import { markdownToSlack } from "@/slack/responses";
13
- import { createToolRegistrar } from "@/tools/utils";
13
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
14
14
 
15
15
  export const registerSlackReplyTool = (server: McpServer) => {
16
16
  createToolRegistrar(server)(
@@ -32,26 +32,18 @@ export const registerSlackReplyTool = (server: McpServer) => {
32
32
  .describe("The task ID with Slack context (for task-related threads)."),
33
33
  message: z.string().min(1).max(4000).describe("The message to send to the Slack thread."),
34
34
  }),
35
- outputSchema: z.object({
36
- success: z.boolean(),
37
- message: z.string(),
35
+ outputSchema: swarmToolOutputSchema({
38
36
  messageTs: z.string().optional(),
39
37
  }),
40
38
  },
41
39
  async ({ inboxMessageId, taskId, message }, requestInfo, _meta) => {
42
40
  if (!requestInfo.agentId) {
43
- return {
44
- content: [{ type: "text", text: "Agent ID not found." }],
45
- structuredContent: { success: false, message: "Agent ID not found." },
46
- };
41
+ return toolErr("Agent ID not found.");
47
42
  }
48
43
 
49
44
  const agent = getAgentById(requestInfo.agentId);
50
45
  if (!agent) {
51
- return {
52
- content: [{ type: "text", text: "Agent not found." }],
53
- structuredContent: { success: false, message: "Agent not found." },
54
- };
46
+ return toolErr("Agent not found.");
55
47
  }
56
48
 
57
49
  let slackChannelId: string | undefined;
@@ -61,16 +53,10 @@ export const registerSlackReplyTool = (server: McpServer) => {
61
53
  if (inboxMessageId) {
62
54
  const inboxMsg = getInboxMessageById(inboxMessageId);
63
55
  if (!inboxMsg) {
64
- return {
65
- content: [{ type: "text", text: "Inbox message not found." }],
66
- structuredContent: { success: false, message: "Inbox message not found." },
67
- };
56
+ return toolErr("Inbox message not found.");
68
57
  }
69
58
  if (inboxMsg.agentId !== requestInfo.agentId) {
70
- return {
71
- content: [{ type: "text", text: "This inbox message is not yours." }],
72
- structuredContent: { success: false, message: "This inbox message is not yours." },
73
- };
59
+ return toolErr("This inbox message is not yours.");
74
60
  }
75
61
  slackChannelId = inboxMsg.slackChannelId;
76
62
  slackThreadTs = inboxMsg.slackThreadTs;
@@ -80,41 +66,26 @@ export const registerSlackReplyTool = (server: McpServer) => {
80
66
  } else if (taskId) {
81
67
  const task = getTaskById(taskId);
82
68
  if (!task) {
83
- return {
84
- content: [{ type: "text", text: "Task not found." }],
85
- structuredContent: { success: false, message: "Task not found." },
86
- };
69
+ return toolErr("Task not found.");
87
70
  }
88
71
  // Verify agent has context for this task
89
72
  if (task.agentId !== requestInfo.agentId && task.creatorAgentId !== requestInfo.agentId) {
90
- return {
91
- content: [{ type: "text", text: "You don't have context for this task." }],
92
- structuredContent: { success: false, message: "You don't have context for this task." },
93
- };
73
+ return toolErr("You don't have context for this task.");
94
74
  }
95
75
  slackChannelId = task.slackChannelId;
96
76
  slackThreadTs = task.slackThreadTs;
97
77
  } else {
98
- return {
99
- content: [{ type: "text", text: "Must provide inboxMessageId or taskId." }],
100
- structuredContent: { success: false, message: "Must provide inboxMessageId or taskId." },
101
- };
78
+ return toolErr("Must provide inboxMessageId or taskId.");
102
79
  }
103
80
 
104
81
  if (!slackChannelId || !slackThreadTs) {
105
- return {
106
- content: [{ type: "text", text: "No Slack context available." }],
107
- structuredContent: { success: false, message: "No Slack context available." },
108
- };
82
+ return toolErr("No Slack context available.");
109
83
  }
110
84
 
111
85
  // Send the reply
112
86
  const app = getSlackApp();
113
87
  if (!app) {
114
- return {
115
- content: [{ type: "text", text: "Slack not configured." }],
116
- structuredContent: { success: false, message: "Slack not configured." },
117
- };
88
+ return toolErr("Slack not configured.");
118
89
  }
119
90
 
120
91
  try {
@@ -147,20 +118,12 @@ export const registerSlackReplyTool = (server: McpServer) => {
147
118
  console.log(`[Slack] Marked slackReplySent=1 for task ${taskId}`);
148
119
  }
149
120
 
150
- return {
151
- content: [
152
- {
153
- type: "text",
154
- text: `Reply sent successfully.${messageTs ? ` Message timestamp: ${messageTs}` : ""}`,
155
- },
156
- ],
157
- structuredContent: { success: true, message: "Reply sent successfully.", messageTs },
158
- };
121
+ return toolOk("Reply sent successfully.", {
122
+ details: messageTs ? `Message timestamp: ${messageTs}` : undefined,
123
+ data: { messageTs },
124
+ });
159
125
  } catch (error) {
160
- return {
161
- content: [{ type: "text", text: `Failed to send reply: ${error}` }],
162
- structuredContent: { success: false, message: `Failed to send reply: ${error}` },
163
- };
126
+ return toolErr(`Failed to send reply: ${error}`);
164
127
  }
165
128
  },
166
129
  );
@@ -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 registerSlackStartThreadTool = (server: McpServer) => {
11
11
  createToolRegistrar(server)(
@@ -20,9 +20,7 @@ export const registerSlackStartThreadTool = (server: McpServer) => {
20
20
  channelId: z.string().min(1).describe("The Slack channel ID to post to."),
21
21
  message: z.string().min(1).max(4000).describe("The message content to post."),
22
22
  }),
23
- outputSchema: z.object({
24
- success: z.boolean(),
25
- message: z.string(),
23
+ outputSchema: swarmToolOutputSchema({
26
24
  channelId: z.string().optional(),
27
25
  ts: z.string().optional(),
28
26
  messageTs: z.string().optional(),
@@ -30,18 +28,12 @@ export const registerSlackStartThreadTool = (server: McpServer) => {
30
28
  },
31
29
  async ({ channelId, message }, requestInfo, _meta) => {
32
30
  if (!requestInfo.agentId) {
33
- return {
34
- content: [{ type: "text", text: "Agent ID not found." }],
35
- structuredContent: { success: false, message: "Agent ID not found." },
36
- };
31
+ return toolErr("Agent ID not found.");
37
32
  }
38
33
 
39
34
  const agent = getAgentById(requestInfo.agentId);
40
35
  if (!agent) {
41
- return {
42
- content: [{ type: "text", text: "Agent not found." }],
43
- structuredContent: { success: false, message: "Agent not found." },
44
- };
36
+ return toolErr("Agent not found.");
45
37
  }
46
38
 
47
39
  const decision = can({
@@ -51,21 +43,12 @@ export const registerSlackStartThreadTool = (server: McpServer) => {
51
43
  source: "mcp",
52
44
  });
53
45
  if (!decision.allow) {
54
- return {
55
- content: [{ type: "text", text: "Posting to Slack channels requires lead privileges." }],
56
- structuredContent: {
57
- success: false,
58
- message: "Posting to Slack channels requires lead privileges.",
59
- },
60
- };
46
+ return toolErr("Posting to Slack channels requires lead privileges.");
61
47
  }
62
48
 
63
49
  const app = getSlackApp();
64
50
  if (!app) {
65
- return {
66
- content: [{ type: "text", text: "Slack not configured." }],
67
- structuredContent: { success: false, message: "Slack not configured." },
68
- };
51
+ return toolErr("Slack not configured.");
69
52
  }
70
53
 
71
54
  try {
@@ -93,42 +76,18 @@ export const registerSlackStartThreadTool = (server: McpServer) => {
93
76
  const resolvedChannelId = result.channel ?? channelId;
94
77
 
95
78
  if (!ts) {
96
- return {
97
- content: [
98
- {
99
- type: "text",
100
- text: "Message posted but Slack did not return a ts — cannot thread replies.",
101
- },
102
- ],
103
- structuredContent: {
104
- success: false,
105
- message: "Message posted but Slack did not return a ts — cannot thread replies.",
106
- channelId: resolvedChannelId,
107
- },
108
- };
79
+ return toolErr("Message posted but Slack did not return a ts — cannot thread replies.", {
80
+ data: { channelId: resolvedChannelId },
81
+ });
109
82
  }
110
83
 
111
- return {
112
- content: [
113
- {
114
- type: "text",
115
- text: `Thread started. channelId=${resolvedChannelId}, ts=${ts}. Pass ts as threadTs on slack-post to reply in-thread.`,
116
- },
117
- ],
118
- structuredContent: {
119
- success: true,
120
- message: "Thread started successfully.",
121
- channelId: resolvedChannelId,
122
- ts,
123
- messageTs: ts,
124
- },
125
- };
84
+ return toolOk("Thread started successfully.", {
85
+ details: `Thread started. channelId=${resolvedChannelId}, ts=${ts}. Pass ts as threadTs on slack-post to reply in-thread.`,
86
+ data: { channelId: resolvedChannelId, ts, messageTs: ts },
87
+ });
126
88
  } catch (error) {
127
89
  const errorMsg = error instanceof Error ? error.message : String(error);
128
- return {
129
- content: [{ type: "text", text: `Failed to start thread: ${errorMsg}` }],
130
- structuredContent: { success: false, message: `Failed to start thread: ${errorMsg}` },
131
- };
90
+ return toolErr(`Failed to start thread: ${errorMsg}`);
132
91
  }
133
92
  },
134
93
  );
@@ -5,7 +5,7 @@ import { can } from "@/rbac";
5
5
  import { getSlackApp } from "@/slack/app";
6
6
  import { parseSlackTs } from "@/slack/message-text";
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 registerSlackUpdateTool = (server: McpServer) => {
11
11
  createToolRegistrar(server)(
@@ -26,26 +26,18 @@ export const registerSlackUpdateTool = (server: McpServer) => {
26
26
  ),
27
27
  message: z.string().min(1).max(4000).describe("The new message content."),
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, messageTs, message }, 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
  const decision = can({
@@ -55,21 +47,12 @@ export const registerSlackUpdateTool = (server: McpServer) => {
55
47
  source: "mcp",
56
48
  });
57
49
  if (!decision.allow) {
58
- return {
59
- content: [{ type: "text", text: "Editing Slack messages requires lead privileges." }],
60
- structuredContent: {
61
- success: false,
62
- message: "Editing Slack messages requires lead privileges.",
63
- },
64
- };
50
+ return toolErr("Editing Slack messages requires lead privileges.");
65
51
  }
66
52
 
67
53
  const app = getSlackApp();
68
54
  if (!app) {
69
- return {
70
- content: [{ type: "text", text: "Slack not configured." }],
71
- structuredContent: { success: false, message: "Slack not configured." },
72
- };
55
+ return toolErr("Slack not configured.");
73
56
  }
74
57
 
75
58
  try {
@@ -91,14 +74,7 @@ export const registerSlackUpdateTool = (server: McpServer) => {
91
74
  ],
92
75
  });
93
76
 
94
- return {
95
- content: [{ type: "text", text: "Message updated successfully." }],
96
- structuredContent: {
97
- success: true,
98
- message: "Message updated successfully.",
99
- messageTs: result.ts,
100
- },
101
- };
77
+ return toolOk("Message updated successfully.", { data: { messageTs: result.ts } });
102
78
  } catch (error) {
103
79
  const errorCode = (error as { data?: { error?: string } } | undefined)?.data?.error;
104
80
  const errorMsg = error instanceof Error ? error.message : String(error);
@@ -124,10 +100,7 @@ export const registerSlackUpdateTool = (server: McpServer) => {
124
100
  message = `Failed to update message: ${errorMsg}`;
125
101
  }
126
102
 
127
- return {
128
- content: [{ type: "text", text: message }],
129
- structuredContent: { success: false, message },
130
- };
103
+ return toolErr(message);
131
104
  }
132
105
  },
133
106
  );
@@ -4,7 +4,7 @@ import { getAgentById, getInboxMessageById, getTaskById } from "@/be/db";
4
4
  import { can } from "@/rbac";
5
5
  import { getSlackApp } from "@/slack/app";
6
6
  import { MAX_FILE_SIZE, uploadFile } from "@/slack/files";
7
- import { createToolRegistrar } from "@/tools/utils";
7
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
8
8
 
9
9
  /**
10
10
  * Base directory for agent file operations.
@@ -152,9 +152,7 @@ export const registerSlackUploadFileTool = (server: McpServer) => {
152
152
  .optional()
153
153
  .describe("Optional message to post with the file."),
154
154
  }),
155
- outputSchema: z.object({
156
- success: z.boolean(),
157
- message: z.string(),
155
+ outputSchema: swarmToolOutputSchema({
158
156
  fileId: z.string().optional(),
159
157
  }),
160
158
  },
@@ -164,18 +162,12 @@ export const registerSlackUploadFileTool = (server: McpServer) => {
164
162
  _meta,
165
163
  ) => {
166
164
  if (!requestInfo.agentId) {
167
- return {
168
- content: [{ type: "text", text: "Agent ID not found." }],
169
- structuredContent: { success: false, message: "Agent ID not found." },
170
- };
165
+ return toolErr("Agent ID not found.");
171
166
  }
172
167
 
173
168
  const agent = getAgentById(requestInfo.agentId);
174
169
  if (!agent) {
175
- return {
176
- content: [{ type: "text", text: "Agent not found." }],
177
- structuredContent: { success: false, message: "Agent not found." },
178
- };
170
+ return toolErr("Agent not found.");
179
171
  }
180
172
 
181
173
  let slackChannelId: string | undefined;
@@ -185,33 +177,21 @@ export const registerSlackUploadFileTool = (server: McpServer) => {
185
177
  if (inboxMessageId) {
186
178
  const inboxMsg = getInboxMessageById(inboxMessageId);
187
179
  if (!inboxMsg) {
188
- return {
189
- content: [{ type: "text", text: "Inbox message not found." }],
190
- structuredContent: { success: false, message: "Inbox message not found." },
191
- };
180
+ return toolErr("Inbox message not found.");
192
181
  }
193
182
  if (inboxMsg.agentId !== requestInfo.agentId) {
194
- return {
195
- content: [{ type: "text", text: "This inbox message is not yours." }],
196
- structuredContent: { success: false, message: "This inbox message is not yours." },
197
- };
183
+ return toolErr("This inbox message is not yours.");
198
184
  }
199
185
  slackChannelId = inboxMsg.slackChannelId;
200
186
  slackThreadTs = inboxMsg.slackThreadTs;
201
187
  } else if (taskId) {
202
188
  const task = getTaskById(taskId);
203
189
  if (!task) {
204
- return {
205
- content: [{ type: "text", text: "Task not found." }],
206
- structuredContent: { success: false, message: "Task not found." },
207
- };
190
+ return toolErr("Task not found.");
208
191
  }
209
192
  // Verify agent has context for this task
210
193
  if (task.agentId !== requestInfo.agentId && task.creatorAgentId !== requestInfo.agentId) {
211
- return {
212
- content: [{ type: "text", text: "You don't have context for this task." }],
213
- structuredContent: { success: false, message: "You don't have context for this task." },
214
- };
194
+ return toolErr("You don't have context for this task.");
215
195
  }
216
196
  slackChannelId = task.slackChannelId;
217
197
  slackThreadTs = resolveTaskUploadThreadTs(task);
@@ -224,81 +204,35 @@ export const registerSlackUploadFileTool = (server: McpServer) => {
224
204
  source: "mcp",
225
205
  });
226
206
  if (!decision.allow) {
227
- return {
228
- content: [{ type: "text", text: "Direct channel access requires lead privileges." }],
229
- structuredContent: {
230
- success: false,
231
- message: "Direct channel access requires lead privileges.",
232
- },
233
- };
207
+ return toolErr("Direct channel access requires lead privileges.");
234
208
  }
235
209
  slackChannelId = channelId;
236
210
  slackThreadTs = threadTs;
237
211
  } else {
238
- return {
239
- content: [{ type: "text", text: "Must provide inboxMessageId, taskId, or channelId." }],
240
- structuredContent: {
241
- success: false,
242
- message: "Must provide inboxMessageId, taskId, or channelId.",
243
- },
244
- };
212
+ return toolErr("Must provide inboxMessageId, taskId, or channelId.");
245
213
  }
246
214
 
247
215
  if (!slackChannelId) {
248
- return {
249
- content: [{ type: "text", text: "No Slack channel context available." }],
250
- structuredContent: {
251
- success: false,
252
- message: "No Slack channel context available.",
253
- },
254
- };
216
+ return toolErr("No Slack channel context available.");
255
217
  }
256
218
 
257
219
  const app = getSlackApp();
258
220
  if (!app) {
259
- return {
260
- content: [{ type: "text", text: "Slack not configured." }],
261
- structuredContent: { success: false, message: "Slack not configured." },
262
- };
221
+ return toolErr("Slack not configured.");
263
222
  }
264
223
 
265
224
  // Validate: must provide either filePath OR content (not both, not neither)
266
225
  if (!filePath && !content) {
267
- return {
268
- content: [{ type: "text", text: "Must provide either filePath or content (base64)." }],
269
- structuredContent: {
270
- success: false,
271
- message: "Must provide either filePath or content (base64).",
272
- },
273
- };
226
+ return toolErr("Must provide either filePath or content (base64).");
274
227
  }
275
228
 
276
229
  if (filePath && content) {
277
- return {
278
- content: [
279
- {
280
- type: "text",
281
- text: "Cannot provide both filePath and content. Use one or the other.",
282
- },
283
- ],
284
- structuredContent: {
285
- success: false,
286
- message: "Cannot provide both filePath and content. Use one or the other.",
287
- },
288
- };
230
+ return toolErr("Cannot provide both filePath and content. Use one or the other.");
289
231
  }
290
232
 
291
233
  // If using content (base64), filename is required
292
234
  if (content && !filename) {
293
- return {
294
- content: [
295
- { type: "text", text: "filename is required when using base64 content upload." },
296
- ],
297
- structuredContent: {
298
- success: false,
299
- message: "filename is required when using base64 content upload.",
300
- },
301
- };
235
+ return toolErr("filename is required when using base64 content upload.");
302
236
  }
303
237
 
304
238
  let fileToUpload: string | Buffer;
@@ -319,10 +253,7 @@ export const registerSlackUploadFileTool = (server: McpServer) => {
319
253
  `Tips:\n` +
320
254
  `- Put the file in /workspace/shared/<agent-id>/ (e.g., '/workspace/shared/<agent-id>/my-file.png') and pass that path.\n` +
321
255
  `- Or pass the file inline via the \`content\` arg as base64 (with \`filename\`) so no shared-mount round-trip is needed.`;
322
- return {
323
- content: [{ type: "text", text: errorMsg }],
324
- structuredContent: { success: false, message: errorMsg },
325
- };
256
+ return toolErr(errorMsg);
326
257
  }
327
258
 
328
259
  const resolvedPath = pathResult.resolvedPath;
@@ -331,13 +262,7 @@ export const registerSlackUploadFileTool = (server: McpServer) => {
331
262
 
332
263
  if (fileSize > MAX_FILE_SIZE) {
333
264
  const sizeMB = Math.round(fileSize / 1024 / 1024);
334
- return {
335
- content: [{ type: "text", text: `File too large: ${sizeMB} MB. Maximum is 1 GB.` }],
336
- structuredContent: {
337
- success: false,
338
- message: `File too large: ${sizeMB} MB. Maximum is 1 GB.`,
339
- },
340
- };
265
+ return toolErr(`File too large: ${sizeMB} MB. Maximum is 1 GB.`);
341
266
  }
342
267
 
343
268
  fileToUpload = resolvedPath;
@@ -347,21 +272,12 @@ export const registerSlackUploadFileTool = (server: McpServer) => {
347
272
  try {
348
273
  fileToUpload = Buffer.from(content!, "base64");
349
274
  } catch {
350
- return {
351
- content: [{ type: "text", text: "Invalid base64 content." }],
352
- structuredContent: { success: false, message: "Invalid base64 content." },
353
- };
275
+ return toolErr("Invalid base64 content.");
354
276
  }
355
277
 
356
278
  if (fileToUpload.length > MAX_FILE_SIZE) {
357
279
  const sizeMB = Math.round(fileToUpload.length / 1024 / 1024);
358
- return {
359
- content: [{ type: "text", text: `File too large: ${sizeMB} MB. Maximum is 1 GB.` }],
360
- structuredContent: {
361
- success: false,
362
- message: `File too large: ${sizeMB} MB. Maximum is 1 GB.`,
363
- },
364
- };
280
+ return toolErr(`File too large: ${sizeMB} MB. Maximum is 1 GB.`);
365
281
  }
366
282
 
367
283
  actualFilename = filename!;
@@ -377,30 +293,14 @@ export const registerSlackUploadFileTool = (server: McpServer) => {
377
293
  });
378
294
 
379
295
  if (!result.success) {
380
- return {
381
- content: [{ type: "text", text: `Failed to upload file: ${result.error}` }],
382
- structuredContent: {
383
- success: false,
384
- message: `Failed to upload file: ${result.error}`,
385
- },
386
- };
296
+ return toolErr(`Failed to upload file: ${result.error}`);
387
297
  }
388
298
 
389
299
  const successMsg = `File uploaded successfully${result.fileId ? ` (ID: ${result.fileId})` : ""}.`;
390
- return {
391
- content: [{ type: "text", text: successMsg }],
392
- structuredContent: {
393
- success: true,
394
- message: successMsg,
395
- fileId: result.fileId,
396
- },
397
- };
300
+ return toolOk(successMsg, { data: { fileId: result.fileId } });
398
301
  } catch (error) {
399
302
  const errorMsg = error instanceof Error ? error.message : String(error);
400
- return {
401
- content: [{ type: "text", text: `Failed to upload file: ${errorMsg}` }],
402
- structuredContent: { success: false, message: `Failed to upload file: ${errorMsg}` },
403
- };
303
+ return toolErr(`Failed to upload file: ${errorMsg}`);
404
304
  }
405
305
  },
406
306
  );