@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,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { deleteTrackerSync } 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 registerTrackerUnlinkTool = (server: McpServer) => {
7
7
  createToolRegistrar(server)(
@@ -14,27 +14,15 @@ export const registerTrackerUnlinkTool = (server: McpServer) => {
14
14
  inputSchema: z.object({
15
15
  syncId: z.string().describe("The tracker sync mapping ID to remove"),
16
16
  }),
17
- outputSchema: z.object({
18
- success: z.boolean(),
19
- message: z.string(),
20
- }),
17
+ outputSchema: swarmToolOutputSchema(),
21
18
  },
22
19
  async (args, _requestInfo, _meta) => {
23
20
  try {
24
21
  deleteTrackerSync(args.syncId);
25
- return {
26
- content: [{ type: "text", text: `Removed tracker sync mapping ${args.syncId}` }],
27
- structuredContent: {
28
- success: true,
29
- message: `Removed tracker sync mapping ${args.syncId}.`,
30
- },
31
- };
22
+ return toolOk(`Removed tracker sync mapping ${args.syncId}.`);
32
23
  } catch (error) {
33
24
  const message = error instanceof Error ? error.message : "Unknown error";
34
- return {
35
- content: [{ type: "text", text: `Failed to unlink: ${message}` }],
36
- structuredContent: { success: false, message: `Failed: ${message}` },
37
- };
25
+ return toolErr(`Failed to unlink: ${message}`);
38
26
  }
39
27
  },
40
28
  );
@@ -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 { deleteService, getServiceByAgentAndName, getServiceById } from "@/be/db";
4
- import { createToolRegistrar } from "@/tools/utils";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
5
 
6
6
  export const registerUnregisterServiceTool = (server: McpServer) => {
7
7
  createToolRegistrar(server)(
@@ -19,32 +19,19 @@ export const registerUnregisterServiceTool = (server: McpServer) => {
19
19
  .optional()
20
20
  .describe("Service name to unregister (alternative to serviceId)."),
21
21
  }),
22
- outputSchema: z.object({
22
+ outputSchema: swarmToolOutputSchema({
23
23
  yourAgentId: z.string().optional(),
24
- success: z.boolean(),
25
- message: z.string(),
26
24
  }),
27
25
  },
28
26
  async ({ serviceId, name }, requestInfo, _meta) => {
29
27
  if (!requestInfo.agentId) {
30
- return {
31
- content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
32
- structuredContent: {
33
- success: false,
34
- message: 'Agent ID not found. Set the "X-Agent-ID" header.',
35
- },
36
- };
28
+ return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
37
29
  }
38
30
 
39
31
  if (!serviceId && !name) {
40
- return {
41
- content: [{ type: "text", text: "Either serviceId or name is required." }],
42
- structuredContent: {
43
- yourAgentId: requestInfo.agentId,
44
- success: false,
45
- message: "Either serviceId or name is required.",
46
- },
47
- };
32
+ return toolErr("Either serviceId or name is required.", {
33
+ data: { yourAgentId: requestInfo.agentId },
34
+ });
48
35
  }
49
36
 
50
37
  try {
@@ -55,58 +42,31 @@ export const registerUnregisterServiceTool = (server: McpServer) => {
55
42
  }
56
43
 
57
44
  if (!service) {
58
- return {
59
- content: [{ type: "text", text: "Service not found." }],
60
- structuredContent: {
61
- yourAgentId: requestInfo.agentId,
62
- success: false,
63
- message: "Service not found.",
64
- },
65
- };
45
+ return toolErr("Service not found.", { data: { yourAgentId: requestInfo.agentId } });
66
46
  }
67
47
 
68
48
  // Check ownership
69
49
  if (service.agentId !== requestInfo.agentId) {
70
- return {
71
- content: [{ type: "text", text: "You can only unregister your own services." }],
72
- structuredContent: {
73
- yourAgentId: requestInfo.agentId,
74
- success: false,
75
- message: "You can only unregister your own services.",
76
- },
77
- };
50
+ return toolErr("You can only unregister your own services.", {
51
+ data: { yourAgentId: requestInfo.agentId },
52
+ });
78
53
  }
79
54
 
80
55
  const deleted = deleteService(service.id);
81
56
  if (!deleted) {
82
- return {
83
- content: [{ type: "text", text: "Failed to unregister service." }],
84
- structuredContent: {
85
- yourAgentId: requestInfo.agentId,
86
- success: false,
87
- message: "Failed to unregister service.",
88
- },
89
- };
57
+ return toolErr("Failed to unregister service.", {
58
+ data: { yourAgentId: requestInfo.agentId },
59
+ });
90
60
  }
91
61
 
92
- return {
93
- content: [{ type: "text", text: `Unregistered service "${service.name}".` }],
94
- structuredContent: {
95
- yourAgentId: requestInfo.agentId,
96
- success: true,
97
- message: `Unregistered service "${service.name}".`,
98
- },
99
- };
62
+ return toolOk(`Unregistered service "${service.name}".`, {
63
+ data: { yourAgentId: requestInfo.agentId },
64
+ });
100
65
  } catch (error) {
101
66
  const message = error instanceof Error ? error.message : "Unknown error";
102
- return {
103
- content: [{ type: "text", text: `Failed to unregister service: ${message}` }],
104
- structuredContent: {
105
- yourAgentId: requestInfo.agentId,
106
- success: false,
107
- message: `Failed to unregister service: ${message}`,
108
- },
109
- };
67
+ return toolErr(`Failed to unregister service: ${message}`, {
68
+ data: { yourAgentId: requestInfo.agentId },
69
+ });
110
70
  }
111
71
  },
112
72
  );
@@ -5,8 +5,36 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import * as z from "zod";
6
6
  import { getAgentById, updateAgentName, updateAgentProfile } from "@/be/db";
7
7
  import { can } from "@/rbac";
8
- import { createToolRegistrar } from "@/tools/utils";
9
- import { type Agent, AgentAvatarSchema, AgentSchema } from "@/types";
8
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
9
+ import { type Agent, AgentAvatarSchema, AgentStatusSchema, ProviderNameSchema } from "@/types";
10
+
11
+ // Loose mirror of AgentSchema for tool output: every field optional, no
12
+ // datetime/uuid format pins, nested blobs collapsed to permissive objects.
13
+ const agentOutputShape = z.looseObject({
14
+ id: z.string().optional(),
15
+ name: z.string().optional(),
16
+ isLead: z.boolean().optional(),
17
+ status: AgentStatusSchema.optional(),
18
+ description: z.string().optional(),
19
+ role: z.string().optional(),
20
+ capabilities: z.array(z.string()).optional(),
21
+ claudeMd: z.string().optional(),
22
+ soulMd: z.string().optional(),
23
+ identityMd: z.string().optional(),
24
+ setupScript: z.string().optional(),
25
+ toolsMd: z.string().optional(),
26
+ heartbeatMd: z.string().optional(),
27
+ maxTasks: z.number().optional(),
28
+ emptyPollCount: z.number().optional(),
29
+ lastActivityAt: z.string().optional(),
30
+ provider: ProviderNameSchema.optional(),
31
+ harnessProvider: ProviderNameSchema.nullable().optional(),
32
+ credentialMissing: z.array(z.string()).nullable().optional(),
33
+ credStatus: z.looseObject({}).nullable().optional(),
34
+ avatar: z.looseObject({}).nullable().optional(),
35
+ createdAt: z.string().optional(),
36
+ lastUpdatedAt: z.string().optional(),
37
+ });
10
38
 
11
39
  async function validateSetupScriptSyntax(setupScript: string): Promise<string | null> {
12
40
  const proc = Bun.spawn(["bash", "-n", "-c", setupScript], {
@@ -126,11 +154,9 @@ export const registerUpdateProfileTool = (server: McpServer) => {
126
154
  "Custom avatar: { type: 'lucide', icon: '<kebab-case-lucide-name>', color?: '#RRGGBB' }. Pass null to reset to the default deterministic icon/color.",
127
155
  ),
128
156
  }),
129
- outputSchema: z.object({
157
+ outputSchema: swarmToolOutputSchema({
130
158
  yourAgentId: z.string().optional(),
131
- success: z.boolean(),
132
- message: z.string(),
133
- agent: AgentSchema.optional(),
159
+ agent: agentOutputShape.optional(),
134
160
  }),
135
161
  },
136
162
  async (
@@ -152,13 +178,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
152
178
  _meta,
153
179
  ) => {
154
180
  if (!requestInfo.agentId) {
155
- return {
156
- content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
157
- structuredContent: {
158
- success: false,
159
- message: 'Agent ID not found. Set the "X-Agent-ID" header.',
160
- },
161
- };
181
+ return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
162
182
  }
163
183
 
164
184
  // Determine target agent: if agentId is provided, check lead permissions
@@ -169,14 +189,9 @@ export const registerUpdateProfileTool = (server: McpServer) => {
169
189
  // Only lead agents can update other agents' profiles
170
190
  const callingAgent = getAgentById(requestInfo.agentId);
171
191
  if (!callingAgent) {
172
- return {
173
- content: [{ type: "text", text: "Calling agent not found." }],
174
- structuredContent: {
175
- yourAgentId: requestInfo.agentId,
176
- success: false,
177
- message: "Calling agent not found.",
178
- },
179
- };
192
+ return toolErr("Calling agent not found.", {
193
+ data: { yourAgentId: requestInfo.agentId },
194
+ });
180
195
  }
181
196
  const decision = can({
182
197
  principal: { kind: "agent", agentId: callingAgent.id, isLead: callingAgent.isLead },
@@ -185,33 +200,18 @@ export const registerUpdateProfileTool = (server: McpServer) => {
185
200
  source: "mcp",
186
201
  });
187
202
  if (!decision.allow) {
188
- return {
189
- content: [
190
- {
191
- type: "text",
192
- text: "Only lead agents can update other agents' profiles. Provide no agentId to update your own profile.",
193
- },
194
- ],
195
- structuredContent: {
196
- yourAgentId: requestInfo.agentId,
197
- success: false,
198
- message:
199
- "Only lead agents can update other agents' profiles. Provide no agentId to update your own profile.",
200
- },
201
- };
203
+ return toolErr(
204
+ "Only lead agents can update other agents' profiles. Provide no agentId to update your own profile.",
205
+ { data: { yourAgentId: requestInfo.agentId } },
206
+ );
202
207
  }
203
208
 
204
209
  // Validate target agent exists before proceeding
205
210
  const targetAgent = getAgentById(targetAgentId);
206
211
  if (!targetAgent) {
207
- return {
208
- content: [{ type: "text", text: `Target agent ${targetAgentId} not found.` }],
209
- structuredContent: {
210
- yourAgentId: requestInfo.agentId,
211
- success: false,
212
- message: `Target agent ${targetAgentId} not found.`,
213
- },
214
- };
212
+ return toolErr(`Target agent ${targetAgentId} not found.`, {
213
+ data: { yourAgentId: requestInfo.agentId },
214
+ });
215
215
  }
216
216
  }
217
217
 
@@ -229,20 +229,10 @@ export const registerUpdateProfileTool = (server: McpServer) => {
229
229
  heartbeatMd === undefined &&
230
230
  avatar === undefined
231
231
  ) {
232
- return {
233
- content: [
234
- {
235
- type: "text",
236
- text: "At least one field (name, description, role, capabilities, claudeMd, soulMd, identityMd, setupScript, toolsMd, heartbeatMd, or avatar) must be provided.",
237
- },
238
- ],
239
- structuredContent: {
240
- yourAgentId: requestInfo.agentId,
241
- success: false,
242
- message:
243
- "At least one field (name, description, role, capabilities, claudeMd, soulMd, identityMd, setupScript, toolsMd, heartbeatMd, or avatar) must be provided.",
244
- },
245
- };
232
+ return toolErr(
233
+ "At least one field (name, description, role, capabilities, claudeMd, soulMd, identityMd, setupScript, toolsMd, heartbeatMd, or avatar) must be provided.",
234
+ { data: { yourAgentId: requestInfo.agentId } },
235
+ );
246
236
  }
247
237
 
248
238
  try {
@@ -253,14 +243,9 @@ export const registerUpdateProfileTool = (server: McpServer) => {
253
243
  if (setupScript !== undefined) {
254
244
  const syntaxError = await validateSetupScriptSyntax(setupScript);
255
245
  if (syntaxError) {
256
- return {
257
- content: [{ type: "text", text: `Invalid setupScript: ${syntaxError}` }],
258
- structuredContent: {
259
- yourAgentId: requestInfo.agentId,
260
- success: false,
261
- message: `Invalid setupScript: ${syntaxError}`,
262
- },
263
- };
246
+ return toolErr(`Invalid setupScript: ${syntaxError}`, {
247
+ data: { yourAgentId: requestInfo.agentId },
248
+ });
264
249
  }
265
250
  }
266
251
 
@@ -268,14 +253,9 @@ export const registerUpdateProfileTool = (server: McpServer) => {
268
253
  if (name !== undefined) {
269
254
  agent = updateAgentName(targetAgentId, name);
270
255
  if (!agent) {
271
- return {
272
- content: [{ type: "text", text: "Target agent not found." }],
273
- structuredContent: {
274
- yourAgentId: requestInfo.agentId,
275
- success: false,
276
- message: "Target agent not found.",
277
- },
278
- };
256
+ return toolErr("Target agent not found.", {
257
+ data: { yourAgentId: requestInfo.agentId },
258
+ });
279
259
  }
280
260
  }
281
261
 
@@ -368,14 +348,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
368
348
  }
369
349
 
370
350
  if (!agent) {
371
- return {
372
- content: [{ type: "text", text: "Agent not found." }],
373
- structuredContent: {
374
- yourAgentId: requestInfo.agentId,
375
- success: false,
376
- message: "Agent not found.",
377
- },
378
- };
351
+ return toolErr("Agent not found.", { data: { yourAgentId: requestInfo.agentId } });
379
352
  }
380
353
 
381
354
  const updatedFields: string[] = [];
@@ -392,27 +365,14 @@ export const registerUpdateProfileTool = (server: McpServer) => {
392
365
  if (avatar !== undefined) updatedFields.push("avatar");
393
366
 
394
367
  const targetLabel = isUpdatingSelf ? "own" : `agent ${targetAgentId}`;
395
- return {
396
- content: [
397
- { type: "text", text: `Updated ${targetLabel} profile: ${updatedFields.join(", ")}.` },
398
- ],
399
- structuredContent: {
400
- yourAgentId: requestInfo.agentId,
401
- success: true,
402
- message: `Updated ${targetLabel} profile: ${updatedFields.join(", ")}.`,
403
- agent,
404
- },
405
- };
368
+ return toolOk(`Updated ${targetLabel} profile: ${updatedFields.join(", ")}.`, {
369
+ data: { yourAgentId: requestInfo.agentId, agent },
370
+ });
406
371
  } catch (error) {
407
372
  const message = error instanceof Error ? error.message : "Unknown error";
408
- return {
409
- content: [{ type: "text", text: `Failed to update profile: ${message}` }],
410
- structuredContent: {
411
- yourAgentId: requestInfo.agentId,
412
- success: false,
413
- message: `Failed to update profile: ${message}`,
414
- },
415
- };
373
+ return toolErr(`Failed to update profile: ${message}`, {
374
+ data: { yourAgentId: requestInfo.agentId },
375
+ });
416
376
  }
417
377
  },
418
378
  );
@@ -1,8 +1,29 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { getServiceByAgentAndName, getServiceById, updateServiceStatus } from "@/be/db";
4
- import { createToolRegistrar } from "@/tools/utils";
5
- import { ServiceSchema, ServiceStatusSchema } from "@/types";
4
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
+ import { ServiceStatusSchema } from "@/types";
6
+
7
+ // Loose mirror of ServiceSchema for tool output: every field optional, no
8
+ // url/uuid/datetime format pins.
9
+ const serviceOutputShape = z.looseObject({
10
+ id: z.string().optional(),
11
+ agentId: z.string().optional(),
12
+ name: z.string().optional(),
13
+ port: z.number().optional(),
14
+ description: z.string().optional(),
15
+ url: z.string().optional(),
16
+ healthCheckPath: z.string().optional(),
17
+ status: ServiceStatusSchema.optional(),
18
+ script: z.string().optional(),
19
+ cwd: z.string().optional(),
20
+ interpreter: z.string().optional(),
21
+ args: z.array(z.string()).optional(),
22
+ env: z.record(z.string(), z.string()).optional(),
23
+ metadata: z.record(z.string(), z.unknown()).optional(),
24
+ createdAt: z.string().optional(),
25
+ lastUpdatedAt: z.string().optional(),
26
+ });
6
27
 
7
28
  export const registerUpdateServiceStatusTool = (server: McpServer) => {
8
29
  createToolRegistrar(server)(
@@ -20,33 +41,20 @@ export const registerUpdateServiceStatusTool = (server: McpServer) => {
20
41
  "New status: 'starting', 'healthy', 'unhealthy', or 'stopped'.",
21
42
  ),
22
43
  }),
23
- outputSchema: z.object({
44
+ outputSchema: swarmToolOutputSchema({
24
45
  yourAgentId: z.string().optional(),
25
- success: z.boolean(),
26
- message: z.string(),
27
- service: ServiceSchema.optional(),
46
+ service: serviceOutputShape.optional(),
28
47
  }),
29
48
  },
30
49
  async ({ serviceId, name, status }, requestInfo, _meta) => {
31
50
  if (!requestInfo.agentId) {
32
- return {
33
- content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
34
- structuredContent: {
35
- success: false,
36
- message: 'Agent ID not found. Set the "X-Agent-ID" header.',
37
- },
38
- };
51
+ return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
39
52
  }
40
53
 
41
54
  if (!serviceId && !name) {
42
- return {
43
- content: [{ type: "text", text: "Either serviceId or name is required." }],
44
- structuredContent: {
45
- yourAgentId: requestInfo.agentId,
46
- success: false,
47
- message: "Either serviceId or name is required.",
48
- },
49
- };
55
+ return toolErr("Either serviceId or name is required.", {
56
+ data: { yourAgentId: requestInfo.agentId },
57
+ });
50
58
  }
51
59
 
52
60
  try {
@@ -57,64 +65,31 @@ export const registerUpdateServiceStatusTool = (server: McpServer) => {
57
65
  }
58
66
 
59
67
  if (!service) {
60
- return {
61
- content: [{ type: "text", text: "Service not found." }],
62
- structuredContent: {
63
- yourAgentId: requestInfo.agentId,
64
- success: false,
65
- message: "Service not found.",
66
- },
67
- };
68
+ return toolErr("Service not found.", { data: { yourAgentId: requestInfo.agentId } });
68
69
  }
69
70
 
70
71
  // Check ownership
71
72
  if (service.agentId !== requestInfo.agentId) {
72
- return {
73
- content: [{ type: "text", text: "You can only update status of your own services." }],
74
- structuredContent: {
75
- yourAgentId: requestInfo.agentId,
76
- success: false,
77
- message: "You can only update status of your own services.",
78
- },
79
- };
73
+ return toolErr("You can only update status of your own services.", {
74
+ data: { yourAgentId: requestInfo.agentId },
75
+ });
80
76
  }
81
77
 
82
78
  const updated = updateServiceStatus(service.id, status);
83
79
  if (!updated) {
84
- return {
85
- content: [{ type: "text", text: "Failed to update service status." }],
86
- structuredContent: {
87
- yourAgentId: requestInfo.agentId,
88
- success: false,
89
- message: "Failed to update service status.",
90
- },
91
- };
80
+ return toolErr("Failed to update service status.", {
81
+ data: { yourAgentId: requestInfo.agentId },
82
+ });
92
83
  }
93
84
 
94
- return {
95
- content: [
96
- {
97
- type: "text",
98
- text: `Updated service "${service.name}" status to "${status}".`,
99
- },
100
- ],
101
- structuredContent: {
102
- yourAgentId: requestInfo.agentId,
103
- success: true,
104
- message: `Updated service "${service.name}" status to "${status}".`,
105
- service: updated,
106
- },
107
- };
85
+ return toolOk(`Updated service "${service.name}" status to "${status}".`, {
86
+ data: { yourAgentId: requestInfo.agentId, service: updated },
87
+ });
108
88
  } catch (error) {
109
89
  const message = error instanceof Error ? error.message : "Unknown error";
110
- return {
111
- content: [{ type: "text", text: `Failed to update service status: ${message}` }],
112
- structuredContent: {
113
- yourAgentId: requestInfo.agentId,
114
- success: false,
115
- message: `Failed to update service status: ${message}`,
116
- },
117
- };
90
+ return toolErr(`Failed to update service status: ${message}`, {
91
+ data: { yourAgentId: requestInfo.agentId },
92
+ });
118
93
  }
119
94
  },
120
95
  );