@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
@@ -8,8 +8,22 @@ import {
8
8
  } from "@/be/swarm-config-guard";
9
9
  import { scheduleIntegrationsReload } from "@/http/core";
10
10
  import { can } from "@/rbac";
11
- import { createToolRegistrar } from "@/tools/utils";
12
- import { SwarmConfigSchema, SwarmConfigScopeSchema } from "@/types";
11
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
12
+ import { SwarmConfigScopeSchema } from "@/types";
13
+
14
+ const configEntryShape = z.looseObject({
15
+ id: z.string().optional(),
16
+ scope: SwarmConfigScopeSchema.optional(),
17
+ scopeId: z.string().nullable().optional(),
18
+ key: z.string().optional(),
19
+ value: z.string().optional(),
20
+ isSecret: z.boolean().optional(),
21
+ envPath: z.string().nullable().optional(),
22
+ description: z.string().nullable().optional(),
23
+ createdAt: z.string().optional(),
24
+ lastUpdatedAt: z.string().optional(),
25
+ encrypted: z.boolean().optional(),
26
+ });
13
27
 
14
28
  export const registerSetConfigTool = (server: McpServer) => {
15
29
  createToolRegistrar(server)(
@@ -47,51 +61,27 @@ export const registerSetConfigTool = (server: McpServer) => {
47
61
  .optional()
48
62
  .describe("Optional human-readable description of this config entry."),
49
63
  }),
50
- outputSchema: z.object({
64
+ outputSchema: swarmToolOutputSchema({
51
65
  yourAgentId: z.string().optional(),
52
- success: z.boolean(),
53
- message: z.string(),
54
- config: SwarmConfigSchema.optional(),
66
+ config: configEntryShape.optional(),
55
67
  }),
56
68
  },
57
69
  async ({ scope, scopeId, key, value, isSecret, envPath, description }, requestInfo) => {
58
70
  if (!requestInfo.agentId) {
59
- return {
60
- content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
61
- structuredContent: {
62
- success: false,
63
- message: 'Agent ID not found. Set the "X-Agent-ID" header.',
64
- },
65
- };
71
+ return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
66
72
  }
67
73
 
68
74
  try {
69
75
  if (scope !== "global" && !scopeId) {
70
- return {
71
- content: [
72
- {
73
- type: "text",
74
- text: `scopeId is required for scope '${scope}'. Provide an agent ID or repo ID.`,
75
- },
76
- ],
77
- structuredContent: {
78
- yourAgentId: requestInfo.agentId,
79
- success: false,
80
- message: `scopeId is required for scope '${scope}'.`,
81
- },
82
- };
76
+ return toolErr(`scopeId is required for scope '${scope}'.`, {
77
+ details: `scopeId is required for scope '${scope}'. Provide an agent ID or repo ID.`,
78
+ data: { yourAgentId: requestInfo.agentId },
79
+ });
83
80
  }
84
81
 
85
82
  if (isReservedConfigKey(key)) {
86
83
  const message = reservedKeyError(key).message;
87
- return {
88
- content: [{ type: "text", text: message }],
89
- structuredContent: {
90
- yourAgentId: requestInfo.agentId,
91
- success: false,
92
- message,
93
- },
94
- };
84
+ return toolErr(message, { data: { yourAgentId: requestInfo.agentId } });
95
85
  }
96
86
 
97
87
  // Every swarm-config write is lead-gated (DES-445 follow-up): previously
@@ -110,27 +100,14 @@ export const registerSetConfigTool = (server: McpServer) => {
110
100
  source: "mcp",
111
101
  });
112
102
  if (!decision.allow) {
113
- const message = "Writing swarm config requires the lead agent.";
114
- return {
115
- content: [{ type: "text", text: message }],
116
- structuredContent: {
117
- yourAgentId: requestInfo.agentId,
118
- success: false,
119
- message,
120
- },
121
- };
103
+ return toolErr("Writing swarm config requires the lead agent.", {
104
+ data: { yourAgentId: requestInfo.agentId },
105
+ });
122
106
  }
123
107
 
124
108
  const validationError = validateConfigValue(key, value);
125
109
  if (validationError) {
126
- return {
127
- content: [{ type: "text", text: validationError }],
128
- structuredContent: {
129
- yourAgentId: requestInfo.agentId,
130
- success: false,
131
- message: validationError,
132
- },
133
- };
110
+ return toolErr(validationError, { data: { yourAgentId: requestInfo.agentId } });
134
111
  }
135
112
 
136
113
  const config = upsertSwarmConfig({
@@ -149,30 +126,15 @@ export const registerSetConfigTool = (server: McpServer) => {
149
126
 
150
127
  const [masked] = maskSecrets([config]);
151
128
 
152
- return {
153
- content: [
154
- {
155
- type: "text",
156
- text: `Config "${key}" set successfully (scope: ${scope}${scopeId ? `, scopeId: ${scopeId}` : ""}).`,
157
- },
158
- ],
159
- structuredContent: {
160
- yourAgentId: requestInfo.agentId,
161
- success: true,
162
- message: `Config "${key}" set successfully.`,
163
- config: masked,
164
- },
165
- };
129
+ return toolOk(`Config "${key}" set successfully.`, {
130
+ details: `Config "${key}" set successfully (scope: ${scope}${scopeId ? `, scopeId: ${scopeId}` : ""}).`,
131
+ data: { yourAgentId: requestInfo.agentId, config: masked },
132
+ });
166
133
  } catch (error) {
167
134
  const message = error instanceof Error ? error.message : "Unknown error";
168
- return {
169
- content: [{ type: "text", text: `Failed to set config: ${message}` }],
170
- structuredContent: {
171
- yourAgentId: requestInfo.agentId,
172
- success: false,
173
- message: `Failed to set config: ${message}`,
174
- },
175
- };
135
+ return toolErr(`Failed to set config: ${message}`, {
136
+ data: { yourAgentId: requestInfo.agentId },
137
+ });
176
138
  }
177
139
  },
178
140
  );
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { COMPOSIO_HTTP_METHODS, composioArgsFromParts, executeComposioRequest } from "@/x/composio";
4
- import { createToolRegistrar } from "./utils";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "./utils";
5
5
 
6
6
  const primitiveQueryValueSchema = z.union([z.string(), z.number(), z.boolean()]);
7
7
 
@@ -46,16 +46,15 @@ export const registerSwarmXTool = (server: McpServer) => {
46
46
  .default(false)
47
47
  .describe("Return raw text instead of JSON-pretty output text."),
48
48
  }),
49
- outputSchema: z.object({
50
- target: z.literal("composio"),
51
- ok: z.boolean(),
52
- status: z.number(),
53
- statusText: z.string(),
54
- method: z.string(),
55
- url: z.string(),
56
- response: z.unknown(),
57
- responseText: z.string(),
58
- message: z.string(),
49
+ outputSchema: swarmToolOutputSchema({
50
+ target: z.literal("composio").optional(),
51
+ ok: z.boolean().optional(),
52
+ status: z.number().optional(),
53
+ statusText: z.string().optional(),
54
+ method: z.string().optional(),
55
+ url: z.string().optional(),
56
+ response: z.unknown().optional(),
57
+ responseText: z.string().optional(),
59
58
  }),
60
59
  },
61
60
  async (input) => {
@@ -75,9 +74,8 @@ export const registerSwarmXTool = (server: McpServer) => {
75
74
  );
76
75
  } catch (err) {
77
76
  const message = err instanceof Error ? err.message : String(err);
78
- return {
79
- content: [{ type: "text", text: `swarm_x composio: ${message}` }],
80
- structuredContent: {
77
+ return toolErr(`swarm_x composio: ${message}`, {
78
+ data: {
81
79
  target: "composio",
82
80
  ok: false,
83
81
  status: 0,
@@ -86,15 +84,14 @@ export const registerSwarmXTool = (server: McpServer) => {
86
84
  url: "",
87
85
  response: null,
88
86
  responseText: "",
89
- message,
90
87
  },
91
- isError: true,
92
- };
88
+ });
93
89
  }
94
90
 
95
- const message =
96
- result.error || result.formattedBody || `HTTP ${result.status} ${result.statusText}`.trim();
97
- const structuredContent = {
91
+ const summary =
92
+ `Composio ${result.method} ${result.url} HTTP ${result.status} ${result.statusText}`.trim();
93
+ const details = result.error || result.formattedBody || undefined;
94
+ const data = {
98
95
  target: "composio" as const,
99
96
  ok: result.ok,
100
97
  status: result.status,
@@ -103,14 +100,11 @@ export const registerSwarmXTool = (server: McpServer) => {
103
100
  url: result.url,
104
101
  response: result.body,
105
102
  responseText: result.text,
106
- message,
107
103
  };
108
104
 
109
- return {
110
- content: [{ type: "text", text: message }],
111
- structuredContent,
112
- isError: !result.ok,
113
- };
105
+ return result.ok
106
+ ? toolOk(summary, { details, data })
107
+ : toolErr(result.error || summary, { details, data });
114
108
  },
115
109
  );
116
110
  };
@@ -1,5 +1,4 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
2
  import * as z from "zod";
4
3
  import { AssetKeyAuthorizationError, authorizeAssetKeyWrite } from "@/be/asset-key-auth";
5
4
  import { resolveTaskAuditUserId } from "@/be/audit-user";
@@ -29,15 +28,21 @@ import {
29
28
  updateTaskClaudeSessionId,
30
29
  } from "@/be/db";
31
30
  import { assertOwnsTask, ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
32
- import { createToolRegistrar } from "@/tools/utils";
33
31
  import {
34
- AgentTaskSchema,
32
+ createToolRegistrar,
33
+ type SwarmToolResult,
34
+ swarmToolOutputSchema,
35
+ toolErr,
36
+ toolOk,
37
+ } from "@/tools/utils";
38
+ import {
35
39
  AssetKeySchema,
36
40
  BudgetRefusalCauseSchema,
37
41
  ModelTierSchema,
38
42
  ReasoningEffortSchema,
39
43
  splitLegacyModelAlias,
40
44
  } from "@/types";
45
+ import { looseAgentTaskOutputSchema } from "./get-task-details";
41
46
 
42
47
  export const TaskActionSchema = z.enum([
43
48
  "create",
@@ -100,11 +105,9 @@ export const taskActionInputSchema = z.object({
100
105
  ),
101
106
  });
102
107
 
103
- export const taskActionOutputSchema = z.object({
108
+ export const taskActionOutputSchema = swarmToolOutputSchema({
104
109
  yourAgentId: z.string().optional(),
105
- success: z.boolean(),
106
- message: z.string(),
107
- task: AgentTaskSchema.optional(),
110
+ task: looseAgentTaskOutputSchema.optional(),
108
111
  // Phase 3: budget-admission refusal fields. Populated only on
109
112
  // `accept` action when the per-agent or global daily budget is blown.
110
113
  refusalCause: BudgetRefusalCauseSchema.optional(),
@@ -133,43 +136,26 @@ type TaskActionResult = {
133
136
  refusalSideEffects?: unknown;
134
137
  };
135
138
 
136
- function agentOnlyActionResult(): CallToolResult {
137
- const message = "This action is only available to worker agents.";
138
- const structuredContent = {
139
- success: false,
140
- message,
141
- };
142
-
143
- return {
144
- isError: true,
145
- content: [
146
- { type: "text", text: message },
147
- { type: "text", text: JSON.stringify(structuredContent) },
148
- ],
149
- structuredContent,
150
- };
139
+ function agentOnlyActionResult(): SwarmToolResult {
140
+ return toolErr("This action is only available to worker agents.");
151
141
  }
152
142
 
153
- function taskActionCallResult(result: TaskActionResult, agentId?: string): CallToolResult {
154
- const { refusalSideEffects: _omit, ...publicResult } = result;
155
- const structuredContent = {
143
+ function taskActionResult(result: TaskActionResult, agentId?: string): SwarmToolResult {
144
+ const { refusalSideEffects: _omit, success, message, ...rest } = result;
145
+ const data = {
156
146
  yourAgentId: agentId,
157
- ...publicResult,
158
- };
159
-
160
- return {
161
- content: [
162
- { type: "text", text: result.message },
163
- { type: "text", text: JSON.stringify(structuredContent) },
164
- ],
165
- structuredContent,
147
+ ...rest,
166
148
  };
149
+ // Most harnesses only show the model the text channel — render the payload
150
+ // (task object, refusalCause, ...) there too, not just in structuredContent.
151
+ const details = Object.keys(rest).length > 0 ? JSON.stringify(rest, null, 2) : undefined;
152
+ return success ? toolOk(message, { data, details }) : toolErr(message, { data, details });
167
153
  }
168
154
 
169
155
  export async function taskActionHandler(
170
156
  ctx: ToolCtx,
171
157
  input: TaskActionArgs,
172
- ): Promise<CallToolResult> {
158
+ ): Promise<SwarmToolResult> {
173
159
  const {
174
160
  action,
175
161
  task,
@@ -191,7 +177,7 @@ export async function taskActionHandler(
191
177
  return agentOnlyActionResult();
192
178
  }
193
179
 
194
- const result = getDb().transaction((): TaskActionResult | CallToolResult => {
180
+ const result = getDb().transaction((): TaskActionResult | SwarmToolResult => {
195
181
  if (!taskId) {
196
182
  return { success: false, message: `Task ID is required for '${action}' action.` };
197
183
  }
@@ -239,17 +225,11 @@ export async function taskActionHandler(
239
225
  };
240
226
  })();
241
227
 
242
- return "content" in result ? result : taskActionCallResult(result);
228
+ return "ok" in result ? result : taskActionResult(result);
243
229
  }
244
230
 
245
231
  if (!ctx.agentId) {
246
- return {
247
- content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
248
- structuredContent: {
249
- success: false,
250
- message: 'Agent ID not found. Set the "X-Agent-ID" header.',
251
- },
252
- };
232
+ return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
253
233
  }
254
234
 
255
235
  const agentId = ctx.agentId;
@@ -266,7 +246,7 @@ export async function taskActionHandler(
266
246
  : error instanceof Error
267
247
  ? error.message
268
248
  : String(error);
269
- return taskActionCallResult({ success: false, message }, agentId);
249
+ return taskActionResult({ success: false, message }, agentId);
270
250
  }
271
251
  }
272
252
 
@@ -597,7 +577,7 @@ export async function taskActionHandler(
597
577
  emitBudgetRefusalSideEffects(sideEffects.context, sideEffects.inserted);
598
578
  }
599
579
 
600
- return taskActionCallResult(result, agentId);
580
+ return taskActionResult(result, agentId);
601
581
  }
602
582
 
603
583
  export const registerTaskActionTool = (server: McpServer) => {
@@ -1,7 +1,6 @@
1
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
1
  import { can, type PermissionVerb } from "@/rbac";
3
2
  import type { AgentTask, User } from "@/types";
4
- import type { RequestInfo } from "./utils";
3
+ import { type RequestInfo, type SwarmToolResult, toolErr } from "./utils";
5
4
 
6
5
  export type ToolCtx =
7
6
  | { kind: "owner"; agentId?: string; sourceTaskId?: string; sessionId?: string }
@@ -29,7 +28,7 @@ export function assertOwnsTask(
29
28
  ctx: ToolCtx,
30
29
  task: AgentTask,
31
30
  verb: PermissionVerb = "task.read.own",
32
- ): CallToolResult | null {
31
+ ): SwarmToolResult | null {
33
32
  // RBAC chokepoint — a future admin/role tier widens visibility here, in this one function.
34
33
  const decision = can({
35
34
  principal:
@@ -46,14 +45,7 @@ export function assertOwnsTask(
46
45
  return null;
47
46
  }
48
47
 
49
- const message = `Forbidden: this task is not yours (task ${task.id}).`;
50
- return {
51
- isError: true,
52
- content: [{ type: "text", text: message }],
53
- structuredContent: {
54
- success: false,
55
- code: "forbidden",
56
- message,
57
- },
58
- };
48
+ return toolErr(`Forbidden: this task is not yours (task ${task.id}).`, {
49
+ data: { code: "forbidden" },
50
+ });
59
51
  }
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { createTrackerSync } from "@/be/db-queries/tracker";
4
- import { createToolRegistrar } from "@/tools/utils";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
5
 
6
6
  export const registerTrackerLinkTaskTool = (server: McpServer) => {
7
7
  createToolRegistrar(server)(
@@ -21,10 +21,8 @@ export const registerTrackerLinkTaskTool = (server: McpServer) => {
21
21
  .describe("Human-readable identifier (e.g. 'ENG-42')"),
22
22
  externalUrl: z.string().optional().describe("URL to the external issue"),
23
23
  }),
24
- outputSchema: z.object({
25
- success: z.boolean(),
26
- message: z.string(),
27
- sync: z.any().optional(),
24
+ outputSchema: swarmToolOutputSchema({
25
+ sync: z.unknown().optional(),
28
26
  }),
29
27
  },
30
28
  async (args, _requestInfo, _meta) => {
@@ -39,25 +37,13 @@ export const registerTrackerLinkTaskTool = (server: McpServer) => {
39
37
  syncDirection: "bidirectional",
40
38
  });
41
39
 
42
- return {
43
- content: [
44
- {
45
- type: "text",
46
- text: `Linked task ${args.swarmTaskId} to ${args.provider} issue ${args.externalIdentifier ?? args.externalId}`,
47
- },
48
- ],
49
- structuredContent: {
50
- success: true,
51
- message: `Linked task ${args.swarmTaskId} to ${args.provider} issue ${args.externalIdentifier ?? args.externalId}.`,
52
- sync,
53
- },
54
- };
40
+ return toolOk(
41
+ `Linked task ${args.swarmTaskId} to ${args.provider} issue ${args.externalIdentifier ?? args.externalId}.`,
42
+ { data: { sync } },
43
+ );
55
44
  } catch (error) {
56
45
  const message = error instanceof Error ? error.message : "Unknown error";
57
- return {
58
- content: [{ type: "text", text: `Failed to link task: ${message}` }],
59
- structuredContent: { success: false, message: `Failed: ${message}` },
60
- };
46
+ return toolErr(`Failed to link task: ${message}`);
61
47
  }
62
48
  },
63
49
  );
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { createTrackerAgentMapping } from "@/be/db-queries/tracker";
4
- import { createToolRegistrar } from "@/tools/utils";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
5
 
6
6
  export const registerTrackerMapAgentTool = (server: McpServer) => {
7
7
  createToolRegistrar(server)(
@@ -17,10 +17,8 @@ export const registerTrackerMapAgentTool = (server: McpServer) => {
17
17
  externalUserId: z.string().describe("The external user ID in the tracker"),
18
18
  agentName: z.string().describe("Display name for the agent mapping"),
19
19
  }),
20
- outputSchema: z.object({
21
- success: z.boolean(),
22
- message: z.string(),
23
- mapping: z.any().optional(),
20
+ outputSchema: swarmToolOutputSchema({
21
+ mapping: z.unknown().optional(),
24
22
  }),
25
23
  },
26
24
  async (args, _requestInfo, _meta) => {
@@ -32,25 +30,13 @@ export const registerTrackerMapAgentTool = (server: McpServer) => {
32
30
  agentName: args.agentName,
33
31
  });
34
32
 
35
- return {
36
- content: [
37
- {
38
- type: "text",
39
- text: `Mapped agent ${args.agentName} to ${args.provider} user ${args.externalUserId}`,
40
- },
41
- ],
42
- structuredContent: {
43
- success: true,
44
- message: `Mapped agent ${args.agentName} to ${args.provider} user ${args.externalUserId}.`,
45
- mapping,
46
- },
47
- };
33
+ return toolOk(
34
+ `Mapped agent ${args.agentName} to ${args.provider} user ${args.externalUserId}.`,
35
+ { data: { mapping } },
36
+ );
48
37
  } catch (error) {
49
38
  const message = error instanceof Error ? error.message : "Unknown error";
50
- return {
51
- content: [{ type: "text", text: `Failed to map agent: ${message}` }],
52
- structuredContent: { success: false, message: `Failed: ${message}` },
53
- };
39
+ return toolErr(`Failed to map agent: ${message}`);
54
40
  }
55
41
  },
56
42
  );
@@ -2,7 +2,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { getOAuthApp, getOAuthTokens } from "@/be/db-queries/oauth";
4
4
  import { ensureToken } from "@/oauth/ensure-token";
5
- import { createToolRegistrar } from "@/tools/utils";
5
+ import { createToolRegistrar, swarmToolOutputSchema, toolOk } from "@/tools/utils";
6
6
 
7
7
  export const registerTrackerStatusTool = (server: McpServer) => {
8
8
  createToolRegistrar(server)(
@@ -13,17 +13,18 @@ export const registerTrackerStatusTool = (server: McpServer) => {
13
13
  "Show all connected trackers and their OAuth status (token expiry, workspace info). Proactively refreshes near-expiry tokens before reporting, so the returned `tokenExpiresAt` reflects the row that subsequent API calls (and direct DB reads) will see.",
14
14
  annotations: { readOnlyHint: true },
15
15
 
16
- outputSchema: z.object({
17
- success: z.boolean(),
18
- trackers: z.array(
19
- z.object({
20
- provider: z.string(),
21
- connected: z.boolean(),
22
- tokenExpiresAt: z.string().nullable(),
23
- scopes: z.string().nullable(),
24
- redirectUri: z.string().nullable(),
25
- }),
26
- ),
16
+ outputSchema: swarmToolOutputSchema({
17
+ trackers: z
18
+ .array(
19
+ z.looseObject({
20
+ provider: z.string().optional(),
21
+ connected: z.boolean().optional(),
22
+ tokenExpiresAt: z.string().nullable().optional(),
23
+ scopes: z.string().nullable().optional(),
24
+ redirectUri: z.string().nullable().optional(),
25
+ }),
26
+ )
27
+ .optional(),
27
28
  }),
28
29
  },
29
30
  async (_requestInfo, _meta) => {
@@ -50,13 +51,20 @@ export const registerTrackerStatusTool = (server: McpServer) => {
50
51
  .map((t) => `${t.provider}: ${t.connected ? "connected" : "not connected"}`)
51
52
  .join(", ");
52
53
 
53
- return {
54
- content: [{ type: "text", text: `Tracker status: ${summary}` }],
55
- structuredContent: {
56
- success: true,
57
- trackers,
58
- },
59
- };
54
+ const details = trackers
55
+ .map((t) => {
56
+ const bits = [
57
+ `provider: ${t.provider}`,
58
+ `connected: ${t.connected}`,
59
+ `tokenExpiresAt: ${t.tokenExpiresAt ?? "n/a"}`,
60
+ `scopes: ${t.scopes ?? "n/a"}`,
61
+ `redirectUri: ${t.redirectUri ?? "n/a"}`,
62
+ ];
63
+ return `- ${bits.join(", ")}`;
64
+ })
65
+ .join("\n");
66
+
67
+ return toolOk(`Tracker status: ${summary}`, { details, data: { trackers } });
60
68
  },
61
69
  );
62
70
  };
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { getAllTrackerSyncs } from "@/be/db-queries/tracker";
4
- import { createToolRegistrar } from "@/tools/utils";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolOk } from "@/tools/utils";
5
5
 
6
6
  export const registerTrackerSyncStatusTool = (server: McpServer) => {
7
7
  createToolRegistrar(server)(
@@ -15,28 +15,18 @@ export const registerTrackerSyncStatusTool = (server: McpServer) => {
15
15
  provider: z.string().optional().describe("Filter by provider (e.g. 'linear', 'jira')"),
16
16
  entityType: z.enum(["task"]).optional().describe("Filter by entity type"),
17
17
  }),
18
- outputSchema: z.object({
19
- success: z.boolean(),
20
- count: z.number(),
21
- syncs: z.array(z.any()),
18
+ outputSchema: swarmToolOutputSchema({
19
+ count: z.number().optional(),
20
+ syncs: z.array(z.unknown()).optional(),
22
21
  }),
23
22
  },
24
23
  async (args, _requestInfo, _meta) => {
25
24
  const syncs = getAllTrackerSyncs(args.provider, args.entityType);
26
25
 
27
- return {
28
- content: [
29
- {
30
- type: "text",
31
- text: `Found ${syncs.length} tracker sync mapping(s)${args.provider ? ` for ${args.provider}` : ""}${args.entityType ? ` (${args.entityType})` : ""}.`,
32
- },
33
- ],
34
- structuredContent: {
35
- success: true,
36
- count: syncs.length,
37
- syncs,
38
- },
39
- };
26
+ return toolOk(
27
+ `Found ${syncs.length} tracker sync mapping(s)${args.provider ? ` for ${args.provider}` : ""}${args.entityType ? ` (${args.entityType})` : ""}.`,
28
+ { data: { count: syncs.length, syncs } },
29
+ );
40
30
  },
41
31
  );
42
32
  };