@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.
- package/.env.example +5 -5
- package/README.md +6 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +11 -0
- package/dist/claude-coding-worker/driver.d.ts +12 -0
- package/dist/claude-coding-worker/driver.js +29 -7
- package/dist/claude-coding-worker/main.js +5 -1
- package/dist/claude-coding-worker/sdk.js +17 -1
- package/dist/claude-plugin/command.d.ts +64 -0
- package/dist/claude-plugin/command.js +379 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +2 -1
- package/dist/cli.js +71 -3
- package/dist/coding/code-host.d.ts +16 -0
- package/dist/coding/code-host.js +60 -0
- package/dist/coding/mcp-token.d.ts +12 -0
- package/dist/coding/mcp-token.js +26 -0
- package/dist/coding/protocol.js +2 -0
- package/dist/coding/redact.js +6 -1
- package/dist/coding/transcript/recorder.d.ts +5 -4
- package/dist/coding/transcript/recorder.js +3 -9
- package/dist/coding-worker/debug-trace.d.ts +3 -1
- package/dist/coding-worker/debug-trace.js +4 -2
- package/dist/coding-worker/driver.js +8 -1
- package/dist/coding-worker/main.js +5 -1
- package/dist/coding-worker/mcp-config.d.ts +27 -0
- package/dist/coding-worker/mcp-config.js +32 -0
- package/dist/coding-worker/sdk.d.ts +36 -0
- package/dist/coding-worker/sdk.js +28 -1
- package/dist/coding-worker/types.d.ts +4 -0
- package/dist/config/mcp-call-limit.d.ts +8 -0
- package/dist/config/mcp-call-limit.js +18 -0
- package/dist/config/mcp-servers.d.ts +8 -0
- package/dist/config/mcp-servers.js +12 -0
- package/dist/config/test-simulators.d.ts +10 -0
- package/dist/config/test-simulators.js +28 -0
- package/dist/config/tool-timeout.d.ts +1 -0
- package/dist/config/tool-timeout.js +18 -0
- package/dist/core/coding-mcp.d.ts +36 -0
- package/dist/core/coding-mcp.js +253 -0
- package/dist/core/coding-repository.d.ts +46 -0
- package/dist/core/coding-repository.js +92 -0
- package/dist/core/delegation-slots.d.ts +27 -0
- package/dist/core/delegation-slots.js +44 -0
- package/dist/core/dispatch.d.ts +6 -2
- package/dist/core/dispatch.js +39 -11
- package/dist/core/engine-native.d.ts +2 -0
- package/dist/core/engine-native.js +13 -0
- package/dist/core/error-code.js +2 -0
- package/dist/core/grants.d.ts +28 -3
- package/dist/core/grants.js +46 -4
- package/dist/core/host-events.js +7 -3
- package/dist/core/host-status.js +2 -2
- package/dist/core/issue-status.js +4 -3
- package/dist/core/listen-address.d.ts +10 -0
- package/dist/core/listen-address.js +14 -0
- package/dist/core/mark-ready.d.ts +1 -1
- package/dist/core/mark-ready.js +1 -1
- package/dist/core/mcp-client/client.d.ts +47 -0
- package/dist/core/mcp-client/client.js +141 -0
- package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
- package/dist/core/mcp-client/jsonrpc.js +54 -0
- package/dist/core/mcp-client/results.d.ts +13 -0
- package/dist/core/mcp-client/results.js +42 -0
- package/dist/core/mcp-servers.d.ts +90 -0
- package/dist/core/mcp-servers.js +230 -0
- package/dist/core/mcp-tools.d.ts +108 -0
- package/dist/core/mcp-tools.js +287 -0
- package/dist/core/merge-order-check.js +38 -19
- package/dist/core/notification-dispatcher.js +2 -1
- package/dist/core/power-idle.d.ts +8 -2
- package/dist/core/power-idle.js +17 -3
- package/dist/core/power.d.ts +5 -0
- package/dist/core/power.js +5 -0
- package/dist/core/related-pull-requests.js +26 -10
- package/dist/core/retention-days.d.ts +5 -0
- package/dist/core/retention-days.js +14 -0
- package/dist/core/review-fix.js +2 -2
- package/dist/core/review-host-tools.js +3 -1
- package/dist/core/run-steps/config.d.ts +25 -0
- package/dist/core/run-steps/config.js +40 -0
- package/dist/core/run-steps/control-chars.d.ts +16 -0
- package/dist/core/run-steps/control-chars.js +26 -0
- package/dist/core/run-steps/detail.d.ts +32 -0
- package/dist/core/run-steps/detail.js +240 -0
- package/dist/core/run-steps/extract.d.ts +52 -0
- package/dist/core/run-steps/extract.js +467 -0
- package/dist/core/run-steps/ingress.d.ts +51 -0
- package/dist/core/run-steps/ingress.js +160 -0
- package/dist/core/run-steps/last-step.d.ts +15 -0
- package/dist/core/run-steps/last-step.js +35 -0
- package/dist/core/run-steps/native.d.ts +48 -0
- package/dist/core/run-steps/native.js +164 -0
- package/dist/core/run-steps/seen.d.ts +16 -0
- package/dist/core/run-steps/seen.js +35 -0
- package/dist/core/run-steps/summarize.d.ts +57 -0
- package/dist/core/run-steps/summarize.js +304 -0
- package/dist/core/run-steps/sweep.d.ts +21 -0
- package/dist/core/run-steps/sweep.js +41 -0
- package/dist/core/run-steps/types.d.ts +89 -0
- package/dist/core/run-steps/types.js +39 -0
- package/dist/core/run-steps/wire.d.ts +35 -0
- package/dist/core/run-steps/wire.js +47 -0
- package/dist/core/run-steps/writer.d.ts +49 -0
- package/dist/core/run-steps/writer.js +279 -0
- package/dist/core/runner.d.ts +70 -16
- package/dist/core/runner.js +296 -74
- package/dist/core/secret-broker-config.d.ts +2 -0
- package/dist/core/secret-broker-config.js +1 -1
- package/dist/core/secret-guard.d.ts +5 -2
- package/dist/core/secret-guard.js +22 -7
- package/dist/core/secrets.d.ts +11 -1
- package/dist/core/secrets.js +27 -1
- package/dist/core/serializable-retry.d.ts +18 -0
- package/dist/core/serializable-retry.js +43 -0
- package/dist/core/tool-admin.d.ts +5 -1
- package/dist/core/tool-admin.js +7 -2
- package/dist/core/tool-names.d.ts +13 -0
- package/dist/core/tool-names.js +20 -0
- package/dist/core/webhooks.d.ts +4 -0
- package/dist/core/webhooks.js +68 -49
- package/dist/core/workflow-run-events.js +2 -1
- package/dist/generated/prisma/browser.d.ts +39 -0
- package/dist/generated/prisma/client.d.ts +39 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +80 -50
- package/dist/generated/prisma/enums.d.ts +6 -0
- package/dist/generated/prisma/enums.js +5 -0
- package/dist/generated/prisma/internal/class.d.ts +77 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +632 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +94 -3
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +98 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +94 -3
- package/dist/generated/prisma/models/Agent.d.ts +372 -1
- package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
- package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +40 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +0 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +159 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +76 -1
- package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
- package/dist/generated/prisma/models/HostProvider.js +1 -0
- package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
- package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
- package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
- package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
- package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
- package/dist/generated/prisma/models/McpServer.js +1 -0
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/Principal.d.ts +167 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
- package/dist/generated/prisma/models/Run.d.ts +503 -1
- package/dist/generated/prisma/models/RunStep.d.ts +1453 -0
- package/dist/generated/prisma/models/RunStep.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +7 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +812 -52
- package/dist/import/create.js +16 -1
- package/dist/import/neutral-schema.d.ts +1 -0
- package/dist/import/neutral-schema.js +2 -0
- package/dist/mcp/auth/access.d.ts +1 -0
- package/dist/mcp/auth/grants-cli.d.ts +6 -4
- package/dist/mcp/auth/grants-cli.js +42 -3
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +17 -2
- package/dist/mcp/context.d.ts +6 -0
- package/dist/mcp/index.js +21 -0
- package/dist/mcp/tools/agents.js +135 -20
- package/dist/mcp/tools/grants.js +66 -28
- package/dist/mcp/tools/host-providers.d.ts +2 -0
- package/dist/mcp/tools/host-providers.js +215 -0
- package/dist/mcp/tools/mcp-servers.d.ts +26 -0
- package/dist/mcp/tools/mcp-servers.js +481 -0
- package/dist/mcp/tools/power.js +2 -2
- package/dist/mcp/tools/repositories.js +50 -10
- package/dist/mcp/tools/run-access.d.ts +7 -0
- package/dist/mcp/tools/run-access.js +14 -0
- package/dist/mcp/tools/run-steps.d.ts +16 -0
- package/dist/mcp/tools/run-steps.js +85 -0
- package/dist/mcp/tools/runs.js +186 -18
- package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
- package/dist/mcp/tools/secret-elicitation.js +25 -2
- package/dist/mcp/tools/secrets.js +38 -4
- package/dist/mcp/tools/tools.d.ts +6 -4
- package/dist/mcp/tools/tools.js +40 -8
- package/dist/mcp/tools/trigger.js +28 -6
- package/dist/mcp/transport/http-limits.d.ts +2 -0
- package/dist/mcp/transport/http-limits.js +6 -2
- package/dist/mcp/webhooks/ingress.js +10 -0
- package/dist/native-worker/gateway-server.d.ts +18 -1
- package/dist/native-worker/gateway-server.js +38 -9
- package/dist/native-worker/gateway.d.ts +5 -0
- package/dist/native-worker/gateway.js +13 -1
- package/dist/native-worker/http-server.d.ts +6 -4
- package/dist/native-worker/http-server.js +53 -8
- package/dist/native-worker/ledger.d.ts +6 -2
- package/dist/native-worker/ledger.js +5 -4
- package/dist/native-worker/protocol.d.ts +1 -0
- package/dist/native-worker/protocol.js +4 -1
- package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
- package/dist/providers/coding-proxy/capability-hash.js +5 -0
- package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
- package/dist/providers/coding-proxy/mcp-route.js +166 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +2 -0
- package/dist/providers/coding-proxy/memory-ledger.js +16 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +36 -2
- package/dist/providers/coding-proxy/proxy.d.ts +33 -1
- package/dist/providers/coding-proxy/proxy.js +266 -75
- package/dist/providers/coding-proxy/run-step-forwarder.d.ts +42 -0
- package/dist/providers/coding-proxy/run-step-forwarder.js +206 -0
- package/dist/providers/coding-proxy/runtime.d.ts +13 -0
- package/dist/providers/coding-proxy/runtime.js +87 -4
- package/dist/providers/coding-proxy/server.d.ts +3 -0
- package/dist/providers/coding-proxy/server.js +10 -0
- package/dist/providers/coding-proxy/types.d.ts +18 -0
- package/dist/providers/engine/types.d.ts +26 -0
- package/dist/providers/executor/composition.js +5 -0
- package/dist/providers/executor/container.d.ts +47 -1
- package/dist/providers/executor/container.js +84 -8
- package/dist/providers/host-tools/invoke.d.ts +37 -0
- package/dist/providers/host-tools/invoke.js +117 -0
- package/dist/providers/host-tools/operations.d.ts +65 -0
- package/dist/providers/host-tools/operations.js +149 -0
- package/dist/providers/host-tools/registry.d.ts +79 -0
- package/dist/providers/host-tools/registry.js +362 -0
- package/dist/providers/host-tools/request-guard.d.ts +17 -0
- package/dist/providers/host-tools/request-guard.js +99 -0
- package/dist/providers/host-tools/self-test.d.ts +64 -0
- package/dist/providers/host-tools/self-test.js +142 -0
- package/dist/providers/jobs/docker-isolation.d.ts +2 -2
- package/dist/providers/jobs/docker-isolation.js +30 -8
- package/dist/providers/jobs/docker.d.ts +8 -0
- package/dist/providers/jobs/docker.js +33 -9
- package/dist/providers/jobs/kubernetes-isolation.d.ts +3 -1
- package/dist/providers/jobs/kubernetes-isolation.js +15 -2
- package/dist/providers/jobs/kubernetes.d.ts +13 -0
- package/dist/providers/jobs/kubernetes.js +28 -5
- package/dist/providers/jobs/types.d.ts +6 -0
- package/dist/providers/review-host/types.d.ts +6 -4
- package/dist/providers/review-host/types.js +1 -1
- package/dist/providers/vcs/git.d.ts +6 -2
- package/dist/providers/vcs/git.js +15 -4
- package/dist/providers/vcs/github.d.ts +2 -71
- package/dist/providers/vcs/github.js +8 -11
- package/dist/providers/vcs/index.d.ts +1 -1
- package/dist/providers/vcs/index.js +1 -1
- package/dist/providers/vcs/remote.d.ts +4 -0
- package/dist/providers/vcs/remote.js +7 -1
- package/dist/providers/vcs/routing.d.ts +12 -6
- package/dist/providers/vcs/routing.js +21 -6
- package/dist/providers/vcs/types.d.ts +71 -3
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +21 -0
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/eval-core.d.ts +12 -2
- package/dist/sandbox/eval-core.js +20 -4
- package/dist/sandbox/host-functions.d.ts +4 -0
- package/dist/sandbox/host-functions.js +7 -2
- package/dist/sandbox/limits.d.ts +4 -1
- package/dist/sandbox/limits.js +4 -1
- package/dist/sandbox/run-in-sandbox.d.ts +6 -0
- package/dist/sandbox/run-in-sandbox.js +14 -2
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +1 -1
- package/dist/sandbox/secret-broker.d.ts +1 -0
- package/dist/sandbox/secret-broker.js +1 -1
- package/dist/sandbox/tool-timeout.d.ts +32 -0
- package/dist/sandbox/tool-timeout.js +52 -0
- package/dist/sandbox/user-tool.d.ts +8 -1
- package/dist/sandbox/user-tool.js +9 -3
- package/dist/serve.js +26 -18
- package/dist/viewer/api-schema.d.ts +66 -0
- package/dist/viewer/api-schema.js +31 -0
- package/dist/viewer/graph.js +2 -1
- package/dist/viewer/openapi.js +2 -1
- package/dist/viewer/run-detail.d.ts +2 -0
- package/dist/viewer/run-detail.js +26 -1
- package/dist/wardby-bin.js +5 -0
- package/docs/README.md +11 -1
- package/docs/agent-recipes.md +37 -4
- package/docs/architecture-runtime.md +1 -1
- package/docs/claude-code-plugin.md +208 -0
- package/docs/code-review-agents.md +6 -0
- package/docs/coding-agent-setup.md +94 -1
- package/docs/coding-worker-byo-images.md +4 -2
- package/docs/coding-worker-isolation.md +94 -7
- package/docs/debugging-agent-runs.md +354 -0
- package/docs/getting-started-gke.md +33 -11
- package/docs/getting-started-identity-provider.md +43 -37
- package/docs/getting-started.md +8 -0
- package/docs/host-providers.md +294 -0
- package/docs/mcp-servers.md +263 -0
- package/docs/native-sandbox.md +31 -9
- package/docs/security-deployment.md +161 -31
- package/docs/tool-secrets.md +3 -0
- package/docs/viewer-api.md +27 -12
- package/docs/viewer-api.openapi.yml +78 -2
- package/help/admin-viewer.md +5 -0
- package/help/agent-recipes.md +33 -2
- package/help/brokered-secrets.md +3 -0
- package/help/build-claude-code-image.md +1 -1
- package/help/claude-code-run-pane.md +63 -0
- package/help/deploy-gke.md +5 -0
- package/help/errors/coding-mcp-gateway-unavailable.md +46 -0
- package/help/errors/coding-mcp-pins-unavailable.md +35 -0
- package/help/errors/database-auth-invalid.md +1 -1
- package/help/errors/host-provider-error.md +40 -0
- package/help/errors/host-tool-failed.md +36 -0
- package/help/errors/mcp-call-limit.md +22 -0
- package/help/errors/mcp-call-timeout.md +22 -0
- package/help/errors/mcp-server-auth-failed.md +22 -0
- package/help/errors/mcp-server-credentials-unavailable.md +45 -0
- package/help/errors/mcp-server-error.md +28 -0
- package/help/errors/mcp-server-unreachable.md +22 -0
- package/help/errors/mcp-servers-off.md +21 -0
- package/help/errors/mcp-tool-not-allowed.md +26 -0
- package/help/errors/mcp-tool-removed.md +22 -0
- package/help/errors/mcp-unavailable.md +25 -0
- package/help/errors/native-sandbox-capacity.md +1 -1
- package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
- package/help/errors/native-sandbox-docker-failed.md +1 -1
- package/help/errors/native-sandbox-gateway-error.md +1 -1
- package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
- package/help/errors/native-sandbox-image-not-pinned.md +1 -1
- package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
- package/help/errors/native-sandbox-network-unenforced.md +1 -1
- package/help/errors/native-sandbox-requires-catalog.md +1 -1
- package/help/errors/native-sandbox-unavailable.md +1 -1
- package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
- package/help/errors/native-sandbox-worker-exited.md +1 -1
- package/help/errors/native-sandbox-worker-lost.md +1 -1
- package/help/errors/native-sandbox-worker-unready.md +1 -1
- package/help/errors/repository-not-listed.md +36 -0
- package/help/errors/repository-required.md +36 -0
- package/help/errors/tool-timeout.md +38 -0
- package/help/host-providers.md +40 -0
- package/help/identity-and-access.md +4 -1
- package/help/low-power-mode.md +19 -8
- package/help/mcp-servers.md +73 -0
- package/help/mcp.md +9 -2
- package/help/native-capabilities.md +27 -1
- package/help/native-sandbox.md +1 -1
- package/help/operating-agents.md +16 -0
- package/help/related-pull-requests.md +7 -3
- package/help/repo-instructions-and-skills.md +3 -1
- package/help/resume-runs-locally.md +7 -0
- package/help/run-steps.md +140 -0
- package/help/stop-run.md +1 -1
- package/help/troubleshooting/coding-workers.md +9 -1
- package/help/troubleshooting/debugging-agent-runs.md +29 -0
- package/package.json +2 -1
- package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
- package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
- package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
- package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
- package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
- package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
- package/prisma/migrations/20261110000000_run_steps/migration.sql +26 -0
- package/prisma/migrations/20261111000000_run_steps_viewer_notify/migration.sql +59 -0
- package/prisma/migrations/20261112000000_run_steps_detail/migration.sql +9 -0
- package/prisma/schema.prisma +211 -14
- 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).
|
package/docs/native-sandbox.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Native sandbox
|
|
2
2
|
|
|
3
|
-
> **Requires Wardby 0.
|
|
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
|
|
135
|
-
|
|
|
136
|
-
| `NATIVE_GATEWAY_LISTEN`
|
|
137
|
-
| `DATABASE_URL`
|
|
138
|
-
| LLM provider keys
|
|
139
|
-
| `SECRET_APP_KEY`
|
|
140
|
-
| Integration settings
|
|
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.
|