Skip to content

Connect a kagent agent

For a platform engineer who runs kagent: at the end, an AgentBackend points Agent Kourier at your kagent install and a Binding's turns reach one of its agents.

kagent 1.0 uses the kagent-v1 dialect, which speaks A2A 1.0 and kagent's human-in-the-loop extension: structured questions with choices, and tool calls shown as step cards. It is tested against kagent 1.0.0-alpha5.

1. Check the cluster can run it. kagent 1.0 runs every agent session on Agent Substrate, which needs the pod-certificate APIs: the ClusterTrustBundle, ClusterTrustBundleProjection and PodCertificateRequest feature gates and certificates.k8s.io/v1beta1. They are off by default through Kubernetes 1.36, and managed EKS through 1.36 cannot enable them. From 1.37 the APIs are GA, but v1beta1 is still off by default, and the pinned Substrate needs it; EKS 1.37 serves the GA v1 APIs but not v1beta1. The project's kind tiers turn them on, so a cluster that runs the kind tiers' kagent may still not run it elsewhere. Check:

kubectl api-resources --api-group=certificates.k8s.io   # lists clustertrustbundles and podcertificaterequests
kubectl api-versions | grep certificates.k8s.io/v1beta1

Without them, use kagent 0.9.x (the other tab) or another agent through the a2a dialect.

2. Put a verifying front door in front of the controller. Agent Kourier must never call kagent's controller port (8083) directly: in its OIDC (trusted-proxy) mode kagent decodes the token without verifying it. See Put a verifying front door before kagent.

3. Declare the backend, pointing at the front door. The dialect adds the agent's path itself:

config:
  agentBackends:
    kagent:
      spec:
        dialect: kagent-v1
        url: http://kagent-frontdoor.kagent.svc.cluster.local:4180
        allowedNamespaces: [payments]

4. Give each Binding its own token. A Binding's identity.tokenSecretRef names a Secret with an OIDC token the front door accepts. Create the Binding identity shows how to provision the account and issue the first token with Dex. In the tested 1.0.0-alpha5 OIDC setup, kagent binds a session to the identity that created it, so every turn of a Binding uses the same token owner. A Binding with tokenSecretRef must target an OIDC-mode kagent: in insecure mode kagent ignores the token and takes the user from ?user_id= or X-User-Id; with neither, it runs every turn as [email protected].

5. Configure interactions. Set interactions.askUser: true when the agent should ask people questions. Set interactions.toolApprovals: true when a write-capable agent should pause for a Slack decision. Approvals are disabled by default. Optionally set interactions.approverGroups to Slack user group IDs; without groups, every non-bot channel member may decide and the config loader warns. With groups, the decider must belong to one of them, belong to the channel, and not be a bot. The Slack app needs the usergroups:read bot scope when groups are used; reinstall the app after adding that scope. See Let the agent ask questions for the controls, timeout and audit behavior.

toolApprovals and askUser are independent. A tool approval on a Binding where toolApprovals is false is denied through kagent with a fixed reason and a notice in the thread. A prompt that cannot be shown exactly or cannot fit in one message is refused and the task is cancelled. A pinned kagent 1.0.0-alpha5 runtime check approved add_numbers and denied check_status in one two-tool pause; only add_numbers executed. The broker's end-to-end suite verifies this with fake Slack and pinned kagent 1.0.0-alpha5.

kagent 0.9.x speaks A2A 0.3, which the generic a2a dialect reads. It runs on a cluster without the pod-certificate APIs. It is tested against kagent 0.9.4 on a real cluster.

1. Find the controller's Service. Its name depends on the Helm release name. Argo CD names the release after the Application, so an Application called my-kagent makes the Service my-kagent-controller.

kubectl get svc -n <kagent-namespace>

2. Declare the backend with the agent's A2A endpoint. Keep the trailing slash: a POST without it was answered with a 307 in the 0.9.x installs this was written against (kagent's own router issues no redirect, so another hop in front of it may be the source), and Agent Kourier follows no redirect.

config:
  agentBackends:
    kagent:
      spec:
        dialect: a2a
        url: http://<controller-service>.<namespace>.svc.cluster.local:8083/api/a2a/<agent-namespace>/<agent>/

With the a2a dialect the URL names one agent, so each agent needs its own AgentBackend.

3. Know the limits of this setup.

  • A 0.3 agent has no task list. After a restart, or after a send whose outcome is unknown, Agent Kourier cannot ask whether the agent already has the turn. It sends again, and the agent may run the turn twice. That is harmless for a read-only agent and not for one that changes things.
  • The a2a dialect does not see tool calls, so the agent's own tool list and the role behind its tools are the only fence on a write. It shows no step cards.
  • A text input-required pause is shown as a question, and the reply is sent as text, only when the Binding sets interactions.askUser: true. Whether kagent 0.9.x ever pauses with a text question has not been tested.
  • The controller accepted a placeholder token in the first install, so nothing verifies the Binding's identity. Put a verifying front door in front of it if that matters.

Do not install kagent 0.9.x with its defaults

Its chart binds the bundled tool server to cluster-admin and serves write tools such as delete, apply and exec. For an agent that answers alerts, run the tool server with --read-only, bind it to a role that cannot read Secrets, and list the agent's tools by name. The defaults also run ten bundled agents, about 2.3 GiB of memory requests.

Check it

Mention the bot in a bound channel. The thread gets the agent's answer. If it says "I can't reach the agent right now, and I'm trying again.", the pod log's session turn failed line has the reason: see Troubleshoot an install.