SafetyCard Request a review

mcp-server-kubernetes

TypeScript MCP server for kubectl/Helm management of Kubernetes clusters

github.com/Flux159/mcp-server-kubernetes · 2026-10-03 · 3d71add

Defense-in-depth score

2.6 / 10

As shipped, this server gives the model the operator's full kubeconfig authority across every context, with exec into any pod, a generic kubectl tool, Helm installs from any repository, and forced deletes all enabled. It has a careful argv flag denylist and masks Secret values in kubectl_get, but the generic tool can run 'config view --raw', which returns raw credentials to the model, and neither the denylist nor the masking is a complete boundary. The dominant risk is a prompt-injected host agent using cluster-admin-equivalent credentials unattended; the read-only and allowlist modes that would contain it are opt-in.

Safest setup

As shipped, this server runs every tool, including pod exec and a general-purpose kubectl tool, with your full kubeconfig, and nothing in it asks a person before acting. It does include read-only and tool-allowlist modes, a kubectl flag denylist and Secret masking in kubectl_get, so the safest setup narrows the Kubernetes identity first, turns those modes on and builds containment around the process.

SetupHowWhat it gives you
A scoped Kubernetes identityKUBECONFIG_PATH=<file> pointing at a kubeconfig with one context whose ServiceAccount has a namespace-scoped, read-mostly roleThe Kubernetes API itself refuses anything outside that role, on that one cluster C1
Read-only modeALLOW_ONLY_READONLY_TOOLS=true in the server's environmentThe server lists and accepts only its small read-only tool set, checked on every call C2
Only the tools neededWhen a task needs writes, ALLOWED_TOOLS=<list> naming just those tools, or ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS=trueExec, generic kubectl and delete tools stay off unless the task calls for them C3
The kubectl flag denylistKeep ALLOW_KUBECTL_UNSAFE_FLAGS unsetEvery kubectl and helm call keeps rejecting API server, token, impersonation and kubeconfig flags C3
Secret maskingKeep the default MASK_SECRETS, leaving it unsetkubectl_get keeps masking Secret values, the one tool output the review found masked C8
Its own container or VMRun the server in a container or VM whose network reaches only the Kubernetes API servers it needsAn outer boundary around host-side kubectl and helm, and a narrow path for data to leave C5
Tool-call tracingENABLE_TELEMETRY=true with OTEL_EXPORTER_OTLP_ENDPOINT=<url> pointing at a collector you controlA span for every tool call, with tool name, argument names, context and namespace, kept outside the server C9

Approving with confidence

  • Read each prompt and approve calls one at a time; kubectl_generic and kubectl_create can change cluster state without a destructive label.
  • Deletes, Helm uninstalls and pod exec have no preview or undo, so check the context, namespace and resource names before approving.

Handling data

  • Output from kubectl_generic, exec_in_pod and logs reaches the model unmasked; treat transcripts like the cluster data they include.
  • Telemetry spans hold argument names, not the commands or manifests; only kubectl_generic also prints its command, to stderr.

Key gaps (3)

  1. Default local mode runs every tool with the operator's full kubeconfig across all contexts, with no authorization check. C1 · Identity & least privilege
  2. exec_in_pod and kubectl_generic run model-chosen commands in any pod, and arbitrary pods can be created, with no isolation from the server. C4 · Code-execution isolation
  3. Worst case under prompt injection from pod logs or resource fields: the default tool set can both exfiltrate cluster data and delete resources with no server-side human step. C5 · Untrusted input blast radius

Criteria

C1 Identity & least privilege

0.00 / 1.00

In the default local mode the server loads the operator's own kubeconfig and runs every kubectl and helm command with it, and the model can pick any context in that file on every call, so it inherits the operator's full authority across every cluster they can reach. There is no server identity, no scoping of verbs or resources, and no authorization check of any kind before a command runs; the only narrowing is an opt-in tool allowlist set by environment variable. A hijacked or misled session therefore holds whatever the kubeconfig grants, which for most operators is cluster-admin.

