shadow-claw 1.28.0 → 1.29.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 (194) hide show
  1. package/.well-known/mcp/server-card.json +1 -1
  2. package/.well-known/mcp.json +1 -1
  3. package/README.md +96 -278
  4. package/bin/cli.mjs +4 -0
  5. package/bin/commands/mcp.mjs +409 -125
  6. package/bin/commands/mcp.test.mjs +151 -18
  7. package/bin/utils/control-client.mjs +338 -103
  8. package/bin/utils/control-client.test.mjs +14 -0
  9. package/dist/electron/main.cjs +179 -179
  10. package/dist/lib/components/index.js +21 -21
  11. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  12. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  13. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  14. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  15. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  16. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  17. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  18. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  19. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  20. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  21. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  22. package/dist/lib/components/shadow-claw-channels.js +1 -1
  23. package/dist/lib/components/shadow-claw-chat.js +1 -1
  24. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  25. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  26. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  27. package/dist/lib/components/shadow-claw-files.js +1 -1
  28. package/dist/lib/components/shadow-claw-git.js +1 -1
  29. package/dist/lib/components/shadow-claw-llm.js +1 -1
  30. package/dist/lib/components/shadow-claw-networking.js +1 -1
  31. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  32. package/dist/lib/components/shadow-claw-pages.js +1 -1
  33. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  34. package/dist/lib/components/shadow-claw-settings.js +1 -1
  35. package/dist/lib/components/shadow-claw-storage.js +1 -1
  36. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  37. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  38. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  39. package/dist/lib/components/shadow-claw-tools.js +1 -1
  40. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  41. package/dist/lib/components/shadow-claw.js +1 -1
  42. package/dist/lib/{configurePeerJs-D8Ph0-A9.js → configurePeerJs-DG4hliKp.js} +2 -2
  43. package/dist/lib/{configurePeerJs-D8Ph0-A9.js.map → configurePeerJs-DG4hliKp.js.map} +1 -1
  44. package/dist/lib/index.js +21 -21
  45. package/dist/lib/{initControlPlane-BIrWmpzc.js → initControlPlane-DnpAzphg.js} +8 -8
  46. package/dist/lib/{initControlPlane-BIrWmpzc.js.map → initControlPlane-DnpAzphg.js.map} +1 -1
  47. package/dist/lib/{markdown-DEUoM1c6.js → markdown-DgdSt2GD.js} +2 -2
  48. package/dist/lib/{markdown-DEUoM1c6.js.map → markdown-DgdSt2GD.js.map} +1 -1
  49. package/dist/lib/{orchestrator-CFCW-l1O.js → orchestrator-CjEau_RG.js} +5 -5
  50. package/dist/lib/{orchestrator-CFCW-l1O.js.map → orchestrator-CjEau_RG.js.map} +1 -1
  51. package/dist/lib/{orchestrator-BtVj9bBB.js → orchestrator-ClXbJ4Mb.js} +2 -2
  52. package/dist/lib/{orchestrator-BtVj9bBB.js.map → orchestrator-ClXbJ4Mb.js.map} +1 -1
  53. package/dist/lib/{peerjs-BoBkx9DJ.js → peerjs-BdfpltIe.js} +3 -3
  54. package/dist/lib/{peerjs-BoBkx9DJ.js.map → peerjs-BdfpltIe.js.map} +1 -1
  55. package/dist/lib/{push-client-D6hbzDNV.js → push-client-DjwKknNg.js} +9 -3
  56. package/dist/lib/push-client-DjwKknNg.js.map +1 -0
  57. package/dist/lib/{shadow-claw-Cuf2O5bZ.js → shadow-claw-DcPGKenn.js} +4 -4
  58. package/dist/lib/{shadow-claw-Cuf2O5bZ.js.map → shadow-claw-DcPGKenn.js.map} +1 -1
  59. package/dist/lib/{shadow-claw-channel-config-BDPKt6Bd.js → shadow-claw-channel-config-DEL-aZ7N.js} +3 -3
  60. package/dist/lib/{shadow-claw-channel-config-BDPKt6Bd.js.map → shadow-claw-channel-config-DEL-aZ7N.js.map} +1 -1
  61. package/dist/lib/{shadow-claw-channels-BsP5JABM.js → shadow-claw-channels-BgaJiPdE.js} +2 -2
  62. package/dist/lib/{shadow-claw-channels-BsP5JABM.js.map → shadow-claw-channels-BgaJiPdE.js.map} +1 -1
  63. package/dist/lib/{shadow-claw-chat-Cam2lX4k.js → shadow-claw-chat-mXPQZbwT.js} +4 -4
  64. package/dist/lib/{shadow-claw-chat-Cam2lX4k.js.map → shadow-claw-chat-mXPQZbwT.js.map} +1 -1
  65. package/dist/lib/{shadow-claw-control-plane-DB2qcCFs.js → shadow-claw-control-plane-DO6foOLV.js} +6 -6
  66. package/dist/lib/{shadow-claw-control-plane-DB2qcCFs.js.map → shadow-claw-control-plane-DO6foOLV.js.map} +1 -1
  67. package/dist/lib/{shadow-claw-conversations-C9Vf4XGx.js → shadow-claw-conversations-BLwHTwdW.js} +2 -2
  68. package/dist/lib/{shadow-claw-conversations-C9Vf4XGx.js.map → shadow-claw-conversations-BLwHTwdW.js.map} +1 -1
  69. package/dist/lib/{shadow-claw-file-viewer-B4HYiI0v.js → shadow-claw-file-viewer-BSbZvSl-.js} +3 -3
  70. package/dist/lib/{shadow-claw-file-viewer-B4HYiI0v.js.map → shadow-claw-file-viewer-BSbZvSl-.js.map} +1 -1
  71. package/dist/lib/{shadow-claw-files-DVROUESO.js → shadow-claw-files-BLVr9Pua.js} +2 -2
  72. package/dist/lib/{shadow-claw-files-DVROUESO.js.map → shadow-claw-files-BLVr9Pua.js.map} +1 -1
  73. package/dist/lib/{shadow-claw-git-BqrO7cHJ.js → shadow-claw-git-CHC1H86J.js} +3 -3
  74. package/dist/lib/{shadow-claw-git-BqrO7cHJ.js.map → shadow-claw-git-CHC1H86J.js.map} +1 -1
  75. package/dist/lib/{shadow-claw-llm-BexUQdYo.js → shadow-claw-llm-CKXfxd3D.js} +2 -2
  76. package/dist/lib/{shadow-claw-llm-BexUQdYo.js.map → shadow-claw-llm-CKXfxd3D.js.map} +1 -1
  77. package/dist/lib/{shadow-claw-networking-CfXRGpUD.js → shadow-claw-networking-dpYvfchh.js} +2 -2
  78. package/dist/lib/{shadow-claw-networking-CfXRGpUD.js.map → shadow-claw-networking-dpYvfchh.js.map} +1 -1
  79. package/dist/lib/{shadow-claw-notifications-C9KPMgx_.js → shadow-claw-notifications-_YeAVPga.js} +2 -2
  80. package/dist/lib/{shadow-claw-notifications-C9KPMgx_.js.map → shadow-claw-notifications-_YeAVPga.js.map} +1 -1
  81. package/dist/lib/{shadow-claw-pages-CGzGXmrI.js → shadow-claw-pages-DVzZriZ7.js} +3 -3
  82. package/dist/lib/{shadow-claw-pages-CGzGXmrI.js.map → shadow-claw-pages-DVzZriZ7.js.map} +1 -1
  83. package/dist/lib/{shadow-claw-peerjs-B0ZB7yhB.js → shadow-claw-peerjs-BvixjeBz.js} +3 -3
  84. package/dist/lib/{shadow-claw-peerjs-B0ZB7yhB.js.map → shadow-claw-peerjs-BvixjeBz.js.map} +1 -1
  85. package/dist/lib/{shadow-claw-settings-DBfMZdDS.js → shadow-claw-settings-DV9-gD4Q.js} +2 -2
  86. package/dist/lib/{shadow-claw-settings-DBfMZdDS.js.map → shadow-claw-settings-DV9-gD4Q.js.map} +1 -1
  87. package/dist/lib/{shadow-claw-storage-CM-w6mgG.js → shadow-claw-storage-Bv__kXG6.js} +2 -2
  88. package/dist/lib/{shadow-claw-storage-CM-w6mgG.js.map → shadow-claw-storage-Bv__kXG6.js.map} +1 -1
  89. package/dist/lib/{shadow-claw-task-server-DV4b1g2-.js → shadow-claw-task-server-DNOe8PWK.js} +2 -2
  90. package/dist/lib/{shadow-claw-task-server-DV4b1g2-.js.map → shadow-claw-task-server-DNOe8PWK.js.map} +1 -1
  91. package/dist/lib/{shadow-claw-tasks-K-foOHSE.js → shadow-claw-tasks-qmcDT4PU.js} +3 -3
  92. package/dist/lib/{shadow-claw-tasks-K-foOHSE.js.map → shadow-claw-tasks-qmcDT4PU.js.map} +1 -1
  93. package/dist/lib/{shadow-claw-terminal-CpAknKyA.js → shadow-claw-terminal-0QUWP8of.js} +2 -2
  94. package/dist/lib/{shadow-claw-terminal-CpAknKyA.js.map → shadow-claw-terminal-0QUWP8of.js.map} +1 -1
  95. package/dist/lib/{shadow-claw-tools-D98XZ0Ep.js → shadow-claw-tools-Bs_bmI6-.js} +3 -3
  96. package/dist/lib/{shadow-claw-tools-D98XZ0Ep.js.map → shadow-claw-tools-Bs_bmI6-.js.map} +1 -1
  97. package/dist/lib/{shadow-claw-webvm-DLOb8dMI.js → shadow-claw-webvm-DYaoL7QY.js} +2 -2
  98. package/dist/lib/{shadow-claw-webvm-DLOb8dMI.js.map → shadow-claw-webvm-DYaoL7QY.js.map} +1 -1
  99. package/dist/lib/{syncWebMcpRegistration-NspKefOI.js → syncWebMcpRegistration-BhbqI6R2.js} +2 -2
  100. package/dist/lib/{syncWebMcpRegistration-NspKefOI.js.map → syncWebMcpRegistration-BhbqI6R2.js.map} +1 -1
  101. package/dist/public/.well-known/mcp/server-card.json +1 -1
  102. package/dist/public/.well-known/mcp.json +1 -1
  103. package/dist/public/AGENTS.md +22 -3
  104. package/dist/public/README.md +96 -278
  105. package/dist/public/agent.worker.js +50 -50
  106. package/dist/public/{base-task-model-CYkpwnvU-DDfHUUIS.js → base-task-model-CYkpwnvU-DrXr4G1R.js} +1 -1
  107. package/dist/public/{configurePeerJs-BhBokQlC.js → configurePeerJs-B470pdeU.js} +1 -1
  108. package/dist/public/docs/README.md +31 -31
  109. package/dist/public/docs/architecture/context-management.md +7 -0
  110. package/dist/public/docs/architecture/storage.md +10 -1
  111. package/dist/public/docs/decisions/bundled-typescript-architecture.md +0 -1
  112. package/dist/public/docs/decisions/native-web-components-and-signals.md +0 -1
  113. package/dist/public/docs/example/article/index.html +1 -1
  114. package/dist/public/docs/guides/publishing-to-github-pages.md +334 -0
  115. package/dist/public/docs/guides/server-development-configuration.md +15 -0
  116. package/dist/public/docs/publishing/index.html +1 -1
  117. package/dist/public/docs/skill-creator/index.html +1 -1
  118. package/dist/public/docs/subsystems/cli.md +10 -2
  119. package/dist/public/docs/subsystems/control-plane.md +9 -8
  120. package/dist/public/docs/subsystems/crypto.md +9 -0
  121. package/dist/public/docs/subsystems/custom-element-security.md +2 -0
  122. package/dist/public/docs/subsystems/mcp-server.md +31 -16
  123. package/dist/public/docs/subsystems/notifications.md +8 -5
  124. package/dist/public/docs/subsystems/providers.md +9 -0
  125. package/dist/public/docs/subsystems/share-target.md +107 -0
  126. package/dist/public/docs/subsystems/webmcp.md +43 -0
  127. package/dist/public/e2e/README.md +4 -2
  128. package/dist/public/{e2e-bridge-DcBoeFvd.js → e2e-bridge-CSW0S5Yz.js} +1 -1
  129. package/dist/public/{file-viewer-CKtVC9Ix.js → file-viewer-Cs-riNvy.js} +1 -1
  130. package/dist/public/{firebase-OSDX3viP.js → firebase-cwAl_ABm.js} +1 -1
  131. package/dist/public/{git-CkhCJonj.js → git-GD09WATp.js} +25 -25
  132. package/dist/public/index.html +1 -1
  133. package/dist/public/index.js +1 -1
  134. package/dist/public/{initControlPlane-Bi8ouFEb.js → initControlPlane-BqCY0NQy.js} +1 -1
  135. package/dist/public/initControlPlane-C-IXaKYX.js +1 -0
  136. package/dist/public/{language-detector-BRKrlra0.js → language-detector-CIeEuQFk.js} +1 -1
  137. package/dist/public/llms.txt +4 -2
  138. package/dist/public/main/index.html +1 -1
  139. package/dist/public/main/memory/index.html +1 -1
  140. package/dist/public/{markdown-C-hCVJtc.js → markdown-Cw7vCtTg.js} +1 -1
  141. package/dist/public/{openai-Bpd71Oja.js → openai-BJIb2c5j.js} +3 -3
  142. package/dist/public/{orchestrator-8qNGfw8r.js → orchestrator-BWkHHO9Y.js} +1 -1
  143. package/dist/public/{orchestrator-BT3Ks5ip.js → orchestrator-DpkkeD6T.js} +1 -1
  144. package/dist/public/{peerjs-DfhwEiiq.js → peerjs-tf2EmWjB.js} +1 -1
  145. package/dist/public/{prompt-api-polyfill-Dl1kbJhR.js → prompt-api-polyfill-CXd75leW.js} +1 -1
  146. package/dist/public/{push-client-BVLrE9pK.js → push-client-CHjSbrWs.js} +1 -1
  147. package/dist/public/{rewriter-j6D9LDER.js → rewriter-BEHn9gll.js} +1 -1
  148. package/dist/public/service-worker.js +1 -1
  149. package/dist/public/{shadow-claw-DCjxNDCX.js → shadow-claw-BBPV_9X8.js} +2 -2
  150. package/dist/public/{shadow-claw-channels-CITDrqMl.js → shadow-claw-channels-BJvrKQjg.js} +1 -1
  151. package/dist/public/{shadow-claw-chat-BQrSgSKF.js → shadow-claw-chat-BG2EaRaw.js} +1 -1
  152. package/dist/public/{shadow-claw-control-plane-Cp001OY0.js → shadow-claw-control-plane-BBa7WbTg.js} +2 -2
  153. package/dist/public/{shadow-claw-conversations-EhX4RvIj.js → shadow-claw-conversations-CFaWFmkm.js} +1 -1
  154. package/dist/public/{shadow-claw-file-viewer-DiQXIjZD.js → shadow-claw-file-viewer-Z-GIKaE9.js} +1 -1
  155. package/dist/public/{shadow-claw-files-lkTnSJsn.js → shadow-claw-files-BBx4iMsk.js} +1 -1
  156. package/dist/public/{shadow-claw-git-BlcA_z8x.js → shadow-claw-git-vTOKatwd.js} +2 -2
  157. package/dist/public/{shadow-claw-llm-CW16X9Rg.js → shadow-claw-llm-Ce1Ax6-x.js} +1 -1
  158. package/dist/public/{shadow-claw-networking-ySsMBY_i.js → shadow-claw-networking-q4XIzdB5.js} +1 -1
  159. package/dist/public/{shadow-claw-notifications-CyV2zCQ8.js → shadow-claw-notifications-D2cz0gmm.js} +1 -1
  160. package/dist/public/{shadow-claw-pages-Cx7cF9ah.js → shadow-claw-pages-3335lmSC.js} +1 -1
  161. package/dist/public/{shadow-claw-settings-zm4PHHGi.js → shadow-claw-settings-4ptYuJwO.js} +2 -2
  162. package/dist/public/{shadow-claw-storage-dy9JAEGd.js → shadow-claw-storage-bIrPKFhF.js} +1 -1
  163. package/dist/public/{shadow-claw-task-server-Bleww48s.js → shadow-claw-task-server-5yqVBxy1.js} +1 -1
  164. package/dist/public/{shadow-claw-tasks-CTI5oaLN.js → shadow-claw-tasks-DcqYHPLZ.js} +1 -1
  165. package/dist/public/{shadow-claw-terminal-mZ3i4HaQ.js → shadow-claw-terminal-fARaDONr.js} +1 -1
  166. package/dist/public/{shadow-claw-tools-B_nuk8Zl.js → shadow-claw-tools-paEAhcJr.js} +1 -1
  167. package/dist/public/{shadow-claw-webvm-Vl_jH5YL.js → shadow-claw-webvm-CbzH_vyX.js} +1 -1
  168. package/dist/public/{summarizer-VJNr-7xG.js → summarizer-BT3AuFed.js} +1 -1
  169. package/dist/public/{syncWebMcpRegistration-n2vAMJ4w.js → syncWebMcpRegistration-CWSYaHex.js} +1 -1
  170. package/dist/public/{transformers-BhQMWQG5.js → transformers-1VTxqKjT.js} +1 -1
  171. package/dist/public/{translator-DEQXb7ZL.js → translator-B50RYlFt.js} +1 -1
  172. package/dist/public/{webllm-CHN5H-Sy.js → webllm-FmyJWWpt.js} +1 -1
  173. package/dist/public/{writer-CD9oN2pq.js → writer-Bhb4YYRE.js} +1 -1
  174. package/dist/server.js +205 -203
  175. package/package.json +1 -1
  176. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +2 -0
  177. package/src/core/orchestrator/utils/configurePeerJs.test.ts +2 -0
  178. package/src/server/client-registry.test.ts +9 -0
  179. package/src/server/client-registry.ts +14 -0
  180. package/src/server/mcp/mcp-integration.test.ts +68 -16
  181. package/src/server/mcp/tools/built-in-tools.ts +125 -34
  182. package/src/server/mcp/tools/client-tool-relay.ts +81 -44
  183. package/src/server/mcp/tools/mcp-tools.test.ts +103 -13
  184. package/src/server/mcp/types.ts +3 -0
  185. package/src/server/openapi/paths/push.ts +48 -0
  186. package/src/server/server.ts +30 -0
  187. package/src/subsystems/notifications/push-client.test.ts +17 -0
  188. package/src/subsystems/notifications/push-client.ts +22 -2
  189. package/src/subsystems/notifications/push-routes.test.ts +31 -0
  190. package/src/subsystems/notifications/push-routes.ts +10 -0
  191. package/src/subsystems/notifications/push-store.test.ts +49 -0
  192. package/src/subsystems/notifications/push-store.ts +45 -0
  193. package/src/worker/worker.test.ts +2 -0
  194. package/dist/lib/push-client-D6hbzDNV.js.map +0 -1
