Agent Harness Configuration Reference
The complete reference for the agent harness chart and control plane. For the guided install and an explanation of how the pieces fit together, see Background Agents on Kubernetes.
Nothing on this page configures agent behavior. Prompts, tools, skills, rules, and model selection are defined in Willow and synced. The configuration schema accepts infrastructure settings only.
Helm values
Images
| Value | Default | Purpose |
|---|---|---|
images.controlPlane.repository | quay.io/webrix/mcp-s-harness | Control-plane image |
images.controlPlane.tag | — required | Pin a concrete build; there is no default |
images.controlPlane.pullPolicy | IfNotPresent | |
images.runtime.repository | quay.io/webrix/mcp-s-harness-runtime | Agent runtime image |
images.runtime.tag | — required | Pin a concrete build |
imagePullSecrets | [] | For example [{ name: webrix-registry }] |
Both tags are required. See Choose an image tag for why a moving tag is unsafe here.
Control plane
| Value | Default | Purpose |
|---|---|---|
replicas | 1 | Control-plane replicas |
resources | 250m / 512Mi requests, 1Gi memory limit | Control-plane resources |
service.type | ClusterIP | |
service.port | 3000 | |
reconcileIntervalSeconds | 300 | How often every agent is re-applied. 0 disables the loop |
defaults.model | anthropic:claude-sonnet-4-6 | Model used only when the agent's Willow platform configuration leaves it unset |
Ingress
| Value | Default | Purpose |
|---|---|---|
ingress.enabled | false | |
ingress.className | nginx | |
ingress.host | harness.example.com | Public hostname Willow calls |
ingress.tls | [] | Standard Kubernetes ingress TLS block |
ingress.annotations | SSE-friendly defaults | Keep proxy-buffering: "off" and a long proxy-read-timeout |
Agents namespace
| Value | Default | Purpose |
|---|---|---|
agents.namespace | mcp-s-agents | Where per-agent workloads are created |
agents.createNamespace | true | Create it as part of the release |
The control plane's Role is bound to this namespace only.
Database
| Value | Default | Purpose |
|---|---|---|
database.existingSecret | "" | Name of an existing Secret holding the connection string |
database.secretKey | DATABASE_URL | Key within that Secret |
database.url | "" | Inline URL, rendered into a chart-managed Secret. Evaluation only |
Setting database.url also renders a copy of the Secret into the agents namespace, because agent pods mount the same connection string as their conversation checkpointer. When you bring your own Secret with existingSecret, make sure an equivalent Secret named mcp-s-harness-db exists in the agents namespace.
Schema migrations run automatically when the control plane starts, so there is no separate migration step.
Secret providers
Three secrets are used across all providers:
| Name | Required | Purpose |
|---|---|---|
willowWebhookToken | Yes | Bearer token Willow sends on sync, message, and sessions calls |
modelApiKey | Yes | Model provider key handed to each agent pod |
githubToken | Only with GitOps | Read access to the configuration repository |
env (default)
Values come from a Kubernetes Secret, mounted rather than only injected so the kubelet can refresh them in place on rotation.
secrets:
provider: env
env:
existingSecret: "" # bring your own Secret
willowWebhookToken: "" # or let the chart render one (evaluation)
modelApiKey: ""
githubToken: ""
Preferred for real installations, the chart can instead render an ExternalSecret and manage no secret material itself:
secrets:
provider: env
env:
externalSecret:
secretName: my-org/mcp-s-harness-secret # required
secretStoreName: aws-secretsmanager # default
secretStoreKind: ClusterSecretStore # or SecretStore
refreshInterval: 1h # default
The backend entry is a JSON object whose keys land verbatim in the Secret: WILLOW_WEBHOOK_TOKEN, MODEL_API_KEY, and optionally GITHUB_TOKEN. The external-secrets operator and its store are cluster infrastructure and are not installed by this chart — it only references an existing store.
vault
HashiCorp Vault KV v2, authenticated with the Kubernetes auth method. References are path#key within the mount.
secrets:
provider: vault
vault:
addr: https://vault.your-domain.com
mountPath: secret
k8sRole: mcp-s-harness
k8sAuthMount: kubernetes
refs:
willowWebhookToken: mcp-s-harness/willow#webhook_token
modelApiKey: mcp-s-harness/models#anthropic_api_key
githubToken: mcp-s-harness/github#token
aws
AWS Secrets Manager via IRSA. References are a secret name, optionally with a JSON key.
secrets:
provider: aws
aws:
refs:
willowWebhookToken: mcp-s-harness/willow-webhook-token
modelApiKey: mcp-s-harness/model-api-key
githubToken: mcp-s-harness/github-token
Configuration schema
The chart renders the bootstrap layer of this document into a ConfigMap and mounts it. The Git and API layers, when enabled, override it at runtime. secretRef values are resolved by the active provider at use time; plaintext secrets are rejected.
willow:
run_url: https://run.mcp-s.com # MCP gateway base; fallback when a sync omits mcp_url
connect_url: https://willow.example.com # optional, reserved for end-user flows
webhook_auth:
secretRef: WILLOW_WEBHOOK_TOKEN
defaults:
model: anthropic:claude-sonnet-4-6
model_api_key:
secretRef: MODEL_API_KEY
runtime_image: quay.io/webrix/mcp-s-harness-runtime:<build>
resources:
cpu: "500m"
memory: "1Gi"
git_sync:
enabled: false
repo: my-org/harness-config
branch: main
path: harness.yaml
auth:
secretRef: GITHUB_TOKEN
poll_interval_seconds: 300
config_api: read_write
defaults.resources applies to agent pods. Control-plane resources are set with the resources Helm value instead.
There is no agents: section, and there will not be one. Per-agent behavior lives in Willow.
The control plane enforces the bearer token only when willow.webhook_auth resolves to a value. If it is unset, the webhook and sessions endpoints accept unauthenticated calls — intended for deployments that authenticate at the mesh level with mTLS. The chart always configures it.
GitOps configuration
Set gitSync.enabled and point the harness at a repository to keep infrastructure configuration versioned and reviewable:
gitSync:
enabled: true
repo: my-org/harness-config
branch: main
path: harness.yaml
pollIntervalSeconds: 300
configApi: read_only
POST /config/git-sync/trigger doubles as a GitHub push-webhook target, so changes apply immediately; polling is the fallback. Each pull is validated against the schema and stored as a revision. An invalid document is rejected and recorded with its error rather than applied — inspect the outcome with GET /config/revisions.
configApi: read_only makes PUT /config return 403, which is what you want in a pure-GitOps setup: the repository is the only way in.
Control-plane API
All endpoints are on the harness host. Willow webhooks and the agents/sessions API authenticate with the willowWebhookToken; the config API uses a separate admin token when ADMIN_TOKEN_SECRET_REF is set, and otherwise falls back to the same token.
Authorization: Bearer <token>
Willow webhooks
| Endpoint | Purpose |
|---|---|
POST /willow/sync | agent.sync deploys or updates an agent and returns {external_agent_id, status}. agent.delete tears it down and returns 204 |
POST /willow/message | Delivers one conversation turn; returns {session_id, session_key} |
These are the two URLs you register on the Willow custom agent type.
Agents and sessions
| Endpoint | Purpose |
|---|---|
GET /agents | Inventory of deployed agents with status |
DELETE /agents/:agentId | Tear down an agent's workload |
GET /agents/:agentId/sessions | List sessions. limit (max 200), offset, order |
POST /agents/:agentId/sessions | Create a session; an optional message starts a run immediately |
POST /agents/:agentId/sessions/:sessionId/messages | Send a message to an existing session |
GET /agents/:agentId/sessions/:sessionId/events | The event log. after_seq, limit (max 1000) |
GET /agents/:agentId/sessions/:sessionId/stream | Live SSE stream; replays history first, then follows |
:agentId accepts either the harness id or the Willow agent id, so you can address an agent by whichever you have.
Example — read a conversation as it happens:
curl -N https://harness.your-domain.com/agents/<agentId>/sessions/<sessionId>/stream \
-H "Authorization: Bearer <token>" \
-H "Accept: text/event-stream"
event: user.message
data: {"id":"…","type":"user.message","content":[{"type":"text","text":"Summarize today's PRs."}]}
event: agent.tool_use
data: {"id":"…","type":"agent.tool_use","name":"github__list_pull_requests","input":{"state":"merged"}}
event: agent.tool_result
data: {"id":"…","type":"agent.tool_result","tool_use_id":"…","is_error":false,"content":[…]}
event: agent.message
data: {"id":"…","type":"agent.message","content":[{"type":"text","text":"Seven PRs merged today…"}]}
The stream sends a : keep-alive comment every 15 seconds. Pass after_seq to resume without replaying events you already have.
Configuration
| Endpoint | Purpose |
|---|---|
GET /config/effective | The merged configuration actually in use |
GET /config | The API-override layer only |
PUT /config | Replace the override layer. 403 when config_api is read_only, 400 when the document is invalid |
GET /config/revisions | Revision history with source, version, and validation status |
POST /config/git-sync/trigger | Pull from Git now; also usable as a GitHub webhook target |
Operations
| Endpoint | Purpose |
|---|---|
GET /healthz | Liveness. 200 once the process is up |
GET /readyz | Readiness. 503 database unavailable when Postgres is unreachable |
GET /metrics | Prometheus metrics |
Status values
GET /agents reports one of:
| Status | Meaning |
|---|---|
deploying | A sync is in flight |
ready | Workload applied and the agent is active in Willow |
disabled | The agent is toggled off in Willow; its Deployment is scaled to zero |
error | The last deploy failed; the reason is in error |
Sessions are idle or running.
Related
- Background Agents on Kubernetes — architecture, installation, and troubleshooting
- Run Agents in Your Own Cluster — the dashboard side, for administrators
- Background Agent Settings — registering the platform type in Willow
- Harness API: New Sessions and Messages — the payloads Willow delivers