·

What Is Kubernetes MCP

Learn what Kubernetes MCP is and how it lets your AI agent inspect and manage clusters, pods, and deployments.

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/getkubectl_get, pods_list, resources_list (Go server uses a generic resources_list with a kind parameter 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.
  • Logspods_log or logs_get, with parameters for container (required on multi-container pods), tailLines, previous (crashed container's last run), and sinceSeconds.
  • Events — cluster-wide or namespace-scoped event streams, sortable by lastTimestamp. This is where BackOff, FailedScheduling, and Unhealthy reasons show up before they're visible anywhere else.
  • Execpods_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 bad exec can still corrupt an app's in-memory state or trigger a write to a mounted volume.
  • Apply/Create/Deleteresources_create_or_update, resources_delete. This is the tool category that needs the tightest RBAC binding, covered later in this topic.
  • Scaledeployments_scale or generic patch against spec.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 pass container explicitly on multi-container pods (sidecars like Envoy or Istio's proxy will silently return their own logs otherwise).
- previous: true on 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.
- Prefer resources_get/describe-equivalent calls over raw list when 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
- Run kubectl config get-contexts before 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 pass KUBECONFIG explicitly 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 Go kubernetes-mcp-server has a --read-only flag), 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 bind secrets read 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.
- Use kubectl auth can-i --list --as=system:serviceaccount:checkout-prod:mcp-agent -n checkout-prod to 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 Go kubernetes-mcp-server if you want a single static binary with no kubectl runtime dependency and native --read-only support; pick mcp-server-kubernetes if 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.