@@ -89,17 +89,50 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
89
89
  expect(res).toBeDefined();
90
90
  expect(res.result.resultType).toBe("complete");
91
91
  const toolNames = res.result.tools.map((t) => t.name);
92
- expect(toolNames).toContain("shadowclaw_list_clients");
93
- expect(toolNames).toContain("shadowclaw_send_message");
94
- expect(toolNames).toContain("shadowclaw_send_notification");
95
- expect(toolNames).toContain("read_file");
92
+ expect(toolNames).toContain("shadowclaw_server_list_clients");
93
+ expect(toolNames).toContain("shadowclaw_server_send_message");
94
+ expect(toolNames).toContain("shadowclaw_server_send_notification");
95
+ expect(toolNames).toContain("shadowclaw_server_status");
96
+ expect(toolNames).toContain("shadowclaw_client_read_file");
96
97
 
97
98
  const notifTool = res.result.tools.find(
98
- (t) => t.name === "shadowclaw_send_notification",
99
+ (t) => t.name === "shadowclaw_server_send_notification",
99
100
  );
100
101
  expect(notifTool.inputSchema.properties).toHaveProperty("clientId");
101
102
  });
102
103
 
104
+ it("populates clientId enum and description for shadowclaw_server_send_notification when push clients are registered", async () => {
105
+ const mockClient = {
106
+ listClients: jest.fn().mockResolvedValue([]),
107
+ listPushClients: jest.fn().mockResolvedValue([
108
+ { clientId: "client-pwa-1", deviceLabel: "Android Pixel" },
109
+ { clientId: "client-pwa-2", deviceLabel: "iPad Safari" },
110
+ ]),
111
+ };
112
+
113
+ const engine = createCliMcpEngine({
114
+ client: mockClient,
115
+ relayClientTools: false,
116
+ });
117
+ const res = await engine.handleMessage({
118
+ jsonrpc: "2.0",
119
+ id: 20,
120
+ method: "tools/list",
121
+ });
122
+
123
+ expect(res).toBeDefined();
124
+ expect(res.result.resultType).toBe("complete");
125
+ const notifTool = res.result.tools.find(
126
+ (t) => t.name === "shadowclaw_server_send_notification",
127
+ );
128
+ expect(notifTool).toBeDefined();
129
+ const clientIdProp = notifTool.inputSchema.properties.clientId;
130
+ expect(clientIdProp).toBeDefined();
131
+ expect(clientIdProp.enum).toEqual(["client-pwa-1", "client-pwa-2"]);
132
+ expect(clientIdProp.description).toContain("client-pwa-1 (Android Pixel)");
133
+ expect(clientIdProp.description).toContain("client-pwa-2 (iPad Safari)");
134
+ });
135
+
103
136
  it("processes tools/call for shadowclaw_send_notification directly via broadcastNotification", async () => {
104
137
  const mockClient = {
105
138
  listClients: jest.fn().mockResolvedValue([]),
@@ -251,7 +284,7 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
251
284
  );
252
285
  });
