@wardby/cli 0.7.0 → 0.9.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 (367) hide show
  1. package/.env.example +5 -5
  2. package/README.md +6 -1
  3. package/deploy/local/docker-compose.quickstart-coding.yml +11 -0
  4. package/dist/claude-coding-worker/driver.d.ts +12 -0
  5. package/dist/claude-coding-worker/driver.js +29 -7
  6. package/dist/claude-coding-worker/main.js +5 -1
  7. package/dist/claude-coding-worker/sdk.js +17 -1
  8. package/dist/claude-plugin/command.d.ts +64 -0
  9. package/dist/claude-plugin/command.js +379 -0
  10. package/dist/cli-help.d.ts +1 -1
  11. package/dist/cli-help.js +2 -1
  12. package/dist/cli.js +71 -3
  13. package/dist/coding/code-host.d.ts +16 -0
  14. package/dist/coding/code-host.js +60 -0
  15. package/dist/coding/mcp-token.d.ts +12 -0
  16. package/dist/coding/mcp-token.js +26 -0
  17. package/dist/coding/protocol.js +2 -0
  18. package/dist/coding/redact.js +6 -1
  19. package/dist/coding/transcript/recorder.d.ts +5 -4
  20. package/dist/coding/transcript/recorder.js +3 -9
  21. package/dist/coding-worker/debug-trace.d.ts +3 -1
  22. package/dist/coding-worker/debug-trace.js +4 -2
  23. package/dist/coding-worker/driver.js +8 -1
  24. package/dist/coding-worker/main.js +5 -1
  25. package/dist/coding-worker/mcp-config.d.ts +27 -0
  26. package/dist/coding-worker/mcp-config.js +32 -0
  27. package/dist/coding-worker/sdk.d.ts +36 -0
  28. package/dist/coding-worker/sdk.js +28 -1
  29. package/dist/coding-worker/types.d.ts +4 -0
  30. package/dist/config/mcp-call-limit.d.ts +8 -0
  31. package/dist/config/mcp-call-limit.js +18 -0
  32. package/dist/config/mcp-servers.d.ts +8 -0
  33. package/dist/config/mcp-servers.js +12 -0
  34. package/dist/config/test-simulators.d.ts +10 -0
  35. package/dist/config/test-simulators.js +28 -0
  36. package/dist/config/tool-timeout.d.ts +1 -0
  37. package/dist/config/tool-timeout.js +18 -0
  38. package/dist/core/coding-mcp.d.ts +36 -0
  39. package/dist/core/coding-mcp.js +253 -0
  40. package/dist/core/coding-repository.d.ts +46 -0
  41. package/dist/core/coding-repository.js +92 -0
  42. package/dist/core/delegation-slots.d.ts +27 -0
  43. package/dist/core/delegation-slots.js +44 -0
  44. package/dist/core/dispatch.d.ts +6 -2
  45. package/dist/core/dispatch.js +39 -11
  46. package/dist/core/engine-native.d.ts +2 -0
  47. package/dist/core/engine-native.js +13 -0
  48. package/dist/core/error-code.js +2 -0
  49. package/dist/core/grants.d.ts +28 -3
  50. package/dist/core/grants.js +46 -4
  51. package/dist/core/host-events.js +7 -3
  52. package/dist/core/host-status.js +2 -2
  53. package/dist/core/issue-status.js +4 -3
  54. package/dist/core/listen-address.d.ts +10 -0
  55. package/dist/core/listen-address.js +14 -0
  56. package/dist/core/mark-ready.d.ts +1 -1
  57. package/dist/core/mark-ready.js +1 -1
  58. package/dist/core/mcp-client/client.d.ts +47 -0
  59. package/dist/core/mcp-client/client.js +141 -0
  60. package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
  61. package/dist/core/mcp-client/jsonrpc.js +54 -0
  62. package/dist/core/mcp-client/results.d.ts +13 -0
  63. package/dist/core/mcp-client/results.js +42 -0
  64. package/dist/core/mcp-servers.d.ts +90 -0
  65. package/dist/core/mcp-servers.js +230 -0
  66. package/dist/core/mcp-tools.d.ts +108 -0
  67. package/dist/core/mcp-tools.js +287 -0
  68. package/dist/core/merge-order-check.js +38 -19
  69. package/dist/core/notification-dispatcher.js +2 -1
  70. package/dist/core/power-idle.d.ts +8 -2
  71. package/dist/core/power-idle.js +17 -3
  72. package/dist/core/power.d.ts +5 -0
  73. package/dist/core/power.js +5 -0
  74. package/dist/core/related-pull-requests.js +26 -10
  75. package/dist/core/retention-days.d.ts +5 -0
  76. package/dist/core/retention-days.js +14 -0
  77. package/dist/core/review-fix.js +2 -2
  78. package/dist/core/review-host-tools.js +3 -1
  79. package/dist/core/run-steps/config.d.ts +25 -0
  80. package/dist/core/run-steps/config.js +40 -0
  81. package/dist/core/run-steps/control-chars.d.ts +16 -0
  82. package/dist/core/run-steps/control-chars.js +26 -0
  83. package/dist/core/run-steps/detail.d.ts +32 -0
  84. package/dist/core/run-steps/detail.js +240 -0
  85. package/dist/core/run-steps/extract.d.ts +52 -0
  86. package/dist/core/run-steps/extract.js +467 -0
  87. package/dist/core/run-steps/ingress.d.ts +51 -0
  88. package/dist/core/run-steps/ingress.js +160 -0
  89. package/dist/core/run-steps/last-step.d.ts +15 -0
  90. package/dist/core/run-steps/last-step.js +35 -0
  91. package/dist/core/run-steps/native.d.ts +48 -0
  92. package/dist/core/run-steps/native.js +164 -0
  93. package/dist/core/run-steps/seen.d.ts +16 -0
  94. package/dist/core/run-steps/seen.js +35 -0
  95. package/dist/core/run-steps/summarize.d.ts +57 -0
  96. package/dist/core/run-steps/summarize.js +304 -0
  97. package/dist/core/run-steps/sweep.d.ts +21 -0
  98. package/dist/core/run-steps/sweep.js +41 -0
  99. package/dist/core/run-steps/types.d.ts +89 -0
  100. package/dist/core/run-steps/types.js +39 -0
  101. package/dist/core/run-steps/wire.d.ts +35 -0
  102. package/dist/core/run-steps/wire.js +47 -0
  103. package/dist/core/run-steps/writer.d.ts +49 -0
  104. package/dist/core/run-steps/writer.js +279 -0
  105. package/dist/core/runner.d.ts +70 -16
  106. package/dist/core/runner.js +296 -74
  107. package/dist/core/secret-broker-config.d.ts +2 -0
  108. package/dist/core/secret-broker-config.js +1 -1
  109. package/dist/core/secret-guard.d.ts +5 -2
  110. package/dist/core/secret-guard.js +22 -7
  111. package/dist/core/secrets.d.ts +11 -1
  112. package/dist/core/secrets.js +27 -1
  113. package/dist/core/serializable-retry.d.ts +18 -0
  114. package/dist/core/serializable-retry.js +43 -0
  115. package/dist/core/tool-admin.d.ts +5 -1
  116. package/dist/core/tool-admin.js +7 -2
  117. package/dist/core/tool-names.d.ts +13 -0
  118. package/dist/core/tool-names.js +20 -0
  119. package/dist/core/webhooks.d.ts +4 -0
  120. package/dist/core/webhooks.js +68 -49
  121. package/dist/core/workflow-run-events.js +2 -1
  122. package/dist/generated/prisma/browser.d.ts +39 -0
  123. package/dist/generated/prisma/client.d.ts +39 -0
  124. package/dist/generated/prisma/commonInputTypes.d.ts +80 -50
  125. package/dist/generated/prisma/enums.d.ts +6 -0
  126. package/dist/generated/prisma/enums.js +5 -0
  127. package/dist/generated/prisma/internal/class.d.ts +77 -0
  128. package/dist/generated/prisma/internal/class.js +4 -4
  129. package/dist/generated/prisma/internal/prismaNamespace.d.ts +632 -1
  130. package/dist/generated/prisma/internal/prismaNamespace.js +94 -3
  131. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +98 -0
  132. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +94 -3
  133. package/dist/generated/prisma/models/Agent.d.ts +372 -1
  134. package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
  135. package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
  136. package/dist/generated/prisma/models/AgentRepository.d.ts +40 -1
  137. package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
  138. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +0 -7
  139. package/dist/generated/prisma/models/CodingProxySession.d.ts +159 -2
  140. package/dist/generated/prisma/models/CodingRun.d.ts +76 -1
  141. package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
  142. package/dist/generated/prisma/models/HostProvider.js +1 -0
  143. package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
  144. package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
  145. package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
  146. package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
  147. package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
  148. package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
  149. package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
  150. package/dist/generated/prisma/models/McpServer.js +1 -0
  151. package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
  152. package/dist/generated/prisma/models/Principal.d.ts +167 -0
  153. package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
  154. package/dist/generated/prisma/models/Run.d.ts +503 -1
  155. package/dist/generated/prisma/models/RunStep.d.ts +1453 -0
  156. package/dist/generated/prisma/models/RunStep.js +1 -0
  157. package/dist/generated/prisma/models/Secret.d.ts +387 -0
  158. package/dist/generated/prisma/models/Tool.d.ts +223 -1
  159. package/dist/generated/prisma/models.d.ts +7 -0
  160. package/dist/help/search.js +12 -3
  161. package/dist/help-index.json +812 -52
  162. package/dist/import/create.js +16 -1
  163. package/dist/import/neutral-schema.d.ts +1 -0
  164. package/dist/import/neutral-schema.js +2 -0
  165. package/dist/mcp/auth/access.d.ts +1 -0
  166. package/dist/mcp/auth/grants-cli.d.ts +6 -4
  167. package/dist/mcp/auth/grants-cli.js +42 -3
  168. package/dist/mcp/auth/resource-server.d.ts +3 -1
  169. package/dist/mcp/auth/resource-server.js +17 -2
  170. package/dist/mcp/context.d.ts +6 -0
  171. package/dist/mcp/index.js +21 -0
  172. package/dist/mcp/tools/agents.js +135 -20
  173. package/dist/mcp/tools/grants.js +66 -28
  174. package/dist/mcp/tools/host-providers.d.ts +2 -0
  175. package/dist/mcp/tools/host-providers.js +215 -0
  176. package/dist/mcp/tools/mcp-servers.d.ts +26 -0
  177. package/dist/mcp/tools/mcp-servers.js +481 -0
  178. package/dist/mcp/tools/power.js +2 -2
  179. package/dist/mcp/tools/repositories.js +50 -10
  180. package/dist/mcp/tools/run-access.d.ts +7 -0
  181. package/dist/mcp/tools/run-access.js +14 -0
  182. package/dist/mcp/tools/run-steps.d.ts +16 -0
  183. package/dist/mcp/tools/run-steps.js +85 -0
  184. package/dist/mcp/tools/runs.js +186 -18
  185. package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
  186. package/dist/mcp/tools/secret-elicitation.js +25 -2
  187. package/dist/mcp/tools/secrets.js +38 -4
  188. package/dist/mcp/tools/tools.d.ts +6 -4
  189. package/dist/mcp/tools/tools.js +40 -8
  190. package/dist/mcp/tools/trigger.js +28 -6
  191. package/dist/mcp/transport/http-limits.d.ts +2 -0
  192. package/dist/mcp/transport/http-limits.js +6 -2
  193. package/dist/mcp/webhooks/ingress.js +10 -0
  194. package/dist/native-worker/gateway-server.d.ts +18 -1
  195. package/dist/native-worker/gateway-server.js +38 -9
  196. package/dist/native-worker/gateway.d.ts +5 -0
  197. package/dist/native-worker/gateway.js +13 -1
  198. package/dist/native-worker/http-server.d.ts +6 -4
  199. package/dist/native-worker/http-server.js +53 -8
  200. package/dist/native-worker/ledger.d.ts +6 -2
  201. package/dist/native-worker/ledger.js +5 -4
  202. package/dist/native-worker/protocol.d.ts +1 -0
  203. package/dist/native-worker/protocol.js +4 -1
  204. package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
  205. package/dist/providers/coding-proxy/capability-hash.js +5 -0
  206. package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
  207. package/dist/providers/coding-proxy/mcp-route.js +166 -0
  208. package/dist/providers/coding-proxy/memory-ledger.d.ts +2 -0
  209. package/dist/providers/coding-proxy/memory-ledger.js +16 -0
  210. package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
  211. package/dist/providers/coding-proxy/prisma-ledger.js +36 -2
  212. package/dist/providers/coding-proxy/proxy.d.ts +33 -1
  213. package/dist/providers/coding-proxy/proxy.js +266 -75
  214. package/dist/providers/coding-proxy/run-step-forwarder.d.ts +42 -0
  215. package/dist/providers/coding-proxy/run-step-forwarder.js +206 -0
  216. package/dist/providers/coding-proxy/runtime.d.ts +13 -0
  217. package/dist/providers/coding-proxy/runtime.js +87 -4
  218. package/dist/providers/coding-proxy/server.d.ts +3 -0
  219. package/dist/providers/coding-proxy/server.js +10 -0
  220. package/dist/providers/coding-proxy/types.d.ts +18 -0
  221. package/dist/providers/engine/types.d.ts +26 -0
  222. package/dist/providers/executor/composition.js +5 -0
  223. package/dist/providers/executor/container.d.ts +47 -1
  224. package/dist/providers/executor/container.js +84 -8
  225. package/dist/providers/host-tools/invoke.d.ts +37 -0
  226. package/dist/providers/host-tools/invoke.js +117 -0
  227. package/dist/providers/host-tools/operations.d.ts +65 -0
  228. package/dist/providers/host-tools/operations.js +149 -0
  229. package/dist/providers/host-tools/registry.d.ts +79 -0
  230. package/dist/providers/host-tools/registry.js +362 -0
  231. package/dist/providers/host-tools/request-guard.d.ts +17 -0
  232. package/dist/providers/host-tools/request-guard.js +99 -0
  233. package/dist/providers/host-tools/self-test.d.ts +64 -0
  234. package/dist/providers/host-tools/self-test.js +142 -0
  235. package/dist/providers/jobs/docker-isolation.d.ts +2 -2
  236. package/dist/providers/jobs/docker-isolation.js +30 -8
  237. package/dist/providers/jobs/docker.d.ts +8 -0
  238. package/dist/providers/jobs/docker.js +33 -9
  239. package/dist/providers/jobs/kubernetes-isolation.d.ts +3 -1
  240. package/dist/providers/jobs/kubernetes-isolation.js +15 -2
  241. package/dist/providers/jobs/kubernetes.d.ts +13 -0
  242. package/dist/providers/jobs/kubernetes.js +28 -5
  243. package/dist/providers/jobs/types.d.ts +6 -0
  244. package/dist/providers/review-host/types.d.ts +6 -4
  245. package/dist/providers/review-host/types.js +1 -1
  246. package/dist/providers/vcs/git.d.ts +6 -2
  247. package/dist/providers/vcs/git.js +15 -4
  248. package/dist/providers/vcs/github.d.ts +2 -71
  249. package/dist/providers/vcs/github.js +8 -11
  250. package/dist/providers/vcs/index.d.ts +1 -1
  251. package/dist/providers/vcs/index.js +1 -1
  252. package/dist/providers/vcs/remote.d.ts +4 -0
  253. package/dist/providers/vcs/remote.js +7 -1
  254. package/dist/providers/vcs/routing.d.ts +12 -6
  255. package/dist/providers/vcs/routing.js +21 -6
  256. package/dist/providers/vcs/types.d.ts +71 -3
  257. package/dist/quickstart/index.d.ts +5 -0
  258. package/dist/quickstart/index.js +21 -0
  259. package/dist/quickstart-images.json +1 -1
  260. package/dist/sandbox/eval-core.d.ts +12 -2
  261. package/dist/sandbox/eval-core.js +20 -4
  262. package/dist/sandbox/host-functions.d.ts +4 -0
  263. package/dist/sandbox/host-functions.js +7 -2
  264. package/dist/sandbox/limits.d.ts +4 -1
  265. package/dist/sandbox/limits.js +4 -1
  266. package/dist/sandbox/run-in-sandbox.d.ts +6 -0
  267. package/dist/sandbox/run-in-sandbox.js +14 -2
  268. package/dist/sandbox/safe-fetch.d.ts +2 -0
  269. package/dist/sandbox/safe-fetch.js +1 -1
  270. package/dist/sandbox/secret-broker.d.ts +1 -0
  271. package/dist/sandbox/secret-broker.js +1 -1
  272. package/dist/sandbox/tool-timeout.d.ts +32 -0
  273. package/dist/sandbox/tool-timeout.js +52 -0
  274. package/dist/sandbox/user-tool.d.ts +8 -1
  275. package/dist/sandbox/user-tool.js +9 -3
  276. package/dist/serve.js +26 -18
  277. package/dist/viewer/api-schema.d.ts +66 -0
  278. package/dist/viewer/api-schema.js +31 -0
  279. package/dist/viewer/graph.js +2 -1
  280. package/dist/viewer/openapi.js +2 -1
  281. package/dist/viewer/run-detail.d.ts +2 -0
  282. package/dist/viewer/run-detail.js +26 -1
  283. package/dist/wardby-bin.js +5 -0
  284. package/docs/README.md +11 -1
  285. package/docs/agent-recipes.md +37 -4
  286. package/docs/architecture-runtime.md +1 -1
  287. package/docs/claude-code-plugin.md +208 -0
  288. package/docs/code-review-agents.md +6 -0
  289. package/docs/coding-agent-setup.md +94 -1
  290. package/docs/coding-worker-byo-images.md +4 -2
  291. package/docs/coding-worker-isolation.md +94 -7
  292. package/docs/debugging-agent-runs.md +354 -0
  293. package/docs/getting-started-gke.md +33 -11
  294. package/docs/getting-started-identity-provider.md +43 -37
  295. package/docs/getting-started.md +8 -0
  296. package/docs/host-providers.md +294 -0
  297. package/docs/mcp-servers.md +263 -0
  298. package/docs/native-sandbox.md +31 -9
  299. package/docs/security-deployment.md +161 -31
  300. package/docs/tool-secrets.md +3 -0
  301. package/docs/viewer-api.md +27 -12
  302. package/docs/viewer-api.openapi.yml +78 -2
  303. package/help/admin-viewer.md +5 -0
  304. package/help/agent-recipes.md +33 -2
  305. package/help/brokered-secrets.md +3 -0
  306. package/help/build-claude-code-image.md +1 -1
  307. package/help/claude-code-run-pane.md +63 -0
  308. package/help/deploy-gke.md +5 -0
  309. package/help/errors/coding-mcp-gateway-unavailable.md +46 -0
  310. package/help/errors/coding-mcp-pins-unavailable.md +35 -0
  311. package/help/errors/database-auth-invalid.md +1 -1
  312. package/help/errors/host-provider-error.md +40 -0
  313. package/help/errors/host-tool-failed.md +36 -0
  314. package/help/errors/mcp-call-limit.md +22 -0
  315. package/help/errors/mcp-call-timeout.md +22 -0
  316. package/help/errors/mcp-server-auth-failed.md +22 -0
  317. package/help/errors/mcp-server-credentials-unavailable.md +45 -0
  318. package/help/errors/mcp-server-error.md +28 -0
  319. package/help/errors/mcp-server-unreachable.md +22 -0
  320. package/help/errors/mcp-servers-off.md +21 -0
  321. package/help/errors/mcp-tool-not-allowed.md +26 -0
  322. package/help/errors/mcp-tool-removed.md +22 -0
  323. package/help/errors/mcp-unavailable.md +25 -0
  324. package/help/errors/native-sandbox-capacity.md +1 -1
  325. package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
  326. package/help/errors/native-sandbox-docker-failed.md +1 -1
  327. package/help/errors/native-sandbox-gateway-error.md +1 -1
  328. package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
  329. package/help/errors/native-sandbox-image-not-pinned.md +1 -1
  330. package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
  331. package/help/errors/native-sandbox-network-unenforced.md +1 -1
  332. package/help/errors/native-sandbox-requires-catalog.md +1 -1
  333. package/help/errors/native-sandbox-unavailable.md +1 -1
  334. package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
  335. package/help/errors/native-sandbox-worker-exited.md +1 -1
  336. package/help/errors/native-sandbox-worker-lost.md +1 -1
  337. package/help/errors/native-sandbox-worker-unready.md +1 -1
  338. package/help/errors/repository-not-listed.md +36 -0
  339. package/help/errors/repository-required.md +36 -0
  340. package/help/errors/tool-timeout.md +38 -0
  341. package/help/host-providers.md +40 -0
  342. package/help/identity-and-access.md +4 -1
  343. package/help/low-power-mode.md +19 -8
  344. package/help/mcp-servers.md +73 -0
  345. package/help/mcp.md +9 -2
  346. package/help/native-capabilities.md +27 -1
  347. package/help/native-sandbox.md +1 -1
  348. package/help/operating-agents.md +16 -0
  349. package/help/related-pull-requests.md +7 -3
  350. package/help/repo-instructions-and-skills.md +3 -1
  351. package/help/resume-runs-locally.md +7 -0
  352. package/help/run-steps.md +140 -0
  353. package/help/stop-run.md +1 -1
  354. package/help/troubleshooting/coding-workers.md +9 -1
  355. package/help/troubleshooting/debugging-agent-runs.md +29 -0
  356. package/package.json +2 -1
  357. package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
  358. package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
  359. package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
  360. package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
  361. package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
  362. package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
  363. package/prisma/migrations/20261110000000_run_steps/migration.sql +26 -0
  364. package/prisma/migrations/20261111000000_run_steps_viewer_notify/migration.sql +59 -0
  365. package/prisma/migrations/20261112000000_run_steps_detail/migration.sql +9 -0
  366. package/prisma/schema.prisma +211 -14
  367. package/scripts/git-askpass.sh +1 -1
