@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
@@ -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";
@@ -18,16 +17,22 @@ import { repointTrackerSyncBySwarmId } from "@/be/db-queries/tracker";
18
17
  import { checkSlackRoutingCoherence } from "@/tasks/slack-routing";
19
18
  import { findDuplicateTask } from "@/tools/task-dedup";
20
19
  import { ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
21
- import { createToolRegistrar } from "@/tools/utils";
20
+ import {
21
+ createToolRegistrar,
22
+ type SwarmToolResult,
23
+ swarmToolOutputSchema,
24
+ toolErr,
25
+ toolOk,
26
+ } from "@/tools/utils";
22
27
  import {
23
28
  type AgentTask,
24
- AgentTaskSchema,
25
29
  AssetKeySchema,
26
30
  FollowUpConfigSchema,
27
31
  ModelTierSchema,
28
32
  ReasoningEffortSchema,
29
33
  splitLegacyModelAlias,
30
34
  } from "@/types";
35
+ import { looseAgentTaskOutputSchema } from "./get-task-details";
31
36
 
32
37
  export const sendTaskInputSchema = z
33
38
  .object({
@@ -142,11 +147,9 @@ export const sendTaskInputSchema = z
142
147
  }
143
148
  });
144
149
 
145
- export const sendTaskOutputSchema = z.object({
150
+ export const sendTaskOutputSchema = swarmToolOutputSchema({
146
151
  yourAgentId: z.string().optional(),
147
- success: z.boolean(),
148
- message: z.string(),
149
- task: AgentTaskSchema.optional(),
152
+ task: looseAgentTaskOutputSchema.optional(),
150
153
  });
151
154
 
152
155
  type SendTaskArgs = z.infer<typeof sendTaskInputSchema>;
@@ -211,21 +214,11 @@ export async function sendTaskHandler(
211
214
  requestedByUserId: inputRequestedByUserId,
212
215
  followUpConfig,
213
216
  }: SendTaskArgs,
214
- ): Promise<CallToolResult> {
217
+ ): Promise<SwarmToolResult> {
215
218
  if (ctx.kind === "owner" && !ctx.agentId) {
216
- return {
217
- content: [
218
- {
219
- type: "text",
220
- text: 'Agent ID not found. The MCP client should define the "X-Agent-ID" header.',
221
- },
222
- ],
223
- structuredContent: {
224
- yourAgentId: ctx.agentId,
225
- success: false,
226
- message: 'Agent ID not found. The MCP client should define the "X-Agent-ID" header.',
227
- },
228
- };
219
+ return toolErr('Agent ID not found. The MCP client should define the "X-Agent-ID" header.', {
220
+ data: { yourAgentId: ctx.agentId },
221
+ });
229
222
  }
230
223
 
231
224
  const creatorAgentId = ctx.kind === "owner" ? ctx.agentId : undefined;
@@ -237,19 +230,9 @@ export async function sendTaskHandler(
237
230
  : (inputRequestedByUserId ?? callerTask?.requestedByUserId ?? undefined);
238
231
 
239
232
  if (ctx.kind === "owner" && agentId === ctx.agentId) {
240
- return {
241
- content: [
242
- {
243
- type: "text",
244
- text: "Cannot send a task to yourself, are you drunk?",
245
- },
246
- ],
247
- structuredContent: {
248
- yourAgentId: ctx.agentId,
249
- success: false,
250
- message: "Cannot send a task to yourself, are you drunk?",
251
- },
252
- };
233
+ return toolErr("Cannot send a task to yourself, are you drunk?", {
234
+ data: { yourAgentId: ctx.agentId },
235
+ });
253
236
  }
254
237
 
255
238
  const effectiveVcsRepo = vcsRepo;
@@ -280,14 +263,7 @@ export async function sendTaskHandler(
280
263
  routingCheck.verdict === "partial-unit"
281
264
  ? `Slack routing rejected: ${routingCheck.detail}`
282
265
  : `Slack routing mismatch: you passed ${routingCheck.field}="${routingCheck.got}" but the ${routingCheck.expectedSource} task says "${routingCheck.expected}". Omit the three Slack fields to inherit them from the parent as a unit (preferred), or pass overrideSlackContext: true if the cross-channel routing is deliberate.`;
283
- return {
284
- content: [{ type: "text", text: msg }],
285
- structuredContent: {
286
- yourAgentId: creatorAgentId,
287
- success: false,
288
- message: msg,
289
- },
290
- };
266
+ return toolErr(msg, { data: { yourAgentId: creatorAgentId } });
291
267
  }
292
268
  } else if (slackChannelId || slackThreadTs) {
293
269
  console.log(
@@ -308,11 +284,7 @@ export async function sendTaskHandler(
308
284
  : error instanceof Error
309
285
  ? error.message
310
286
  : String(error);
311
- return {
312
- isError: true,
313
- content: [{ type: "text", text: message }],
314
- structuredContent: { yourAgentId: creatorAgentId, success: false, message },
315
- };
287
+ return toolErr(message, { data: { yourAgentId: creatorAgentId } });
316
288
  }
317
289
 
318
290
  // Auto-route to parent's worker if parentTaskId is set and no explicit agentId
@@ -327,15 +299,9 @@ export async function sendTaskHandler(
327
299
  if (existingTrackerWork) {
328
300
  const msg = `Skipped: Linear tracker contextKey ${effectiveParentTask?.contextKey} already has ${existingTrackerWork.reason === "active_task" ? "active task" : "linked open PR"} ${existingTrackerWork.task.id.slice(0, 8)}.`;
329
301
  console.log(`[send-task] ${msg}`);
330
- return {
331
- content: [{ type: "text", text: msg }],
332
- structuredContent: {
333
- yourAgentId: creatorAgentId,
334
- success: true,
335
- message: msg,
336
- task: existingTrackerWork.task,
337
- },
338
- };
302
+ return toolOk(msg, {
303
+ data: { yourAgentId: creatorAgentId, task: existingTrackerWork.task },
304
+ });
339
305
  }
340
306
 
341
307
  // Dedup guard: check for similar recent tasks
@@ -347,14 +313,7 @@ export async function sendTaskHandler(
347
313
  });
348
314
  if (duplicate) {
349
315
  const msg = `Duplicate task detected (matches task ${duplicate.task.id.slice(0, 8)}, ${duplicate.reason}). Skipping. Use allowDuplicate: true to override.`;
350
- return {
351
- content: [{ type: "text", text: msg }],
352
- structuredContent: {
353
- yourAgentId: creatorAgentId,
354
- success: false,
355
- message: msg,
356
- },
357
- };
316
+ return toolErr(msg, { data: { yourAgentId: creatorAgentId } });
358
317
  }
359
318
  }
360
319
 
@@ -397,14 +356,7 @@ export async function sendTaskHandler(
397
356
  new Date(recentCompleted.lastUpdatedAt).getTime();
398
357
  if (!cancelledMoreRecent) {
399
358
  const msg = `Blocked: re-delegation from follow-up task in a thread that already has completed work (task ${recentCompleted.id.slice(0, 8)}). The original request was already handled.`;
400
- return {
401
- content: [{ type: "text", text: msg }],
402
- structuredContent: {
403
- yourAgentId: creatorAgentId,
404
- success: false,
405
- message: msg,
406
- },
407
- };
359
+ return toolErr(msg, { data: { yourAgentId: creatorAgentId } });
408
360
  }
409
361
  // else: fall through — the cancellation is more recent than the
410
362
  // completion, so re-delegation is legitimate.
@@ -557,18 +509,17 @@ export async function sendTaskHandler(
557
509
  });
558
510
 
559
511
  const result = txn();
560
- const structuredContent = {
512
+ const data = {
561
513
  yourAgentId: creatorAgentId,
562
- ...result,
514
+ task: result.task,
563
515
  };
564
516
 
565
- return {
566
- content: [
567
- { type: "text", text: result.message },
568
- { type: "text", text: JSON.stringify(result) },
569
- ],
570
- structuredContent,
571
- };
517
+ // Text channel must carry the created task too — most harnesses never show
518
+ // the model structuredContent.
519
+ const details = result.task ? JSON.stringify(result.task, null, 2) : undefined;
520
+ return result.success
521
+ ? toolOk(result.message, { data, details })
522
+ : toolErr(result.message, { data, details });
572
523
  }
573
524
 
574
525
  export const registerSendTaskTool = (server: McpServer) => {
@@ -3,7 +3,7 @@ import * as z from "zod";
3
3
  import { createSkill, getAgentById, installSkill } from "@/be/db";
4
4
  import { parseSkillContent } from "@/be/skill-parser";
5
5
  import { can } from "@/rbac";
6
- import { createToolRegistrar } from "@/tools/utils";
6
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
7
7
 
8
8
  export const registerSkillCreateTool = (server: McpServer) => {
9
9
  createToolRegistrar(server)(
@@ -24,19 +24,14 @@ export const registerSkillCreateTool = (server: McpServer) => {
24
24
  .optional()
25
25
  .describe("Scope: agent (personal) or swarm (shared). Default: agent"),
26
26
  }),
27
- outputSchema: z.object({
27
+ outputSchema: swarmToolOutputSchema({
28
28
  yourAgentId: z.string().optional(),
29
- success: z.boolean(),
30
- message: z.string(),
31
- skill: z.any().optional(),
29
+ skill: z.looseObject({}).optional(),
32
30
  }),
33
31
  },
34
32
  async (args, requestInfo, _meta) => {
35
33
  if (!requestInfo.agentId) {
36
- return {
37
- content: [{ type: "text", text: "Agent ID not found." }],
38
- structuredContent: { success: false, message: "Agent ID not found." },
39
- };
34
+ return toolErr("Agent ID not found.");
40
35
  }
41
36
 
42
37
  try {
@@ -56,19 +51,10 @@ export const registerSkillCreateTool = (server: McpServer) => {
56
51
  source: "mcp",
57
52
  });
58
53
  if (!decision.allow) {
59
- return {
60
- content: [
61
- {
62
- type: "text",
63
- text: 'Only lead agents can create swarm-scope skills directly. Use "skill-publish" to request approval.',
64
- },
65
- ],
66
- structuredContent: {
67
- yourAgentId: requestInfo.agentId,
68
- success: false,
69
- message: "Only lead agents can create swarm-scope skills directly.",
70
- },
71
- };
54
+ return toolErr("Only lead agents can create swarm-scope skills directly.", {
55
+ details: 'Use "skill-publish" to request approval.',
56
+ data: { yourAgentId: requestInfo.agentId },
57
+ });
72
58
  }
73
59
  }
74
60
 
@@ -91,25 +77,14 @@ export const registerSkillCreateTool = (server: McpServer) => {
91
77
  // Auto-install for the creating agent
92
78
  installSkill(requestInfo.agentId, skill.id);
93
79
 
94
- return {
95
- content: [{ type: "text", text: `Created skill "${skill.name}" (${skill.id})` }],
96
- structuredContent: {
97
- yourAgentId: requestInfo.agentId,
98
- success: true,
99
- message: `Created and installed skill "${skill.name}".`,
100
- skill,
101
- },
102
- };
80
+ return toolOk(`Created and installed skill "${skill.name}".`, {
81
+ data: { yourAgentId: requestInfo.agentId, skill },
82
+ });
103
83
  } catch (error) {
104
84
  const message = error instanceof Error ? error.message : "Unknown error";
105
- return {
106
- content: [{ type: "text", text: `Failed to create skill: ${message}` }],
107
- structuredContent: {
108
- yourAgentId: requestInfo.agentId,
109
- success: false,
110
- message: `Failed: ${message}`,
111
- },
112
- };
85
+ return toolErr(`Failed to create skill: ${message}`, {
86
+ data: { yourAgentId: requestInfo.agentId },
87
+ });
113
88
  }
114
89
  },
115
90
  );
@@ -2,7 +2,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { deleteSkill, getAgentById, getSkillById } from "@/be/db";
4
4
  import { can } from "@/rbac";
5
- import { createToolRegistrar } from "@/tools/utils";
5
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
6
6
 
7
7
  const SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE =
8
8
  "This skill is system-managed and cannot be edited from the UI; it is re-seeded on each start. Fork it under a new name to customize.";
@@ -17,30 +17,18 @@ export const registerSkillDeleteTool = (server: McpServer) => {
17
17
  inputSchema: z.object({
18
18
  skillId: z.string().describe("ID of the skill to delete"),
19
19
  }),
20
- outputSchema: z.object({
20
+ outputSchema: swarmToolOutputSchema({
21
21
  yourAgentId: z.string().optional(),
22
- success: z.boolean(),
23
- message: z.string(),
24
22
  }),
25
23
  },
26
24
  async (args, requestInfo, _meta) => {
27
25
  if (!requestInfo.agentId) {
28
- return {
29
- content: [{ type: "text", text: "Agent ID not found." }],
30
- structuredContent: { success: false, message: "Agent ID not found." },
31
- };
26
+ return toolErr("Agent ID not found.");
32
27
  }
33
28
 
34
29
  const existing = getSkillById(args.skillId);
35
30
  if (!existing) {
36
- return {
37
- content: [{ type: "text", text: "Skill not found." }],
38
- structuredContent: {
39
- yourAgentId: requestInfo.agentId,
40
- success: false,
41
- message: "Skill not found.",
42
- },
43
- };
31
+ return toolErr("Skill not found.", { data: { yourAgentId: requestInfo.agentId } });
44
32
  }
45
33
 
46
34
  const agent = getAgentById(requestInfo.agentId);
@@ -55,38 +43,22 @@ export const registerSkillDeleteTool = (server: McpServer) => {
55
43
  source: "mcp",
56
44
  });
57
45
  if (!decision.allow) {
58
- return {
59
- content: [{ type: "text", text: "Only the owning agent or lead can delete this skill." }],
60
- structuredContent: {
61
- yourAgentId: requestInfo.agentId,
62
- success: false,
63
- message: "Permission denied.",
64
- },
65
- };
46
+ return toolErr("Only the owning agent or lead can delete this skill.", {
47
+ data: { yourAgentId: requestInfo.agentId },
48
+ });
66
49
  }
67
50
 
68
51
  if (existing.systemDefault) {
69
- return {
70
- content: [{ type: "text", text: SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE }],
71
- structuredContent: {
72
- yourAgentId: requestInfo.agentId,
73
- success: false,
74
- message: SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE,
75
- },
76
- };
52
+ return toolErr(SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE, {
53
+ data: { yourAgentId: requestInfo.agentId },
54
+ });
77
55
  }
78
56
 
79
57
  const deleted = deleteSkill(args.skillId);
80
- return {
81
- content: [
82
- { type: "text", text: deleted ? `Deleted skill "${existing.name}".` : "Delete failed." },
83
- ],
84
- structuredContent: {
85
- yourAgentId: requestInfo.agentId,
86
- success: deleted,
87
- message: deleted ? `Deleted skill "${existing.name}".` : "Delete failed.",
88
- },
89
- };
58
+ const message = deleted ? `Deleted skill "${existing.name}".` : "Delete failed.";
59
+ return deleted
60
+ ? toolOk(message, { data: { yourAgentId: requestInfo.agentId } })
61
+ : toolErr(message, { data: { yourAgentId: requestInfo.agentId } });
90
62
  },
91
63
  );
92
64
  };
@@ -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 { getSkillById, getSkillFile } from "@/be/db";
4
- import { createToolRegistrar } from "@/tools/utils";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
5
 
6
6
  export const registerSkillGetFileTool = (server: McpServer) => {
7
7
  createToolRegistrar(server)(
@@ -15,24 +15,15 @@ export const registerSkillGetFileTool = (server: McpServer) => {
15
15
  skillId: z.string().describe("Skill ID"),
16
16
  path: z.string().describe("Relative path, e.g. references/animations.md"),
17
17
  }),
18
- outputSchema: z.object({
18
+ outputSchema: swarmToolOutputSchema({
19
19
  yourAgentId: z.string().optional(),
20
- success: z.boolean(),
21
- message: z.string(),
22
- file: z.any().optional(),
20
+ file: z.looseObject({}).optional(),
23
21
  }),
24
22
  },
25
23
  async (args, requestInfo, _meta) => {
26
24
  const skill = getSkillById(args.skillId);
27
25
  if (!skill) {
28
- return {
29
- content: [{ type: "text", text: "Skill not found." }],
30
- structuredContent: {
31
- yourAgentId: requestInfo.agentId,
32
- success: false,
33
- message: "Skill not found.",
34
- },
35
- };
26
+ return toolErr("Skill not found.", { data: { yourAgentId: requestInfo.agentId } });
36
27
  }
37
28
 
38
29
  let file = null;
@@ -40,41 +31,17 @@ export const registerSkillGetFileTool = (server: McpServer) => {
40
31
  file = getSkillFile(args.skillId, args.path);
41
32
  } catch (err) {
42
33
  const message = err instanceof Error ? err.message : "Invalid file path.";
43
- return {
44
- content: [{ type: "text", text: message }],
45
- structuredContent: {
46
- yourAgentId: requestInfo.agentId,
47
- success: false,
48
- message,
49
- },
50
- };
34
+ return toolErr(message, { data: { yourAgentId: requestInfo.agentId } });
51
35
  }
52
36
 
53
37
  if (!file) {
54
- return {
55
- content: [{ type: "text", text: "Skill file not found." }],
56
- structuredContent: {
57
- yourAgentId: requestInfo.agentId,
58
- success: false,
59
- message: "Skill file not found.",
60
- },
61
- };
38
+ return toolErr("Skill file not found.", { data: { yourAgentId: requestInfo.agentId } });
62
39
  }
63
40
 
64
- return {
65
- content: [
66
- {
67
- type: "text",
68
- text: `Skill file "${skill.name}/${file.path}" (${file.mimeType}):\n\n${file.content}`,
69
- },
70
- ],
71
- structuredContent: {
72
- yourAgentId: requestInfo.agentId,
73
- success: true,
74
- message: `Found skill file "${file.path}".`,
75
- file,
76
- },
77
- };
41
+ return toolOk(`Found skill file "${file.path}".`, {
42
+ details: `Skill file "${skill.name}/${file.path}" (${file.mimeType}):\n\n${file.content}`,
43
+ data: { yourAgentId: requestInfo.agentId, file },
44
+ });
78
45
  },
79
46
  );
80
47
  };
@@ -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 { getSkillById, getSkillByName } from "@/be/db";
4
- import { createToolRegistrar } from "@/tools/utils";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
5
 
6
6
  export const registerSkillGetTool = (server: McpServer) => {
7
7
  createToolRegistrar(server)(
@@ -15,23 +15,14 @@ export const registerSkillGetTool = (server: McpServer) => {
15
15
  skillId: z.string().optional().describe("Skill ID"),
16
16
  name: z.string().optional().describe("Skill name (resolved with precedence)"),
17
17
  }),
18
- outputSchema: z.object({
18
+ outputSchema: swarmToolOutputSchema({
19
19
  yourAgentId: z.string().optional(),
20
- success: z.boolean(),
21
- message: z.string(),
22
- skill: z.any().optional(),
20
+ skill: z.looseObject({}).optional(),
23
21
  }),
24
22
  },
25
23
  async (args, requestInfo, _meta) => {
26
24
  if (!args.skillId && !args.name) {
27
- return {
28
- content: [{ type: "text", text: "Provide skillId or name." }],
29
- structuredContent: {
30
- yourAgentId: requestInfo.agentId,
31
- success: false,
32
- message: "Provide skillId or name.",
33
- },
34
- };
25
+ return toolErr("Provide skillId or name.", { data: { yourAgentId: requestInfo.agentId } });
35
26
  }
36
27
 
37
28
  let skill = null;
@@ -49,27 +40,13 @@ export const registerSkillGetTool = (server: McpServer) => {
49
40
  }
50
41
 
51
42
  if (!skill) {
52
- return {
53
- content: [{ type: "text", text: "Skill not found." }],
54
- structuredContent: {
55
- yourAgentId: requestInfo.agentId,
56
- success: false,
57
- message: "Skill not found.",
58
- },
59
- };
43
+ return toolErr("Skill not found.", { data: { yourAgentId: requestInfo.agentId } });
60
44
  }
61
45
 
62
- return {
63
- content: [
64
- { type: "text", text: `Skill "${skill.name}" (${skill.id}):\n\n${skill.content}` },
65
- ],
66
- structuredContent: {
67
- yourAgentId: requestInfo.agentId,
68
- success: true,
69
- message: `Found skill "${skill.name}".`,
70
- skill,
71
- },
72
- };
46
+ return toolOk(`Found skill "${skill.name}".`, {
47
+ details: `Skill "${skill.name}" (${skill.id}):\n\n${skill.content}`,
48
+ data: { yourAgentId: requestInfo.agentId, skill },
49
+ });
73
50
  },
74
51
  );
75
52
  };
@@ -3,7 +3,7 @@ import * as z from "zod";
3
3
  import { createSkill, getAgentById } from "@/be/db";
4
4
  import { parseSkillContent } from "@/be/skill-parser";
5
5
  import { can } from "@/rbac";
6
- import { createToolRegistrar } from "@/tools/utils";
6
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
7
7
 
8
8
  export const registerSkillInstallRemoteTool = (server: McpServer) => {
9
9
  createToolRegistrar(server)(
@@ -27,19 +27,14 @@ export const registerSkillInstallRemoteTool = (server: McpServer) => {
27
27
  .optional()
28
28
  .describe("If true, registers for npx install (metadata only)"),
29
29
  }),
30
- outputSchema: z.object({
30
+ outputSchema: swarmToolOutputSchema({
31
31
  yourAgentId: z.string().optional(),
32
- success: z.boolean(),
33
- message: z.string(),
34
- skill: z.any().optional(),
32
+ skill: z.looseObject({}).optional(),
35
33
  }),
36
34
  },
37
35
  async (args, requestInfo, _meta) => {
38
36
  if (!requestInfo.agentId) {
39
- return {
40
- content: [{ type: "text", text: "Agent ID not found." }],
41
- structuredContent: { success: false, message: "Agent ID not found." },
42
- };
37
+ return toolErr("Agent ID not found.");
43
38
  }
44
39
 
45
40
  // Only leads can install global/swarm remote skills
@@ -55,14 +50,9 @@ export const registerSkillInstallRemoteTool = (server: McpServer) => {
55
50
  source: "mcp",
56
51
  });
57
52
  if (!decision.allow) {
58
- return {
59
- content: [{ type: "text", text: "Only lead agents can install remote skills." }],
60
- structuredContent: {
61
- yourAgentId: requestInfo.agentId,
62
- success: false,
63
- message: "Only lead agents can install remote skills.",
64
- },
65
- };
53
+ return toolErr("Only lead agents can install remote skills.", {
54
+ data: { yourAgentId: requestInfo.agentId },
55
+ });
66
56
  }
67
57
 
68
58
  try {
@@ -77,19 +67,10 @@ export const registerSkillInstallRemoteTool = (server: McpServer) => {
77
67
  // Fetch SKILL.md content
78
68
  const response = await fetch(rawUrl);
79
69
  if (!response.ok) {
80
- return {
81
- content: [
82
- {
83
- type: "text",
84
- text: `Failed to fetch SKILL.md from ${rawUrl}: ${response.status}`,
85
- },
86
- ],
87
- structuredContent: {
88
- yourAgentId: requestInfo.agentId,
89
- success: false,
90
- message: `Failed to fetch: HTTP ${response.status}`,
91
- },
92
- };
70
+ return toolErr(`Failed to fetch: HTTP ${response.status}`, {
71
+ details: `Failed to fetch SKILL.md from ${rawUrl}: ${response.status}`,
72
+ data: { yourAgentId: requestInfo.agentId },
73
+ });
93
74
  }
94
75
  content = await response.text();
95
76
  sourceHash = new Bun.CryptoHasher("sha256").update(content).digest("hex");
@@ -133,30 +114,13 @@ export const registerSkillInstallRemoteTool = (server: McpServer) => {
133
114
  userInvocable: parsedMeta.userInvocable,
134
115
  });
135
116
 
136
- return {
137
- content: [
138
- {
139
- type: "text",
140
- text: `Installed remote skill "${skill.name}" from ${args.sourceRepo}`,
141
- },
142
- ],
143
- structuredContent: {
144
- yourAgentId: requestInfo.agentId,
145
- success: true,
146
- message: `Installed remote skill "${skill.name}".`,
147
- skill,
148
- },
149
- };
117
+ return toolOk(`Installed remote skill "${skill.name}".`, {
118
+ details: `Installed remote skill "${skill.name}" from ${args.sourceRepo}`,
119
+ data: { yourAgentId: requestInfo.agentId, skill },
120
+ });
150
121
  } catch (error) {
151
122
  const message = error instanceof Error ? error.message : "Unknown error";
152
- return {
153
- content: [{ type: "text", text: `Failed: ${message}` }],
154
- structuredContent: {
155
- yourAgentId: requestInfo.agentId,
156
- success: false,
157
- message: `Failed: ${message}`,
158
- },
159
- };
123
+ return toolErr(`Failed: ${message}`, { data: { yourAgentId: requestInfo.agentId } });
160
124
  }
161
125
  },
162
126
  );