253
286
 
254
- it("processes tools/call for shadowclaw_send_message", async () => {
287
+ it("processes tools/call for shadowclaw_server_send_message and legacy alias", async () => {
255
288
  const mockClient = {
256
289
  listClients: jest
257
290
  .fn()
@@ -268,7 +301,7 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
268
301
  id: 3,
269
302
  method: "tools/call",
270
303
  params: {
271
- name: "shadowclaw_send_message",
304
+ name: "shadowclaw_server_send_message",
272
305
  arguments: { text: "Test message" },
273
306
  },
274
307
  });
@@ -279,9 +312,55 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
279
312
  text: "Test message",
280
313
  groupId: undefined,
281
314
  });
315
+
316
+ // Legacy alias
317
+ const resAlias = await engine.handleMessage({
318
+ jsonrpc: "2.0",
319
+ id: 4,
320
+ method: "tools/call",
321
+ params: {
322
+ name: "shadowclaw_send_message",
323
+ arguments: { text: "Legacy test message" },
324
+ },
325
+ });
326
+ expect(resAlias).toBeDefined();
327
+ expect(resAlias.result.resultType).toBe("complete");
328
+ expect(mockClient.sendCommand).toHaveBeenCalledWith("c1", "send-message", {
329
+ text: "Legacy test message",
330
+ groupId: undefined,
331
+ });
282
332
  });
