Attach an agent
kweblens’s MCP server runs inside the same jar as the web app, so there is no second process to install and no second copy of your kubeconfig. Attaching a coding agent to it is one command.
Every command on this page was executed against a running kweblens instance, except where a note says otherwise. The status codes and the tool output quoted are what the server actually returned, not what the source suggests it would.
|
Read this before you copy anything: the MCP client needs the admin login, in both security modes.
Measured against a running server in
With |
1. Claude Code
KWEBLENS=https://kweblens.example.com
AUTH="Basic $(printf 'admin:%s' "$KWEBLENS_PASSWORD" | base64)"
claude mcp add --transport sse kweblens "$KWEBLENS/sse" --header "Authorization: $AUTH"
--transport sse is not optional: claude mcp add defaults to stdio, and --transport http
speaks Streamable HTTP, which kweblens does not serve.
Confirm it:
claude mcp list
# kweblens: https://kweblens.example.com/sse (SSE) - ✔ Connected
That line is a real health check — Claude Code opens the stream and runs initialize through it.
Dropping the --header turns the same line into ✘ Failed to connect, which is the 401 above.
|
Every form on this page ends with the admin credential sitting in a client config file, Base64-
encoded — which is encoding, not encryption. Whatever file your client stores it in ( |
2. Codex CLI
Codex’s native remote transport (codex mcp add --url …) is Streamable HTTP, and it
authenticates only with a bearer token. kweblens serves SSE and expects Basic, so neither half
matches. Bridge it with mcp-remote, which turns a remote SSE server into a stdio one:
KWEBLENS=https://kweblens.example.com
AUTH="Basic $(printf 'admin:%s' "$KWEBLENS_PASSWORD" | base64)"
codex mcp add kweblens -- \
npx -y mcp-remote "$KWEBLENS/sse" --transport sse-only --header "Authorization:$AUTH"
Note no space after Authorization: — mcp-remote splits an argument containing a space into
two, and the header is then malformed.
codex mcp get kweblens echoes the stored entry; the equivalent hand-written
~/.codex/config.toml block is:
[mcp_servers.kweblens]
command = "npx"
args = ["-y", "mcp-remote", "https://kweblens.example.com/sse",
"--transport", "sse-only",
"--header", "Authorization:Basic <base64 of user:password>"]
3. GitHub Copilot CLI
Copilot CLI reads ~/.copilot/mcp-config.json. The same bridge command works, as a stdio server:
{
"mcpServers": {
"kweblens": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://kweblens.example.com/sse",
"--transport", "sse-only",
"--header", "Authorization:Basic <base64 of user:password>"]
}
}
}
|
The bridge command in this block is the one verified above — it was registered as a stdio server and reported healthy. The Copilot CLI wrapper around it was not executed, because that CLI is not installed on the machine these checks were run on. If it disagrees with what is written here, the file it reads is the authority, not this page. |
4. Any other client
A client that speaks the SSE transport natively needs the URL and a header, and nothing else:
{
"mcpServers": {
"kweblens": {
"url": "https://kweblens.example.com/sse",
"headers": { "Authorization": "Basic <base64 of user:password>" }
}
}
}
A client that only speaks Streamable HTTP needs the mcp-remote bridge, because there is no
POST /mcp — both GET /mcp and POST /mcp answer 404.
5. Cluster ids are not guessable — resolve them
Every tool except listClusters takes a clusterId, and there is no id called default unless
your deployment happens to produce one. ClusterBootstrap registers one cluster per kubeconfig
context, using the context name as the id; default is only the fallback when there is no
readable kubeconfig at all, which is what happens for an in-cluster ServiceAccount.
So the first tool call in any session is listClusters, and the second is listResourceKinds —
resource ids are per cluster, because they include that cluster’s CRDs.
5.1. A cluster-context snapshot, if you want one in a prompt
An attached agent does not need this: listClusters and listResourceKinds are live, and a
checked-in context file goes stale silently, which is worse than no file — an id that no longer
exists produces a confident tool call against nothing. But for a project CLAUDE.md, an
AGENTS.md, or a one-off prompt, the same two facts come straight out of the REST API:
KWEBLENS=https://kweblens.example.com
curl -s "$KWEBLENS/api/v1/clusters" | jq -r '.[] | "- `\(.id)` — \(.name)"'
CLUSTER=$(curl -s "$KWEBLENS/api/v1/clusters" | jq -r '.[0].id')
curl -s "$KWEBLENS/api/v1/clusters/$CLUSTER/nav" |
jq -r '.. | objects | select(.items | length > 0)
| "- \(.label): " + ([.items[].id] | join(", "))'
The recursive .. is load-bearing: a cluster’s CRDs are nested one sub-group per API group under
Custom Resources, so a jq that reads only the top level prints Custom Resources:
customresourcedefinitions and reports a cluster with dozens of custom kinds as having none.
Both are GET`s, so in `open-mode they need no credentials. Against the built-in simulator, which
registers no CRDs, that prints:
- `sim` — Simulator
- Cluster: nodes, namespaces, events
- Workloads: pods, deployments, statefulsets, daemonsets, replicasets, jobs, cronjobs, replicationcontrollers
- Autoscaling: horizontalpodautoscalers
- Config: configmaps, secrets, resourcequotas, limitranges, poddisruptionbudgets, …
- Network: services, ingresses, endpointslices, endpoints, ingressclasses, networkpolicies
- Storage: persistentvolumeclaims, persistentvolumes, storageclasses
- Access Control: serviceaccounts, clusterroles, roles, clusterrolebindings, rolebindings
- Custom Resources: customresourcedefinitions
Paste it with a date on it, and treat it as a snapshot rather than a contract.
6. What the agent can and cannot do
-
Nothing on the tool surface writes. All 17 tools are reads. Changing the cluster stays on the guarded suggest → preview → confirm → apply path in the UI, which is deliberately not reachable from here.
-
Tool output is redacted at the boundary, more strictly than the dashboard redacts anything — Secret
data/stringDatavalues and thelast-applied-configurationannotation are replaced (keys kept),managedFieldsis dropped. The asymmetry is the point: tool output leaves the machine, crosses a network and lands in somebody’s inference logs. See Redaction at the boundary. -
Redaction is not a permission boundary. kweblens acts with one shared credential and is not RBAC-aware, and per ADR-001 (
docs/design/adr-001-identity-model.md, ACCEPTED) that is a decided position, not a gap. An agent attached to this server can read whatever kweblens can read, minus what is redacted. Do not hand out/ssecredentials you would not hand out the kubeconfig to. -
The audit trail names an action, not a person. There is one identity, so a tool call and a browser click are indistinguishable in the log.
7. When it does not connect
Every row below is a status code observed against a running server.
| Symptom | Status | Cause |
|---|---|---|
Client reports "failed to connect"; |
|
No credentials on the client. Add the |
|
|
|
Client posts to |
|
It is using Streamable HTTP. kweblens serves SSE only; use |
Tool calls fail after working for a while |
|
The session id is dead — the SSE stream dropped and the client kept posting to the old
|
Everything handshakes, tools return empty |
|
The |
If a proxy sits in front of kweblens, it must not buffer responses and must tolerate a long-lived
connection — /sse is held open for the life of the session, exactly like the log and watch
streams the SPA uses.
8. Related
-
MCP server — the 17 tools, the transport, and what redaction does
-
Security and identity — open-mode, and the one shared identity
-
Connecting clusters — where cluster ids come from