@@ -0,0 +1,294 @@
1
+ # Host providers
2
+
3
+ A host provider adds a code host to Wardby using your own custom tools. You can add
4
+ a host, or fix one, without a Wardby release.
5
+
6
+ Host providers are being added in stages. **This release (0.8.0) lets you register a
7
+ provider, approve its tools and test them. Agents can't use a registered provider yet**,
8
+ and `link_repository` still refuses its repositories.
9
+
10
+ ## Who can manage one
11
+
12
+ Every host provider tool (`register_host_provider`, `approve_host_provider_tools`,
13
+ `get_host_provider`, `test_host_provider`, `delete_host_provider`) needs the
14
+ `hosts:admin` scope and a role that grants it (`admin` or `host-manager`), or the local
15
+ stdio operator. Bound tools decide who has permission and who wrote a comment, so treat
16
+ this like `agents:admin`. See
17
+ [getting-started-identity-provider.md](getting-started-identity-provider.md#wardby-roles)
18
+ for defining the scope and mapping the role.
19
+
20
+ Providers are shared by every host manager: any host manager can read and re-register any
21
+ provider, including its tool code, whoever owns the tools it names.
22
+
23
+ ## Register a provider
24
+
25
+ 1. Create a tool for each operation with `create_tool`. Each does one host call. See
26
+ [Tool contract](#tool-contract).
27
+ 2. Store each credential with `create_secret`, and broker it with `set_secret_broker`
28
+ ([tool-secrets.md](tool-secrets.md)). Allow every API host and place it in a header or
29
+ query parameter. Repository credentials also allow the git host; the permission
30
+ credential needs only the API hosts.
31
+ 3. Store a webhook secret of at least 16 characters. Don't attach it to any agent.
32
+ 4. Call `register_host_provider`:
33
+
34
+ ```json
35
+ {
36
+ "provider": "your-host",
37
+ "gitHost": "git.example.com",
38
+ "apiHosts": ["api.example.com"],
39
+ "gitUsername": "x-token-auth",
40
+ "pullRequestUrlTemplate": "https://git.example.com/{repository}/pull/{number}",
41
+ "repositoryApiPath": "/2.0/repositories/{repository}",
42
+ "webhook": {
43
+ "header": "X-Hub-Signature",
44
+ "algorithm": "sha256",
45
+ "deliveryHeader": "X-Request-UUID",
46
+ "eventHeader": "X-Event-Key",
47
+ "secret": "your-webhook-secret"
48
+ },
49
+ "credentials": [{ "repositories": "your-workspace/*", "secret": "your-token" }],
50
+ "trustedAccounts": ["<account id>"],
51
+ "tools": { "getPullRequest": "your_get_pr_tool" }
52
+ }
53
+ ```
54
+
55
+ Bind every required operation in `tools` (names here are placeholders), and any optional
56
+ ones you want. For example:
57
+
58
+ ```json
59
+ "tools": {
60
+ "getPullRequest": "your_get_pr", "listPullRequestFiles": "your_list_files",
61
+ "getDiff": "your_get_diff", "readFile": "your_read_file", "listFiles": "your_list_dir",
62
+ "listComments": "your_list_comments", "createComment": "your_create_comment",
63
+ "submitVerdict": "your_submit_verdict", "findPullRequest": "your_find_pr",
64
+ "openPullRequest": "your_open_pr", "updatePullRequest": "your_update_pr"
65
+ }
66
+ ```
67
+
68
+ 5. No restart is needed in this release, because agents can't use a provider yet. Once they
69
+ can, a new provider will need a server restart; approvals won't.
70
+
71
+ The tools and secrets you name must be yours. Secret rules, each refused with a named code:
72
+
73
+ - Credentials and `permissionCredential` must be brokered, placed in a header or query
74
+ parameter, and allowed on the API hosts (credentials also on the git host; the permission credential needs only the API hosts).
75
+ - `permissionCredential` can't also be listed in `credentials`
76
+ (`host_provider_permission_secret_shared`), so no other operation can reach it.
77
+ - The webhook secret can't be a credential or permission secret of any provider, and can't
78
+ be attached to an agent (`host_provider_webhook_secret_shared`). `attach_secret` refuses
79
+ it too.
80
+ - The webhook secret needs at least 16 characters (`host_provider_webhook_secret_short`).
81
+ - A permission secret can't be a credential or permission secret of any other provider, and a
82
+ credential can't be another provider's permission secret
83
+ (`host_provider_permission_secret_shared`).
84
+ - `gitUsername` can't be a credential's value and can't contain "username".
85
+ - `pullRequestUrlTemplate` is an https URL on `gitHost`, with no username, password or port.
86
+
87
+ Keep provider credentials unattached. A brokered credential attached to one of its owner's agents
88
+ can be used by that agent's tools without the host provider path guard, which only applies to a
89
+ provider's bound tools.
90
+
91
+ See [the refusal codes](../help/errors/host-provider-error.md) for what each one means.
92
+
93
+ These provider names can't be used: `github`, `local`, `http`, `https`, `ssh`, `git`,
94
+ `file` and `constructor`. Names match `^[a-z][a-z0-9-]{1,30}$`.
95
+
96
+ These operations are required: `getPullRequest`, `listPullRequestFiles`, `getDiff`,
97
+ `readFile`, `listFiles`, `listComments`, `createComment`, `submitVerdict`,
98
+ `findPullRequest`, `openPullRequest` and `updatePullRequest`. These are optional:
99
+ `editComment`, `resolveThread`, `listCommitStatuses`, `setCommitStatus`,
100
+ `getRepositoryPermission` and `normalizeEvent`. `getRepositoryPermission` becomes required
101
+ when you set `permissionCredential`.
102
+
103
+ ## Who may start a run
104
+
105
+ This is how it will work once agents can use a provider. Registering records your choice
106
+ now; nothing enforces it in this release.
107
+
108
+ | Setup | Situation | Result |
109
+ | ------------------------- | ---------------------------------------- | --------------------------------- |
110
+ | `permissionCredential` | The host says the user has enough access | Run starts |
111
+ | `permissionCredential` | The host says they don't | Refused, even if in the allowlist |
112
+ | `permissionCredential` | The lookup fails | Refused, with no fallback |
113
+ | No `permissionCredential` | The user is in `trustedAccounts` | Run starts, at `write` level |
114
+ | No `permissionCredential` | The user isn't in `trustedAccounts` | Refused |
115
+
116
+ `permissionCredential` needs `permissionApiPath` (with `{workspace}` and `{repo}`) and a
117
+ bound `getRepositoryPermission`.
118
+
119
+ ## Change a tool
120
+
121
+ Registering copies each tool's code, schema and `timeoutMs` (its time limit), and only that
122
+ copy ever runs.
123
+ `update_tool` changes nothing until you approve it, so the last approved version keeps
124
+ working in the meantime.
125
+
126
+ 1. Call `update_tool`.
127
+ 2. Call `get_host_provider` with `includeCode: true`. Changed tools show `status: "changed"`.
128
+ Each operation has `approvedCode` and `approvedParamsZod`, and a changed one also has
129
+ `currentCode` and `currentParamsZod`, because a change can be to the schema alone. Every
130
+ operation also shows `approvedTimeoutMs` and `currentTimeoutMs` (`null` is the default
131
+ 10 seconds), since a change can be to the time limit alone.
132
+ 3. Read the change, then call `approve_host_provider_tools` with the operation names. It
133
+ applies from the next call.
134
+
135
+ Deleting a tool keeps its snapshot running, with `status: "tool_deleted"`. To change that
136
+ operation, bind a new tool by registering again. Registering again also approves every tool
137
+ at its current code.
138
+
139
+ ## Rotate a secret
140
+
141
+ A secret a provider uses is replaced with `create_secret` under the same name, whether it's a
142
+ credential, the permission credential or the webhook secret. Change its broker config with
143
+ `set_secret_broker`. Both need `hosts:admin` with a role that grants it, as well as
144
+ `secrets:write`, and refuse before handing out a browser link. Without brokering a credential
145
+ stops working, so `set_secret_broker` with `broker: null` also breaks the provider.
146
+
147
+ Only the secret's owner can rotate it this way, because secret names belong to their owner. If
148
+ another host manager calls `create_secret` with the same name, they get a separate secret and the
149
+ provider keeps the old value. They must register the provider again with their own secret instead.
150
+
151
+ The new webhook value needs at least 16 characters, whether you pass it directly or enter it
152
+ in the browser (`host_provider_webhook_secret_short`). A browser link requested before the secret
153
+ joined a provider is refused (`host_provider_secret_link_stale`); request a new one.
154
+
155
+ Rotating needs no re-registration: the next call uses the new value. Update the webhook secret on
156
+ the host at the same time.
157
+
158
+ ## Test a provider
159
+
160
+ `test_host_provider` runs each approved read-only tool against one repository, using the
161
+ real credential:
162
+
163
+ ```json
164
+ {
165
+ "provider": "your-host",
166
+ "repository": "your-host:your-workspace/your-repo",
167
+ "pullRequest": 4,
168
+ "headRef": "a-branch-with-a-pull-request",
169
+ "ref": "a-commit-or-branch",
170
+ "accountId": "<account id>"
171
+ }
172
+ ```
173
+
174
+ Only `provider` and `repository` are required. Each other field enables the checks that need it:
175
+
176
+ | Field | Checks it enables |
177
+ | ------------- | ----------------------------------------------------------------------------------- |
178
+ | `pullRequest` | `getPullRequest`, `listPullRequestFiles`, `getDiff`, `listComments` |
179
+ | `headRef` | `findPullRequest` |
180
+ | `ref` | `readFile`, `listFiles`, `listCommitStatuses` (defaults to the pull request's head) |
181
+ | `accountId` | `getRepositoryPermission` |
182
+
183
+ Each result is an `outcome` of `pass`, `fail` (with a fixed code, below) or `skipped`. A skip has
184
+ one of these codes: `not_bound` (no tool bound), `needs_pull_request`, `needs_head_ref`,
185
+ `needs_ref`, `needs_account` (the field above is missing), `allowlist_mode` (no
186
+ `permissionCredential`, so no permission lookup) and `not_read_only` (a write operation, never run).
187
+
188
+ Write operations are skipped. A read-only tool can only send GET requests. The report holds
189
+ an operation, an outcome and a fixed code for each, and no response data, so you can share it.
190
+
191
+ | Code | Meaning |
192
+ | --------------------------------------- | ---------------------------------------------------------------- |
193
+ | `host_unauthorized`, `host_forbidden` | The host refused the credential for this call. |
194
+ | `host_not_found` | The host doesn't have what was asked for. |
195
+ | `host_rate_limited`, `host_unavailable` | Try again later. |
196
+ | `host_conflict` | The host refused because of the current state. |
197
+ | `host_tool_invalid_result` | The tool's answer doesn't match its operation's shape. |
198
+ | `host_tool_invalid_params` | The tool's schema refused what Wardby sends it. |
199
+ | `host_tool_request_denied` | The tool went outside what a bound tool may request (see below). |
200
+ | `host_tool_no_credential` | See below. |
201
+ | `host_tool_failed` | The tool threw an error. Its message is dropped on purpose. |
202
+ | `host_tool_timeout` | The tool ran out of time or instructions. |
203
+
204
+ `host_tool_no_credential` has these causes: no credential's `repositories` covers this repository,
205
+ the secret can't be decrypted, or the secret is no longer brokered or no longer allows every API
206
+ host.
207
+
208
+ ## Tool contract
209
+
210
+ Each tool receives `workspace`, `repo` and `repository` (`workspace/repo`), plus its
211
+ operation's input fields (below). Declare them in `paramsZod`.
212
+
213
+ A tool answers with its operation's result, or `{ "error": "<code>" }` when the host refused,
214
+ where the code is `unauthorized`, `forbidden`, `not_found`, `conflict`, `rate_limited` or
215
+ `unavailable`. Results are checked strictly: an extra key, a missing key or a wrong type fails the
216
+ call with `host_tool_invalid_result`.
217
+
218
+ A pull request object, as returned by `getPullRequest` and `findPullRequest`, has exactly:
219
+ `number` (positive integer), `title`, `body`, `author` (string or null), `state` (`open` or
220
+ `closed`; a merged pull request is `closed` with `merged` true, so map your host's own states),
221
+ `merged` (boolean), `draft` (boolean), `baseRef`, `headRef`, `headSha` (non-empty), `isFork` (boolean) and
222
+ `htmlUrl` (a URL).
223
+
224
+ Read operations (method GET):
225
+
226
+ | Operation | Input fields | Result |
227
+ | ------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
228
+ | `getPullRequest` | `number` | A pull request object. |
229
+ | `findPullRequest` | `headRef` | `{ pullRequest }`: a pull request object, or null if none. |
230
+ | `listPullRequestFiles` | `number`, `page` | `{ files, nextPage }`. Each file: `filename`, `status` (`added`, `modified`, `removed` or `renamed`), `additions`, `deletions` (non-negative integers) and optionally `previousFilename`. |
231
+ | `getDiff` | `number` | `{ diff }`: the unified diff text. |
232
+ | `readFile` | `ref`, `path` | `{ kind: "file", content }`, `{ kind: "directory" }` or `{ kind: "not_found" }`. No other keys. |
233
+ | `listFiles` | `ref`, `path`, `page` | `{ entries, nextPage }`. Each entry: `path`, `type` (`file` or `directory`) and optionally `size` (non-negative integer). |
234
+ | `listComments` | `number`, `page` | `{ comments, nextPage }`. Each comment: `id` (non-empty), `author` and `authorId` (string or null), `body`, `createdAt`, `path` (string or null), `line` (positive integer or null), `parentId` (string or null), `resolved` (boolean). |
235
+ | `listCommitStatuses` | `ref`, `page` | `{ statuses, nextPage }`. Each status: `key`, `state` (`pending`, `success`, `failure` or `stopped`) and `url` (string or null). |
236
+ | `getRepositoryPermission` | `accountId` | `{ permission }`: `none`, `read`, `triage`, `write`, `maintain` or `admin`. Uses the permission credential and `permissionApiPath`. |
237
+
238
+ `listPullRequestFiles`, `listFiles`, `listComments` and `listCommitStatuses` page. Each returns
239
+ one page per call, with `nextPage`: a string to continue from, or null at the end. To get the next
240
+ page, Wardby passes that string back as the optional `page` input, and it leaves `page` out for the
241
+ first page. Declare `page` in `paramsZod`, because `z.object` drops keys it doesn't name, and a tool
242
+ without it would return the first page every time. `test_host_provider` reads the first page only.
243
+
244
+ `ref` is optional on `readFile` and `listFiles`, where leaving it out means the default branch, so
245
+ declare it as optional there. `listCommitStatuses` always needs one, because statuses belong to a
246
+ commit.
247
+
248
+ Keep a file's `status` to those four words: Wardby treats a `removed` file as having nothing to read
249
+ at the head.
250
+
251
+ Write operations:
252
+
253
+ | Operation | Method | Result |
254
+ | ------------------- | ------ | ---------------------------- |
255
+ | `openPullRequest` | POST | A pull request object. |
256
+ | `updatePullRequest` | PUT | A pull request object. |
257
+ | `createComment` | POST | `{ id }` (non-empty string). |
258
+ | `editComment` | PUT | `{ id }` (non-empty string). |
259
+ | `resolveThread` | POST | `{}` |
260
+ | `submitVerdict` | POST | `{}` |
261
+ | `setCommitStatus` | POST | `{}` |
262
+
263
+ The input fields of the write operations are defined when agents can use a provider, in a later
264
+ release, so tools for them may need changing then. `test_host_provider` never runs them.
265
+
266
+ `normalizeEvent` makes no requests and isn't called yet.
267
+
268
+ Use `fetch(url, { secrets: ["host"] })`. Wardby resolves `host` to the credential for this
269
+ call's repository. A bound tool:
270
+
271
+ - reaches only `apiHosts`, over https on the default port, with no embedded credentials;
272
+ - stays under `repositoryApiPath` for this repository. `getRepositoryPermission` stays under
273
+ `permissionApiPath` instead, and is the only operation that gets `permissionCredential`;
274
+ - uses its operation's method: GET for reads, and POST or PUT for writes;
275
+ - may set only the headers `accept`, `content-type`, `if-match` and `if-none-match`;
276
+ - makes at most three requests per call. No request follows a redirect, whether or not it
277
+ carries a secret: follow a `Location` yourself, which counts as a request and goes through
278
+ the same limits;
279
+ - can't send the query keys `_method` or any starting `x-http-method`, in any case, which some
280
+ frameworks read as the request's method;
281
+ - has its path judged by what a server could turn it into: dot-only segments after decoding
282
+ and encoded separators are refused. A few unusual file names can therefore be refused, and a
283
+ repository path must be sent literally, not percent-encoded;
284
+ - has no datastore, shared datastore or readable secrets, and its `console` output is dropped;
285
+ - runs under the usual sandbox limits: 10 s per call, 8 s per fetch, 8 MiB per response.
286
+
287
+ Anything outside these limits fails the call with `host_tool_request_denied`, even if the
288
+ tool catches the error.
289
+
290
+ ## Remove a provider
291
+
292
+ `delete_host_provider` removes a provider and its snapshots. It's refused while an agent links
293
+ to one of its repositories. `delete_secret` is refused while a provider uses that secret, and
294
+ names the provider.
@@ -0,0 +1,263 @@
1
+ # Remote MCP servers for native and coding agents
2
+
3
+ A native, Claude Code or Codex agent can call the tools of a remote MCP server. You register the
4
+ server once (its HTTPS URL plus your brokered secrets), Wardby saves the
5
+ server's tool list, and you attach an allowlisted subset of those tools to
6
+ an agent. A native agent sees each tool as `<server name>__<tool name>`; coding agents see
7
+ them as described in [Coding agents](#coding-agents).
8
+
9
+ `WARDBY_MCP_SERVERS` decides who may register servers: `owners` (the
10
+ default, anyone), `admins` (registering, updating and refreshing need
11
+ `hosts:admin`) or `off` (every tool below is refused except
12
+ `detach_mcp_server` and `delete_mcp_server`, so cleanup stays possible, and
13
+ attached servers' tools are left out of runs).
14
+
15
+ The tools below need the `tools:write` scope to change servers and
16
+ attachments and `agents:read` to list and get them.
17
+
18
+ ## Tools
19
+
20
+ | Tool | Who | What it does |
21
+ | --------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------ |
22
+ | `register_mcp_server` | anyone (see mode) | Registers a server from `name`, `url` (e.g. `https://mcp.example.com/mcp`) and `secretNames`; saves its tool list. |
23
+ | `update_mcp_server` | the server's owner | Changes the URL or secret names, saves a fresh tool list and returns what changed. |
24
+ | `refresh_mcp_server` | the server's owner | Saves a fresh tool list straight away; returns added, removed and changed tools and the agents left missing tools. |
25
+ | `list_mcp_servers` | anyone | Your servers and those shared with you: id, name, host, tool count, saved-at, `yours`. |
26
+ | `get_mcp_server` | owner, or `read`+ | One server with its full saved tool list (secret names and URL only for the owner). |
27
+ | `delete_mcp_server` | the server's owner | Deletes a server and detaches it from every agent. Works when the mode is `off`. |
28
+ | `attach_mcp_server` | agent's owner; `use`+ | Lets a native or coding agent call the named tools (`allowedTools`, exact names) with an optional `callTimeoutMs`. |
29
+ | `detach_mcp_server` | the agent's owner | Removes a server's tools from an agent. Works when the mode is `off`. |
30
+
31
+ `get_agent` shows each attached server under `mcpServers`: the server's `serverId`
32
+ and name, the allowed tools, `callTimeoutMs`, the exposed tool names and any allowed tools the server no
33
+ longer lists (`missingTools`). Anyone with read access on the agent sees this.
34
+ It never includes URLs, secret names or values, or saved tool lists.
35
+
36
+ ## Create a secret for the server
37
+
38
+ Servers are authenticated with [brokered secrets](tool-secrets.md): Wardby adds
39
+ the value to every request to the server's host and nobody, including the
40
+ model, can read it back. Create the secret, then set its broker config with
41
+ `set_secret_broker` so it is limited to the server's host. For a bearer token:
42
+
43
+ ```json
44
+ {
45
+ "hosts": ["mcp.example.com"],
46
+ "placement": { "kind": "header", "name": "Authorization", "format": "Bearer {value}" }
47
+ }
48
+ ```
49
+
50
+ A server that wants two headers needs two secrets, each with its own
51
+ placement (for example `X-Api-Key` with `format: "{value}"`, and
52
+ `X-Workspace` the same way); name both in `secretNames` (at most 8 names, each up to 1024 characters). The URL itself can't
53
+ carry credentials: a username, password or fragment is refused.
54
+
55
+ ## Register, read the tool list, attach
56
+
57
+ 1. `register_mcp_server` with `name` (1-32 characters: lower-case letters,
58
+ digits, `_` or `-`), `url` (`https://mcp.example.com/mcp`: HTTPS only,
59
+ default port) and `secretNames`. Wardby calls the server's `tools/list`
60
+ and saves the result. A secret that is missing, not brokered, or not
61
+ brokered to the URL's host is refused.
62
+ 2. `get_mcp_server` shows the saved tools: name, description, input schema and
63
+ the server's annotations.
64
+ 3. `attach_mcp_server` with the agent, the server and `allowedTools`: exact
65
+ tool names from the saved list, at least one, no wildcards. Attaching
66
+ replaces the agent's earlier allowlist and time limit for that server.
67
+ Native, Claude Code and Codex agents can attach a server (see [Coding agents](#coding-agents)).
68
+
69
+ The agent sees each tool as `<server name>__<tool name>`, with any character
70
+ outside letters, digits, `_` and `-` changed to `_`; the result must be at
71
+ most 64 characters. The description starts with `From MCP server <server name>: `.
72
+ Attaching is refused if the name clashes with another tool on the agent
73
+ (attached, another MCP tool, or a built-in name).
74
+
75
+ ## Coding agents
76
+
77
+ A Claude Code or Codex agent can use attached servers too. Attach them the same way, with
78
+ `attach_mcp_server`. The tools reach the run like this:
79
+
80
+ 1. The worker calls the coding proxy's `/mcp` route with a per-run MCP token (`rmt_…`, in the
81
+ worker's `WARDBY_MCP_TOKEN`). Workers still reach only the proxy.
82
+ 2. The proxy forwards the request to the native gateway's `/coding-mcp` route. The proxy never sees
83
+ a server's URL or secrets. The gateway checks the token again, checks the live attachment and
84
+ runs the call, and removes secret values from the result before it leaves.
85
+
86
+ The token belongs to one run: it works only on `/mcp`, only while the run is active and before its
87
+ deadline, and only its hash is stored.
88
+
89
+ - **Claude Code agents** see each tool as `mcp__wardby_mcp__<server name>__<tool name>` and may use
90
+ any allowed tool. The 64-character limit on `<server name>__<tool name>` is the same as for
91
+ native agents; the prefix adds no other limit.
92
+ - **Codex agents may use only tools the server marks `readOnlyHint: true`.** Wardby enforces this
93
+ when you attach (an allowlist with another tool is refused), when a run loads its tools (a tool
94
+ whose saved annotations no longer say read-only, for example after `refresh_mcp_server`, is left
95
+ out) and on every call. The reason is that repository code running in a Codex run can read the
96
+ run's MCP token, so only tools that don't change anything are safe to give it. `readOnlyHint` is
97
+ the server's own claim, which Wardby can't verify. The allowlist stays your control: allow only
98
+ tools you trust to be read-only.
99
+
100
+ A run follows the provider it was started with, even if the agent's provider changes before the
101
+ run launches. A coding agent with no coding profile is treated as Codex when you attach.
102
+
103
+ - **Switching an agent to Codex.** When `update_agent` makes an agent a Codex agent (changing
104
+ `codingProfile.provider` to `codex`, or making a native agent a coding agent, which uses Codex by
105
+ default) and some of its attached tools aren't marked read-only, the change still goes ahead and
106
+ the result's `warnings` names those tools: "These MCP tools aren't read-only and won't be
107
+ available to Codex runs: …". They stay attached but Codex runs don't get them. Detach them, or
108
+ re-attach the server with only read-only tools, to remove the warning's cause.
109
+ - **Call limit.** A run may make `WARDBY_MCP_MAX_CALLS_PER_RUN` calls (default `100`). Every call
110
+ counts, whatever its outcome. Past the limit a call returns
111
+ [`mcp_call_limit`](../help/errors/mcp-call-limit.md); the run goes on.
112
+ - **Proxy setting.** The coding proxy needs `WARDBY_MCP_GATEWAY_URL`, the native gateway's base URL
113
+ (the kind and GKE manifests set it). Without it `/mcp` answers `503` with
114
+ [`mcp_unavailable`](../help/errors/mcp-unavailable.md) and coding agents get no MCP tools. See
115
+ [Remote MCP servers](security-deployment.md#remote-mcp-servers) for the variables.
116
+ - **Low-power mode (0.9.0 and later).** With `WARDBY_LOW_POWER=1`, a coding run with MCP tools wakes the native
117
+ gateway as well as the coding proxy before its worker launches (both at once), and the gateway
118
+ stays awake while any such run is pending or running, or finished within the idle window. Those
119
+ runs appear in the gateway's `blockers` in `power_status`, and `power_sleep` refuses to sleep the
120
+ gateway while they are pending or running. If the gateway doesn't wake in time the run fails before
121
+ launch with
122
+ [`coding_failure_mcp_gateway_unavailable`](../help/errors/coding-mcp-gateway-unavailable.md),
123
+ rather than start without its MCP tools. Runs without MCP tools don't wake the gateway. See
124
+ [Low-power mode](getting-started-gke.md#low-power-mode).
125
+ - **Worker images.** MCP for coding agents needs this release's Claude worker image and a new Codex
126
+ driver image. An older image ignores the token and the agent gets no MCP tools; the run does not
127
+ fail. Update the images together with the server.
128
+ - **Repository configuration.** A repository's own MCP configuration (such as `.mcp.json`) is still
129
+ never loaded; the attached servers are the only MCP tools a coding run has.
130
+ - **Codex models that use tool search** load MCP tools on demand: Codex sends a client-side
131
+ `tool_search` tool and adds the matching MCP tools to the conversation when the model searches.
132
+ This needs this release's coding proxy; an older proxy refuses those models' requests with
133
+ `openai_tool_not_allowed:tool_search`, MCP or not.
134
+
135
+ Time limits and the other rules in this guide apply to coding agents too. See
136
+ [Coding worker isolation](coding-worker-isolation.md#remote-mcp-tools) for the security notes.
137
+
138
+ ## Time limits
139
+
140
+ Each call has `callTimeoutMs` (default `30000`, set on `attach_mcp_server`).
141
+ It must be at least `1000` and can't exceed `WARDBY_TOOL_MAX_TIMEOUT_MS`; a call runs with the lower of
142
+ the two, and never past the run's own deadline. `stop_run` aborts a call in
143
+ flight. A call that runs out of time returns `mcp_call_timeout` to the model
144
+ and the run goes on.
145
+
146
+ A coding agent's calls have one more limit: the coding proxy gives up on a forwarded call after 5
147
+ minutes, whatever `WARDBY_TOOL_MAX_TIMEOUT_MS` and `callTimeoutMs` say. The call then fails with
148
+ [`mcp_unavailable`](../help/errors/mcp-unavailable.md) (`502`), the gateway stops the call, and it
149
+ still counts against the run's call limit. A run that has been stopped or cancelled can't start new
150
+ calls: its token is refused with `401`.
151
+
152
+ ## Refreshing and changes
153
+
154
+ `refresh_mcp_server` (and `update_mcp_server`) fetch the tool list again and
155
+ apply it at once. The reply lists tools added, removed and changed
156
+ (description or schema). A removed tool that an agent allows is dropped from
157
+ that agent's runs and shows under `missingTools` in `get_agent`; nothing
158
+ needs re-attaching if the server brings it back and you refresh again. A
159
+ tool the server newly adds is not available to any agent until you attach it.
160
+
161
+ ## What a call does
162
+
163
+ Each call opens its own session: `initialize`, `notifications/initialized`,
164
+ `tools/call`, then a `DELETE` to end it. Because every call gets a fresh
165
+ session, a server that keeps state between calls in one session won't see
166
+ that state on the next call. Wardby checks on every call that the tool is
167
+ still attached and still in the saved list, and that the agent's owner still
168
+ owns the server or holds `use` on it; the call uses the server's current URL
169
+ and secrets. Arguments that aren't a JSON object return `invalid_arguments`
170
+ without calling the server. Text returned to the model is cut at 100,000
171
+ characters, with a note.
172
+
173
+ Wardby answers none of a server's own requests (sampling, elicitation,
174
+ roots). Notifications in a reply are ignored. A reply that carries only a
175
+ request from the server, and no answer to Wardby's request, fails the call
176
+ with `mcp_server_error`.
177
+
178
+ One log line per call records the run id, agent id, server id, tool,
179
+ outcome, duration and result size, never the arguments, the response or the
180
+ URL. Secret values are removed from results, errors and logs.
181
+
182
+ ## Not supported
183
+
184
+ stdio servers, OAuth to a server (use a header secret), MCP resources,
185
+ prompts and sampling.
186
+
187
+ ## Trust
188
+
189
+ A server's results are untrusted input to the model, like any tool result. The
190
+ allowlist is the control: attach only the tools an agent needs. A server's
191
+ annotations (read-only, destructive and so on) are hints from the server, not
192
+ something Wardby can verify. The one place Wardby acts on a hint is Codex coding
193
+ agents, which may use only tools marked `readOnlyHint: true` (see
194
+ [Coding agents](#coding-agents)).
195
+
196
+ ## Sharing a server
197
+
198
+ Share a server you own with `grant_access` (`resourceType: "mcp_server"`, a
199
+ principal or `everyone`, level `read`, `use` or `write`; `everyone` is capped
200
+ at `use`). `read` lets the grantee see the server and its tool list; `use`
201
+ also lets them attach it to their own agents. The server's tools then run with
202
+ your brokered secrets, so grant `use` only to people you trust with them.
203
+ `write` allows nothing beyond `use`: only the owner updates, refreshes,
204
+ deletes or re-grants a server.
205
+
206
+ Sharing uses `grant_access` and `revoke_access`, which need the
207
+ `agents:write` scope; registering and attaching need `tools:write`.
208
+
209
+ An agent may use a server only while its owner owns the server or holds `use`
210
+ (or `write`) on it. `revoke_access`, and `grant_access` lowering a grant to
211
+ `read` (for a principal, or `everyone` from `use` to `read`), detach the
212
+ server at once from every agent whose owner can no longer use it; the reply
213
+ counts them under `attachmentsRemoved`. Your own agents, and agents whose
214
+ owner still holds `use` through another grant, keep it. Runs already going
215
+ lose the tools on their next call, which returns `mcp_tool_not_allowed`.
216
+
217
+ `delete_mcp_server` removes the server's tools from every agent attached to
218
+ it, including other owners' agents that use it through a grant, and removes
219
+ its grants.
220
+
221
+ When the server's secrets can't be loaded (a secret was deleted, or its
222
+ broker config no longer lists the server's host), the owner's own agents get
223
+ an `mcp_server_error` that names the secret. An agent using the server through
224
+ a grant gets only "The MCP server's credentials can't be used; ask its owner
225
+ to check it.", never the owner's secret names.
226
+
227
+ When a secret exists but the Wardby process making the call can't decrypt its
228
+ stored value, the call returns `mcp_server_credentials_unavailable`, and
229
+ `register_mcp_server`, `update_mcp_server` and `refresh_mcp_server` fail with
230
+ it as an HTTP 500 error. Every process that decrypts secrets (control plane,
231
+ scheduler, native gateway) must use the same `SECRET_APP_KEY`; if the key
232
+ changed, set the secret again. The owner's agents get that advice; an agent
233
+ using the server through a grant gets the generic message above. Each failure
234
+ also writes one operator `warn` log line with
235
+ `reason: "mcp_secret_decrypt_failed"`, the server and the secret name.
236
+
237
+ An attachment acts with the server owner's secrets. When `make_owner` gives an
238
+ agent a new owner, servers the new owner neither owns nor holds `use` on are
239
+ detached and listed under `bindingsRemoved` with `kind: "mcp_server"`.
240
+ `wardby grants adopt-public` and `wardby grants prune-bindings` remove them the
241
+ same way. An agent that holds an MCP server can't be made writable by
242
+ everyone.
243
+
244
+ ## Troubleshooting
245
+
246
+ A failed call returns `{"error": "<code>", "message": "..."}` to the model;
247
+ the run goes on.
248
+
249
+ | Code | Meaning |
250
+ | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
251
+ | [`mcp_servers_off`](../help/errors/mcp-servers-off.md) | `WARDBY_MCP_SERVERS=off`. |
252
+ | [`mcp_server_unreachable`](../help/errors/mcp-server-unreachable.md) | Wardby couldn't connect to the server. |
253
+ | [`mcp_server_auth_failed`](../help/errors/mcp-server-auth-failed.md) | The server answered 401 or 403. |
254
+ | [`mcp_server_credentials_unavailable`](../help/errors/mcp-server-credentials-unavailable.md) | A secret can't be decrypted: check every process's `SECRET_APP_KEY`. |
255
+ | [`mcp_server_error`](../help/errors/mcp-server-error.md) | Another HTTP error, a malformed reply, or the tool reported an error. |
256
+ | [`mcp_tool_not_allowed`](../help/errors/mcp-tool-not-allowed.md) | Detached, off the allowlist, or the owner lost `use` on the server. |
257
+ | [`mcp_tool_removed`](../help/errors/mcp-tool-removed.md) | The server no longer lists the tool. |
258
+ | [`mcp_call_timeout`](../help/errors/mcp-call-timeout.md) | The call hit its time limit or the run was stopped. |
259
+ | [`mcp_call_limit`](../help/errors/mcp-call-limit.md) | A coding run made more calls than `WARDBY_MCP_MAX_CALLS_PER_RUN`. |
260
+ | [`mcp_unavailable`](../help/errors/mcp-unavailable.md) | The coding proxy has no gateway to forward MCP calls to. |
261
+
262
+ See also [Secrets and tools](tool-secrets.md) and
263
+ [Remote MCP servers](security-deployment.md#remote-mcp-servers).
@@ -1,6 +1,6 @@
1
1
  # Native sandbox
2
2
 
3
- > **Requires Wardby 0.5.4 or later.**
3
+ > **Requires Wardby 0.6.0 or later.**
4
4
 
5
5
  By default a native agent runs inside the Wardby server process: its turn loop
6
6
  and any user tool code you attach execute next to the database connection, the
@@ -78,6 +78,13 @@ container to it under the alias `wardby-native-gateway`, and starts the worker
78
78
  on it. When the run ends the worker, the network, and the gateway's membership
79
79
  of it are removed.
80
80
 
81
+ The gateway also serves coding agents' remote MCP tools: the coding proxy
82
+ forwards a coding run's `/mcp` requests to the gateway's HTTP port, on the
83
+ `/coding-mcp` route, where the run's MCP token is checked again. Set
84
+ `WARDBY_MCP_GATEWAY_URL` on the coding proxy to the gateway's base URL
85
+ ([details](security-deployment.md#remote-mcp-servers)). The proxy holds no
86
+ gateway credentials, and each route authenticates separately.
87
+
81
88
  The gateway is stateless: all state is in Postgres. You can restart it while
82
89
  runs are in flight; workers retry their calls. It must not be given the Docker
83
90
  socket, because workers can reach it.
@@ -131,13 +138,16 @@ The gateway is the same Wardby build as the server, started with the
131
138
  `native-gateway` command. It needs the server's database, LLM, secrets, and
132
139
  integration settings, and **no Docker access**.
133
140
 
134
- | Gateway variable | Purpose |
135
- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
136
- | `NATIVE_GATEWAY_LISTEN` | `host:port` to listen on. Default `0.0.0.0:8790`. |
137
- | `DATABASE_URL` | The same database the server uses. With `DATABASE_AUTH=rds-iam` the gateway also needs `DATABASE_AUTH`, `AWS_REGION` (or `DATABASE_AWS_REGION`) and the CA bundle file named by `sslrootcert` ([details](security-deployment.md#database-authentication)). |
138
- | LLM provider keys | The same provider settings as the server (for example `OPENAI_API_KEY`). |
139
- | `SECRET_APP_KEY` | Same value as the server, so tools can read their bound secrets or send brokered ones ([details](tool-secrets.md)). |
140
- | Integration settings | The same repository-host and issue-tracker settings as the server. |
141
+ | Gateway variable | Purpose |
142
+ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
143
+ | `NATIVE_GATEWAY_LISTEN` | `host:port` to listen on. Default `0.0.0.0:8790`. |
144
+ | `DATABASE_URL` | The same database the server uses. With `DATABASE_AUTH=rds-iam` the gateway also needs `DATABASE_AUTH`, `AWS_REGION` (or `DATABASE_AWS_REGION`) and the CA bundle file named by `sslrootcert` ([details](security-deployment.md#database-authentication)). |
145
+ | LLM provider keys | The same provider settings as the server (for example `OPENAI_API_KEY`). |
146
+ | `SECRET_APP_KEY` | Same value as the server, so tools can read their bound secrets or send brokered ones ([details](tool-secrets.md)). |
147
+ | Integration settings | The same repository-host and issue-tracker settings as the server. |
148
+ | `WARDBY_RUN_STEPS` | Same value as the server, if you set it: the ceiling for run steps. The gateway records at each run's own level, so set `detail` here too if agents use it ([details](debugging-agent-runs.md#run-steps)). |
149
+ | `WARDBY_TOOL_MAX_TIMEOUT_MS` | Same value as the server, if you set it: the longest time limit a tool may use ([details](security-deployment.md#tool-time-limits)). |
150
+ | `WARDBY_MCP_MAX_CALLS_PER_RUN` | Same value as the server, if you set it: the most remote MCP tool calls one coding run may make (default `100`; [details](security-deployment.md#remote-mcp-servers)). |
141
151
 
142
152
  For local development, the repository ships a compose overlay
143
153
  (`deploy/local/docker-compose.native-sandbox.yml`) that runs the gateway next to
@@ -541,6 +551,11 @@ or the server running upkeep is not up.
541
551
  Gateway-side variables (`NATIVE_GATEWAY_LISTEN`, `NATIVE_GATEWAY_DENY_PORT`) are
542
552
  set on the gateway, not the server.
543
553
 
554
+ The gateway also records sandbox runs' steps (see
555
+ [Run steps](debugging-agent-runs.md#run-steps)), so when you change
556
+ `WARDBY_RUN_STEPS` (including `detail`) or `RUN_STEP_RETENTION_DAYS`, set them on
557
+ the gateway as well as the server.
558
+
544
559
  ## Limits and deadline
545
560
 
546
561
  - Each worker is limited by `NATIVE_SANDBOX_CPUS`, `NATIVE_SANDBOX_MEMORY_MB`,
@@ -549,6 +564,12 @@ set on the gateway, not the server.
549
564
  - A sandbox run has a maximum lifetime of 60 minutes. Past it the worker is
550
565
  stopped and the run fails with `native_sandbox_deadline_exceeded`.
551
566
  - The agent's own turn and budget limits still apply as for any native agent.
567
+ - Each tool call has the tool's own time limit (`timeoutMs`, default 10
568
+ seconds, capped by `WARDBY_TOOL_MAX_TIMEOUT_MS`). The worker enforces it; the
569
+ gateway gives each host call the limit plus 20 seconds, never past the run's
570
+ deadline. Update the worker image with the server: an older image refuses a
571
+ run whose agent has a tool with `timeoutMs`
572
+ ([details](security-deployment.md#tool-time-limits)).
552
573
 
553
574
  ## Stopping a run
554
575
 
@@ -558,7 +579,8 @@ run whether it is still waiting for a worker or already running:
558
579
  1. The run is recorded `cancelled` with the caller's reason, unless it already
559
580
  finished (the first terminal state wins; a finished run is not changed).
560
581
  2. Its gateway session ends. The worker's next call (a model turn, a tool, or
561
- its result) is refused, and the worker exits on its own.
582
+ its result) is refused, and the worker exits on its own. A tool's host call
583
+ still running on the gateway is aborted within a few seconds.
562
584
  3. A worker still running 5 seconds later is killed. Either way the worker is
563
585
  then removed; a worker claimed from the warm pool is discarded with its pool
564
586
  row and never returned to the pool.