283
333
 
284
- it("processes tools/call for relayed client tool (e.g. list_files)", async () => {
334
+ it("processes tools/call for shadowclaw_server_status", async () => {
335
+ const mockClient = {
336
+ listClients: jest
337
+ .fn()
338
+ .mockResolvedValue([{ clientId: "c1" }, { clientId: "c2" }]),
339
+ };
340
+
341
+ const engine = createCliMcpEngine({
342
+ client: mockClient,
343
+ version: "1.28.1",
344
+ });
345
+ const res = await engine.handleMessage({
346
+ jsonrpc: "2.0",
347
+ id: 5,
348
+ method: "tools/call",
349
+ params: {
350
+ name: "shadowclaw_server_status",
351
+ arguments: {},
352
+ },
353
+ });
354
+
355
+ expect(res).toBeDefined();
356
+ expect(res.result.resultType).toBe("complete");
357
+ const statusData = JSON.parse(res.result.content[0].text);
358
+ expect(statusData.server).toBe("ShadowClaw");
359
+ expect(statusData.connectedClients).toBe(2);
360
+ expect(statusData.status).toBe("healthy");
361
+ });
362
+
363
+ it("processes tools/call for relayed client tool (e.g. shadowclaw_client_list_files)", async () => {
285
364
  const mockClient = {
286
365
  listClients: jest
287
366
  .fn()
@@ -307,7 +386,7 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
307
386
  id: 4,
308
387
  method: "tools/call",
309
388
  params: {
310
- name: "list_files",
389
+ name: "shadowclaw_client_list_files",
311
390
  arguments: {},
312
391
  },
313
392
  });
@@ -321,6 +400,20 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
321
400
  toolName: "list_files",
322
401
  input: {},
323
402
  });
403
+
404
+ // Also verify backward compatibility: calling with unprefixed name also proxies
405
+ const resUnprefixed = await engine.handleMessage({
406
+ jsonrpc: "2.0",
407
+ id: 41,
408
+ method: "tools/call",
409
+ params: {
410
+ name: "list_files",
411
+ arguments: {},
412
+ },
413
+ });
414
+ expect(resUnprefixed.result.content[0].text).toBe(
415
+ ".agents/ MEMORY.md index.html -/",
416
+ );
324
417
  });
