Kubernetes MCP servers give an AI coding agent a structured, tool-call interface to a cluster instead of forcing it to shell out to kubectl and parse text output. The two servers you'll see most often in production setups are mcp-server-kubernetes (Flux159's implementation, TypeScript, wraps @kubernetes/client-node) and the newer Go-based kubernetes-mcp-server maintained under the containers/kubernetes-mcp-server project, which ships as a static binary with no external kubectl dependency. Both expose a comparable surface: list/get/describe on core resources, log retrieval, exec into containers, and apply/delete for manifests. The distinction matters in practice — the Go binary is easier to sandbox in CI and doesn't require a kubectl install in the agent's execution environment, while the Node-based server has broader adoption and more GitHub issues resolved around edge cases like multi-container pods and CRDs.
This module treats Kubernetes MCP as an operational co-pilot: read cluster state, correlate it against your deployment history and application logs, and only then propose or execute changes. That ordering — read before write — is the single most important discipline this module reinforces. An agent that jumps straight to kubectl rollout restart because a pod looks unhealthy will occasionally restart a StatefulSet mid-migration, or bounce a pod pinned to a node for GPU affinity reasons it never inspected.
Core Kubernetes MCP Tools: Pods, Deployments, Services, Events, and Logs
Both major MCP servers converge on a similar tool taxonomy, though naming differs slightly. Expect these categories:
- Resource listing/get —
kubectl_get,pods_list,resources_list(Go server uses a genericresources_listwith akindparameter covering any API-discoverable type, including CRDs). - Describe — returns the equivalent of
kubectl describe, including conditions, events attached to the object, and volume mounts. This is usually the highest-value tool for triage because it aggregates spec + status + recent events in one call. - Logs —
pods_logorlogs_get, with parameters forcontainer(required on multi-container pods),tailLines,previous(crashed container's last run), andsinceSeconds. - Events — cluster-wide or namespace-scoped event streams, sortable by
lastTimestamp. This is whereBackOff,FailedScheduling, andUnhealthyreasons show up before they're visible anywhere else. - Exec —
pods_exec, runs a command inside a running container. Treat this as a mutating-adjacent capability even though it doesn't change cluster state directly — a badexeccan still corrupt an app's in-memory state or trigger a write to a mounted volume. - Apply/Create/Delete —
resources_create_or_update,resources_delete. This is the tool category that needs the tightest RBAC binding, covered later in this topic. - Scale —
deployments_scaleor generic patch againstspec.replicas. - Rollout control — some servers expose rollout status/undo directly; others require you to patch the Deployment's pod template annotation to trigger a rollout, or delete the ReplicaSet.
Here's what a typical describe-style call looks like from the agent's side, and the shape of data it gets back:
{
"tool": "resources_get",
"arguments": {
"kind": "Pod",
"namespace": "checkout-prod",
"name": "checkout-api-7d8f9c6b45-x2vqp"
}
}
status:
phase: Running
containerStatuses:
- name: checkout-api
ready: false
restartCount: 14
state:
waiting:
reason: CrashLoopBackOff
message: "back-off 5m0s restarting failed container"
lastState:
terminated:
reason: OOMKilled
exitCode: 137
That OOMKilled + exitCode: 137 combination is the kind of signal an AI agent should surface immediately and unprompted — it's a memory limit problem, not a code bug, and no amount of log tailing will explain it if the container never logged before being killed.
Tips
- Always passcontainerexplicitly on multi-container pods (sidecars like Envoy or Istio's proxy will silently return their own logs otherwise).
-previous: trueon the logs call is the only way to see output from a crashed container instance — the current instance's log is usually empty right after a restart.
- Preferresources_get/describe-equivalent calls over rawlistwhen triaging a single pod — you want the events attached to that object, not a bare status table.
Kubernetes MCP Setup: Kubeconfig, Contexts, Namespaces, and RBAC Scoping
Kubernetes MCP servers authenticate the same way kubectl does: via a kubeconfig file (default ~/.kube/config) or in-cluster service account tokens when the server itself runs as a pod. For a developer machine driving Claude Code, Cursor, or Gemini CLI against a real cluster, you're almost always pointing at a kubeconfig with one or more contexts.
A minimal Claude Code MCP registration for the Go server looks like this:
{
"mcpServers": {
"kubernetes": {
"command": "kubernetes-mcp-server",
"args": ["--kubeconfig", "/Users/you/.kube/config"],
"env": {
"KUBECONFIG": "/Users/you/.kube/config"
}
}
}
}
For the Node-based mcp-server-kubernetes, the equivalent is:
{
"mcpServers": {
"kubernetes": {
"command": "npx",
"args": ["-y", "mcp-server-kubernetes"],
"env": {
"KUBECONFIG": "/Users/you/.kube/config"
}
}
}
}
If your kubeconfig has multiple contexts (dev, staging, prod), set the active one before launching the agent session, not inside the agent's prompt:
kubectl config use-context staging-cluster
kubectl config current-context
Some server implementations accept a --context flag to override without touching your global kubeconfig state — check this before relying on prompt text like "use the staging context," because most MCP servers do not parse natural language to select a context; they use whatever the kubeconfig's current-context points to at process start, and won't notice if you switch it mid-session without restarting the MCP connection.
Namespace scoping matters just as much as context scoping. Never let the agent default to --all-namespaces on a shared cluster where other teams' workloads are visible. Bind the service account (or your personal kubeconfig context) to specific namespaces via RBAC, covered next, and additionally pass explicit namespace arguments in every tool call rather than relying on a kubeconfig default namespace, which is easy to lose track of across a long agent session.
Tips
- Runkubectl config get-contextsbefore starting any agent session touching a cluster you don't work in daily — confirm you're not accidentally pointed at prod.
- Keep a separate kubeconfig file per environment (~/.kube/config-staging,~/.kube/config-prod) and passKUBECONFIGexplicitly per MCP server registration instead of one shared file with many contexts — it removes an entire class of "wrong cluster" mistakes.
- Restart the MCP server process after switching contexts; most implementations cache the client at startup.
What AI Can Automate: Read-Only Triage vs Mutating Cluster Operations
Split the tool surface mentally into three tiers, and treat the boundary between tier one and tier two as a hard human-approval gate in any environment that isn't a personal sandbox.
Tier 1 — read-only, safe to automate fully: get, list, describe, logs, events. An agent can run dozens of these calls per session with zero blast radius. This is where 80% of the value of Kubernetes MCP lives — an agent that can chain "list pods in CrashLoopBackOff → describe the worst one → pull its previous logs → check recent Deployment events" in under ten seconds is doing work that would take a human several minutes of context-switching between kubectl and a log viewer.
Tier 2 — mutating but reversible: scale, rollout restart, rollout undo, apply against a manifest you've already reviewed. These should always require an explicit human "go ahead" in the chat before the agent invokes the tool call, even if the agent is confident. The reason isn't that the agent is usually wrong — it's that the cost of an unreviewed mutation on a shared cluster is asymmetric: one bad apply can take down a service for everyone, while an extra ten seconds of human review costs nothing.
Tier 3 — destructive or hard-to-reverse: delete on PVCs, namespaces, or CRDs with finalizers; exec into a production pod's shell; scaling a StatefulSet down when it changes ordinal-based storage bindings. These should not be reachable by MCP tool calls in a production RBAC role at all — see the next section — regardless of how the agent is prompted.
A realistic read-only chain, as an agent would actually execute it:
1. resources_list(kind=Pod, namespace=payments-prod, labelSelector="app=ledger-writer")
2. resources_get(kind=Pod, namespace=payments-prod, name=<worst pod from step 1>)
3. pods_log(namespace=payments-prod, name=<pod>, container=ledger-writer, previous=true, tailLines=200)
4. events_list(namespace=payments-prod, fieldSelector="involvedObject.name=<pod>")
None of those four calls can break anything. That's exactly why they're the right first move for any incident, automated or not.
Tips
- Configure your agent's system prompt or project rules file to explicitly forbid tier-2/tier-3 tool calls without a human "yes, do it" in the same conversation turn.
- If your MCP server supports read-only mode (the Gokubernetes-mcp-serverhas a--read-onlyflag), enable it for any session where you only intend to investigate — it removes the temptation entirely.
- Log every mutating tool call the agent makes to a local file or your team's audit channel; RBAC audit logs from the API server help, but a plain-text trail of "agent scaled X to N replicas at 14:32" is faster to review after an incident.
Least-Privilege RBAC for an AI Agent Touching a Production Cluster
Treat the AI agent as any other automated client hitting the Kubernetes API — it gets a dedicated ServiceAccount, a narrowly scoped Role or ClusterRole, and nothing resembling cluster-admin. Never point an agent's MCP server at a kubeconfig built from your personal kubectl credentials if you have cluster-admin — the agent inherits every permission you have, silently.
A reasonable starting RBAC set for read-heavy triage plus scoped mutation in one namespace:
apiVersion: v1
kind: ServiceAccount
metadata:
name: mcp-agent
namespace: checkout-prod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: mcp-agent-role
namespace: checkout-prod
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "events", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments/scale"]
verbs: ["patch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["patch"]
resourceNames: []
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mcp-agent-binding
namespace: checkout-prod
subjects:
- kind: ServiceAccount
name: mcp-agent
namespace: checkout-prod
roleRef:
kind: Role
name: mcp-agent-role
apiGroup: rbac.authorization.k8s.io
Notice what's deliberately absent: no secrets read access (agents pasting secret values into a chat transcript is a real leak vector), no delete verb anywhere, no exec subresource, and the Role is namespace-scoped rather than a ClusterRole. If the agent needs to triage across several namespaces, create identical Roles per namespace and bind them individually rather than reaching for a ClusterRole out of convenience — the extra YAML is worth the blast-radius reduction.
For pods/exec specifically, if you decide the agent needs it for live debugging, scope it tightly and consider making it a separate, non-default Role that a human explicitly assumes for the session:
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
Generate the kubeconfig for this ServiceAccount with a short-lived token rather than a long-lived Secret-based token (deprecated as default behavior since Kubernetes 1.24):
kubectl create token mcp-agent --namespace checkout-prod --duration=8h
Wire that token into a kubeconfig context dedicated to the agent, and rotate it by re-running the command — an 8-hour expiry means a leaked transcript doesn't grant standing access.
Tips
- Never bindsecretsread access to the agent's Role — treat any need to "check a secret's value" as a signal to do it manually outside the agent session.
- Usekubectl auth can-i --list --as=system:serviceaccount:checkout-prod:mcp-agent -n checkout-prodto verify the effective permission set matches what you intended before wiring it into the MCP server.
- Short-lived tokens (kubectl create token) beat static ServiceAccount secrets for anything an AI agent authenticates with — rotate on every session if the workflow allows it.
Tips
Tips
- Pick the Gokubernetes-mcp-serverif you want a single static binary with nokubectlruntime dependency and native--read-onlysupport; pickmcp-server-kubernetesif you need the broader community-tested tool coverage and don't mind the Node runtime.
- Set up namespace- and verb-scoped RBAC before your first agent session on any cluster that isn't a personal sandbox — retrofitting RBAC after an agent has already run with broad permissions is a much harder sell to a security team.
- Keep tier-2 and tier-3 operations behind explicit human confirmation in every environment except a disposable local cluster (kind, minikube) — the setup cost of a confirmation gate is minutes; the cost of an unreviewed production mutation is not.