C2 Approval gates

0.33 / 1.00

As a tool server it relies on the host to ask the human, so what matters is how accurately it labels risk. Most mutating tools carry a destructive hint and there are dry-run options on apply, create, patch and node drain, but the labels are not reliable: some annotations are inaccurate, kubectl_create and port_forward carry no hints at all, and kubectl_generic mixes reads and writes in one tool. An opt-in read-only mode is enforced by the server on every call, though it is not a strict boundary. Deletes (including forced deletes and Helm uninstall) are irreversible with no preview.

C3 Tool & action scoping

0.25 / 1.00

Every kubectl and helm invocation passes through one wrapper that rejects a denylist of dangerous flags (API server, token, impersonation, kubeconfig) and most tools refuse operands that start with a dash, which is a real improvement over raw passthrough. But kubectl_generic accepts any kubectl verb and any positional arguments, and the flag denylist is not a complete boundary. Helm tools accept any repository URL and chart, and every tool, including exec and generic kubectl, is enabled by default.

C4 Code-execution isolation

0.00 / 1.00

exec_in_pod runs any model-chosen command inside any pod container in any namespace, and kubectl_apply, kubectl_create and kubectl_generic can create arbitrary pods, including privileged or host-mounted ones. The server provides no isolation around any of this: commands run inside existing workloads with their own service-account tokens and network access, and on the host side kubectl and helm run as the operator's user with the full environment. The argv is passed without a shell, which prevents shell injection on the host but is not a sandbox.

C5 Untrusted input blast radius

0.07 / 1.00

Pod logs, events, exec output and resource fields are attacker-influenceable and are returned to the model as plain text with no provenance or untrusted marking. The default tool set gives a hijacked agent both exfiltration channels (a model-chosen Helm repository URL, creating pods that call out) and irreversible actions (forced deletes, Helm uninstall), with nothing in the server requiring a human. The opt-in read-only mode would remove most of the state-changing leg but is off by default.

C6 Memory, context & configuration integrity

0.10 / 1.00

The server has no memory store, but the model can persistently rewrite the operator's kubeconfig, which the server and the operator's own kubectl load on every later run. kubectl_context's set operation switches the current context, and it is not the only kubeconfig-writing path. A single injected instruction can therefore leave behind a configuration that points future sessions, and the operator's own terminal, at a different cluster.

C7 Third-party extensions

N/A · full credit 1.00 / 1.00

The server loads no plugins, MCP servers or downloaded code into its own process: it only invokes the kubectl and helm binaries already installed by the operator. Helm charts installed from model-chosen repositories run in the cluster rather than in the server and are covered under tool scoping and untrusted-input blast radius.

C8 Secrets & sensitive-data protection

0.25 / 1.00

Credentials come from the operator's kubeconfig or environment variables; inline kubeconfigs are written to a 0600 temp file. kubectl_get masks the values of Secret objects by default, but this is the only protected path: it can be turned off with MASK_SECRETS=false, it is not a complete boundary, and kubectl_generic can fetch Secrets or run 'config view --raw' to return the full kubeconfig, tokens and keys included, straight to the model. Telemetry is off by default and records only tool names and argument keys.

C9 Audit & traceability

0.28 / 1.00

By default the server keeps no record of what it did: only kubectl_generic prints the command it runs to stderr, and nothing is written to a file the server owns. An opt-in OpenTelemetry integration wraps every tool call in a span, but it records only the tool name, argument names, context, namespace and resource type, not the commands or manifests, and it is off unless both ENABLE_TELEMETRY and an OTLP endpoint are set.

C10 Limits & kill switch

0.33 / 1.00

Every kubectl and helm call has a default 1 MB output cap, and exec_in_pod, Helm and node operations have timeouts. But most kubectl calls (get, apply, delete, generic, logs with follow) have no timeout and run synchronously, the model can raise exec_in_pod and rollout timeouts itself, and there are no rate limits. Port-forward processes the server spawns are not stopped by cleanup or on shutdown.