This server runs locally (v1) and exposes Kubernetes operations to an AI assistant. The AI assistant is not trusted — we treat its tool calls as if they come from a well-intentioned but unpredictable user.
Risks we mitigate:
| Risk | Mitigation |
|---|---|
| Accidental destructive write | Writes off by default; --enable-writes flag |
Write to a sensitive namespace (e.g. kube-system) |
Optional --namespaces allowlist |
| LLM hallucinates a write and applies it | All writes default to dry_run=True |
| Secrets leak through tool responses | Tools never return Secret .data |
| kubeconfig contents leak via logs / error messages | kubeconfig content is never echoed |
| Server runs longer than intended with writes enabled | No runtime toggle — writes require restart |
| Lost audit trail | Every write emits a structured audit log line |
The server applies five layers of protection. A write tool only executes if every layer passes.
--enable-writesis required to enable write tools at all--namespaces ns1,ns2(optional) restricts every tool — read and write — to those namespaces
- Write tools are not registered with the MCP server unless
--enable-writeswas set - The LLM cannot call a tool that isn't registered — this is the strongest layer
- Every write tool re-checks the writes-enabled flag at call time
- If somehow registered when it shouldn't be (a bug), it still refuses
- All write tools accept
dry_run: bool = True - Dry-run uses the K8s API's native
dryRun=Allparameter — it validates without applying - The LLM must explicitly pass
dry_run=Falseto apply, which is visible in audit logs
- Every write attempt (dry-run or applied) logs to stderr with: timestamp, tool name, parameters, dry_run flag, result, calling user (kubeconfig context)
- v2: persist the audit log to a file or syslog
- Secrets: v1 ships no dedicated list/get tool for Secrets. The only way an LLM can reach a Secret is
describe_resource(kind="secret"), which returns onlyname, namespace, type, age, data_keys(key names) — never.dataor.stringDatavalues. - ConfigMaps: v1 ships no dedicated list/get tool for ConfigMaps.
describe_resource(kind="configmap")returns onlyname, namespace, age, keys(key names) — values are not returned in v1. (A future tool may add an opt-ininclude_dataparameter; out of v1 scope.) - kubeconfig: Never echoed in any tool response. Tools may return the current context name only.
- Log redaction: The audit logger redacts patterns matching
(?i)(token|secret|password|api[_-]?key|bearer)\s*[=:]\s*\S+before emitting.
- v1: Whatever kubeconfig the server starts with is what it uses. No re-auth, no impersonation.
- v2: ServiceAccount in-cluster, with a minimal Role / ClusterRole shipped as part of the Helm chart.
- Apply arbitrary YAML manifests
- Create or modify RBAC resources
- Read or write Secret
.datavalues - Switch kubeconfig contexts at runtime
- Write to namespaces outside the allowlist (when set)
- Persist audit data beyond the current process's stderr
For day-to-day debugging:
uvx --from kubernetes-mcp k8s-mcp-serverFor occasional, scoped operational work:
uvx --from kubernetes-mcp k8s-mcp-server --enable-writes --namespaces dev,stagingFor incident response in production: configure a separate, audited workflow. This tool is not intended as a primary production-write surface in v1.