325
418
 
326
419
  it("handles tools/call relayed client tool failure with informative error content", async () => {
@@ -342,7 +435,7 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
342
435
  id: 5,
343
436
  method: "tools/call",
344
437
  params: {
345
- name: "list_files",
438
+ name: "shadowclaw_client_list_files",
346
439
  arguments: { path: "restricted" },
347
440
  },
348
441
  });
@@ -413,7 +506,7 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
413
506
  });
414
507
 
415
508
  const listFilesTool = listRes.result.tools.find(
416
- (t) => t.name === "list_files",
509
+ (t) => t.name === "shadowclaw_client_list_files",
417
510
  );
418
511
  expect(listFilesTool).toBeDefined();
419
512
  expect(listFilesTool.description).toContain("[Default: Linux Desktop");
@@ -424,7 +517,9 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
424
517
  ]);
425
518
 
426
519
  // ask_user should ONLY be available on client-alpha
427
- const askUserTool = listRes.result.tools.find((t) => t.name === "ask_user");
520
+ const askUserTool = listRes.result.tools.find(
521
+ (t) => t.name === "shadowclaw_client_ask_user",
522
+ );
428
523
  expect(askUserTool).toBeDefined();
429
524
  expect(askUserTool.description).toContain("[Client: Linux Desktop");
