AI agents¶
How administrators run AI agents on kubelatch: a bot for an agent, the approval switch, the sessions and approvals in Agents, and cutting an agent off. AI agents in Concepts explains the model and its limits; people who connect their own agent follow AI agents.
The MCP server¶
kubelatch serves the MCP server at <KUBELATCH_BASE_URL>/mcp, and next to it the endpoints that sign delegated agents in. It's on by default; KUBELATCH_MCP_ENABLED=false turns both off (config.mcpEnabled in the chart).
| Variable | Default | What it sets |
|---|---|---|
KUBELATCH_MCP_RATE_LIMIT |
120 |
Requests per minute to /mcp, per credential; a delegated agent's, per session. Past it, 429 until the minute ends. Each replica counts its own. |
KUBELATCH_AGENT_SESSION_TTL |
8h |
The duration the consent page proposes. |
KUBELATCH_AGENT_SESSION_MAX_TTL |
24h |
The longest a person may choose; at most KUBELATCH_MAX_TTL_USER. |
KUBELATCH_AGENT_APPROVAL_WAIT |
50s |
How long an agent's call waits for the decision on a held write before it answers pending again; 0 to 4m, and 0 answers at once. |
All of them are in Configuration. The consent page a person answers to authorize an agent (Let Claude Code act as you?) says who asks, where the answer goes, what the agent gets and until when, and warns whenever the answer would leave that computer; AI agents walks through it.
Besides what it routes today, the deployment must send /mcp, /oauth/ and /.well-known/ to kubelatch. Agents' clients look for /.well-known/oauth-protected-resource/mcp and /.well-known/oauth-authorization-server at the root of the host of KUBELATCH_BASE_URL, even when the base URL has a path. The chart's Ingress already sends every path of its host to kubelatch.
/mcp refuses a request whose Origin header isn't the origin of KUBELATCH_BASE_URL: MCP clients that run inside a web page are not supported. A request body can be 1 MiB at most.
A bot for an agent¶
For an agent that isn't one person's (an on-call agent, a step of a pipeline):
- Create a bot in Users and grant it the smallest role that works, on a namespace rather than the whole cluster (Bots, Grant permissions). An agent that diagnoses only needs
viewer. - Open the bot's account page from Users. Its AI agent card has:
- Writes need approval: off by default, so the bot's role alone decides. On, every write of the bot's agents waits until an administrator approves it, unless an access approval it was granted covers it. Turning it on or off asks for confirmation (Require approval, Stop requiring it) and applies from the agent's next write.
- Connect an agent: the MCP endpoint and the commands for Claude Code and Cursor. A disabled bot doesn't offer it.
- Sessions of this bot: opens Agents narrowed to that bot's sessions.
-
Issue a credential for the bot (Bots), as short as the task. Where the agent runs, keep its token in the variable
KUBELATCH_TOKEN, and add kubelatch to the agent with the commands from Connect an agent:claude mcp add --transport http kubelatch https://kubelatch.example.com/mcp --header "Authorization: Bearer $KUBELATCH_TOKEN"{ "mcpServers": { "kubelatch": { "url": "https://kubelatch.example.com/mcp", "headers": { "Authorization": "Bearer ${env:KUBELATCH_TOKEN}" } } } }The dialog never shows a token. Only a bot's own credentials work at
/mcp: a person's credential, akubelatch loginsession or a CI credential get401.
The bot's agent session starts the first time its credential is used at /mcp, one session per credential. Give the agent the rules too, so it uses the tools instead of kubectl.
Sessions and approvals¶
Agents in the sidebar is for everyone. An administrator sees every account's sessions and approvals; anyone else, only their own. Three figures head the page: Waiting for you, Active sessions and Last call. The first two are switches that apply the Pending or Active segment of the card below them. Then come two cards, Approvals and Sessions; Approvals goes first while some approval waits for a decision, and Sessions otherwise.
Sessions¶
- Sessions lists each connected agent as a row named after whom it acts as and the agent's own name, such as
deploy-bot · node. Under the name are its mode (Bot or Delegated), the role it works with (for a delegated session, what the person gave it; for a bot's, the bot's own permissions) and its pending approvals, if any. The columns are Session, Expires, Last used and State (Active, Cut or Expired), and the row menu has Open and Cut. The Active and Ended segments split them. - A session's page carries that name, with its mode and state as pills. It starts with Waiting for a decision when approvals wait on this session, each as its sentence linking to its page. Then it shows Session (On behalf of, Agent, Started, Last used, Expires, the Credential or the OAuth client, Writes need approval and, once cut, Cut by), Permissions in effect (what counts now) and, for a delegated session, its Ceiling: Later permissions (what it takes of the permissions the person receives later: Leave out, Read only or Same role), then each permission it has, the one it came from, and whether it still counts.
- Timeline lists the agent's tool calls, newest first, one line each: the time, the tool, what it did (
patch deployments web, plus+1 more writewhen the call made other writes), the result and the duration. The button3 requestsunfolds the requests the call sent to the cluster, oldest first, and each opens the same panel as in Audit. A call is named after its last write, or else its last real request, never after a dry run: a write held for approval is named after what it read, and its dry run is marked dry run among the requests. Discovery requests (/api,/apis,/version) stay out until you check Include API discovery. For administrators, Open in Audit opens the same rows in Audit.
A bot's session has no expiry of its own: it lasts as long as its credential. A delegated session lasts what the person chose.
Approvals¶
Approvals lists what agents ask a person to decide. Its segments, Pending, Resolved and Expired, show how many each holds. Each row is the sentence of the approval, which links to its page, with when it was asked, its Kind and its Status:
- Write: a change held for approval. Its page shows Changes: the object now against what the cluster's dry run says it becomes, with Secret values hidden. A write waits 15 minutes for a decision; once approved, 15 more to run, once.
- Access: a role on a namespace (or the whole cluster) for some minutes, with the agent's reason. It waits an hour.
The agent's client usually opens the decision page for the person (Claude Code, which asks first, with no plugin), and the agent's call waits for the decision for up to KUBELATCH_AGENT_APPROVAL_WAIT, then asks again while the approval lasts. Where the client can't open it, or the person declines, the agent hands the link over in text. Calls for a write that is already waiting don't create another approval.
A pending approval you can decide also has Approve on its row. It loads the approval first, then asks the same confirmation as its page: the sentence, what the change does (for a Secret, that its hidden values may change), what approving means and the time left. Only Approve in that confirmation approves. An approval decided elsewhere or expired meanwhile is not approved, and the list says why. To read the whole diff, open its page.
Who decides:
- A bot's approvals: any administrator. Approving a bot's access grants an ordinary permission with that expiry, under the usual rules (it appears in Permissions), for 8 hours at most.
- A delegated session's approvals: only the person the agent acts for. An administrator sees them but can't decide them.
While approvals wait for someone, Agents in the sidebar carries their count and the bell shows a notice, to whoever can decide them: one approval links to its page, several to the Approvals card. One account can have at most 20 approvals waiting.
The decision page¶
The page of an approval, the link the agent gives the person, is titled by what is asked: "deploy-bot wants to change configmaps/feature-flags in apps (kind-local)", or "deploy-bot asks for the role developer in apps (kind-local) for 2 h". Under the title are its status pill, who asked and when ("Asked 3 min ago by the agent node") and, while it waits, a countdown ("expires in 11:42"). Opening the page decides nothing: only a click on Approve or Deny does.
For a write, Changes starts with one line of what it does ("Creates the object (12 lines)", "Changes 2 lines of data", "Deletes the object") and then the diff: lines that go start with −, lines that come start with +. The metadata the API server manages (creationTimestamp, uid, resourceVersion, managedFields, generation, selfLink) stays folded behind Show the 7 lines of metadata, and on a phone long lines wrap rather than scroll. For a Secret the line never says nothing changes: its values aren't shown, and a value the write changes reads <redacted, N bytes, changed> in what it becomes, so it shows as a changed line even when its size stays the same. A change too large to compare line by line is shown whole, with a warning to compare it yourself. If the agent gave a reason, the page quotes it.
Deny and Approve come after the diff, and stay fixed at the foot of a phone's screen. Each asks for confirmation with the sentence of the change. Approve the write adds that it runs once, as the agent's call for it comes back; Deny the approval adds that the agent gets a refusal and nothing runs. The page of an access approval warns what approving it means, and Approve the access repeats it: while it lasts, the agent's writes there run without approval, with everything its other permissions there allow.
Once decided or expired, the page says what became of it ("Approved by alex at 17:21 · ran once, the cluster answered 201.", "Expired at 17:34 without running; the agent has to ask again.") and the buttons go. Under Details, folded, are the session, the agent, the tool, the id, the request and the exact times it was asked, expires, was decided and ran.
Cut an agent off¶
In Agents, open the session (or its row menu) and click Cut:
- A bot's session: the bot's credential is revoked, its pending approvals are closed, and the permissions it was granted by approval are revoked. Connecting the agent again takes a new credential.
- A delegated session: its tokens are revoked, its pending approvals are closed, and the permissions the person gave it end.
The next call of the agent fails. Disabling the bot or the person also ends everything their agents had. Revoking a person's permission takes from their agents whatever came from it, at once.
Audit¶
Every request an agent sends to a cluster is an audit row with its agent session and its tool. In Audit, More filters has Tool and Agent session, What ends in the tool, and the detail of a request shows MCP tool and Agent session (Audit and retention). A delegated agent's rows have the person as their subject. whoami and request_access reach no cluster and leave no audit row; their session and approvals are in the control plane.
Troubleshooting¶
| What happens | Why, and what to do |
|---|---|
The agent gets 401 with invalid token: the MCP endpoint accepts a bot's credential, or an agent's token from kubelatch's sign-in |
It was given a person's credential, a kubelatch login session or a CI credential. Use a bot's credential, or connect it without a token so the person authorizes it. |
| Claude Code reports the kubelatch server as failed instead of opening the browser | It sends an Authorization header that kubelatch refuses, so it doesn't fall back to signing in. Remove the header from its configuration for a person's agent. |
| The agent's client can't find where to sign in | /.well-known/ doesn't reach kubelatch: check the Ingress or the reverse proxy in front. curl https://kubelatch.example.com/.well-known/oauth-protected-resource/mcp must answer kubelatch's JSON. |
403 with cross-origin requests are not accepted from /mcp itself |
The request carried an Origin other than that of KUBELATCH_BASE_URL: an MCP client inside a web page, which is not supported. |
429 with too many requests for this credential |
Over KUBELATCH_MCP_RATE_LIMIT. Raise it, or ask the agent to make fewer calls. |
A write answers agent.approval_pending again after about a minute |
Not a failure: nobody decided within KUBELATCH_AGENT_APPROVAL_WAIT. The agent calls again while the approval lasts. If a proxy in front of kubelatch cuts requests sooner, lower the value. |
A write answers agent.approval_executed |
The approval already ran, for example because the agent's client reconnected as the person approved. Read the object to see the result. |
A write answers agent.too_many_pending |
20 approvals already wait for that account: decide them in Agents, or let them expire. |
An agent's write fails with 409 after approval |
The object changed between the approval and the run; the approval shows as run once with that answer. The agent asks again. |
Every message is in Errors.