430
525
  expect(askUserTool.inputSchema.properties.clientId.enum).toEqual([
@@ -437,7 +532,7 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
437
532
  id: 11,
438
533
  method: "tools/call",
439
534
  params: {
440
- name: "list_files",
535
+ name: "shadowclaw_client_list_files",
441
536
  arguments: { clientId: "client-beta" },
442
537
  },
443
538
  });
@@ -473,7 +568,7 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
473
568
  id: 13,
474
569
  method: "tools/call",
475
570
  params: {
476
- name: "list_files",
571
+ name: "shadowclaw_client_list_files",
477
572
  arguments: {},
478
573
  },
479
574
  });
@@ -488,14 +583,14 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
488
583
  id: 14,
489
584
  method: "tools/call",
490
585
  params: {
491
- name: "ask_user",
586
+ name: "shadowclaw_client_ask_user",
492
587
  arguments: { clientId: "client-beta", question: "Prompt client beta?" },
493
588
  },
494
589
  });
495
590
 
496
591
  expect(unsupportedRes.result.isError).toBe(true);
497
592
  expect(unsupportedRes.result.content[0].text).toContain(
498
- "Tool 'ask_user' is not enabled or available on client 'client-beta'",
593
+ "Tool 'shadowclaw_client_ask_user' is not enabled or available on client 'client-beta'",
499
594
  );
500
595
  expect(unsupportedRes.result.content[0].text).toContain(
501
596
  "Available on: client-alpha",
@@ -508,7 +603,7 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
508
603
  id: 15,
509
604
  method: "tools/call",
510
605
  params: {
511
- name: "ask_user",
606
+ name: "shadowclaw_client_ask_user",
512
607
  arguments: { question: "Are you ready?" },
513
608
  },
514
609
  });
@@ -530,11 +625,49 @@ describe("CLI MCP Engine (createCliMcpEngine)", () => {
530
625
  id: 16,
531
626
  method: "tools/call",
532
627
  params: {
533
- name: "ask_user",
628
+ name: "shadowclaw_client_ask_user",
534
629
  arguments: { question: "Are you ready?" },
535
630
  inputResponses: { response: "Yes, ready to go!" },
536
631
  },
537
632
  });
538
633
  expect(fulfilledRes.result.content[0].text).toBe("Yes, ready to go!");
539
634
  });
635
+
636
+ it("handles client.listClients error gracefully in shadowclaw_server_list_clients and legacy alias", async () => {
637
+ const mockClient = {
638
+ listClients: jest
639
+ .fn()
640
+ .mockRejectedValue(new Error("Unauthorized: Invalid control token")),
641
+ };
642
+
643
+ const engine = createCliMcpEngine({ client: mockClient });
644
+ const res = await engine.handleMessage({
645
+ jsonrpc: "2.0",
646
+ id: 99,
647
+ method: "tools/call",
648
+ params: {
649
+ name: "shadowclaw_server_list_clients",
650
+ arguments: {},
651
+ },
652
+ });
653
+
654
+ expect(res).toBeDefined();
655
+ expect(res.id).toBe(99);
656
+ expect(res.result.isError).toBe(true);
657
+ expect(res.result.content[0].text).toContain(
658
+ "Error listing clients: Unauthorized: Invalid control token",
659
+ );
660
+
661
+ // Legacy alias
662
+ const resAlias = await engine.handleMessage({
663
+ jsonrpc: "2.0",
664
+ id: 100,
665
+ method: "tools/call",
666
+ params: {
667
+ name: "shadowclaw_list_clients",
668
+ arguments: {},
669
+ },
670
+ });
671
+ expect(resAlias.result.isError).toBe(true);
672
+ });
540
673
  });