Kubernetes & Helm MCP Server
A Model Context Protocol (MCP) server for managing Kubernetes clusters and Helm releases through any MCP-compatible client. Built in Go for low resource overhead, it supports read-only operation by default in Kubernetes, in-cluster or kubeconfig-based authentication, explicit capability gates for dangerous tools, HTTP bearer-token auth, and an autonomous AI-driven DevOps agent via opencode integration.
Table of Contents
- Features
- Architecture
- Quick Start
- Configuration
- Operational Safety Model
- DevOps Agent
- Agent Skills
- Tool Reference
- Deployment
- Security
- Development
- License
Features
Kubernetes Operations — read, write, and explicitly gated dangerous tools covering the full lifecycle of cluster resources:
Helm Operations — 9 tools for chart lifecycle management:
Platform — Flexible resource identifier resolution accepts PascalCase Kind (Deployment), lowercase plural (deployments), singular (deployment), or short names (deploy).
Architecture
src/
├── main.go # Server entry point, tool registration, transport setup
├── tools/
│ ├── k8s.go # Kubernetes MCP tool definitions (schemas)
│ ├── helm.go # Helm MCP tool definitions (schemas)
│ └── agent.go # DevOps agent tool definition
├── handlers/
│ ├── k8s.go # Kubernetes tool handlers (request → response)
│ ├── helm.go # Helm tool handlers (request → response)
│ └── agent.go # DevOps agent handler
└── pkg/
├── k8s/
│ └── client.go # Kubernetes client (dynamic, discovery, metrics, typed)
├── helm/
│ └── client.go # Helm client (install, upgrade, release management)
└── agent/
└── agent.go # OpenCode agent orchestration (subprocess, config, parsing)
The server uses a dynamic Kubernetes client with a cached GVR (GroupVersionResource) resolver backed by the discovery API, so it works with any resource type — including CRDs — without hardcoded mappings.
Quick Start
Prerequisites
- Go 1.24+ (for building from source)
- Access to a Kubernetes cluster with a valid kubeconfig
- metrics-server installed for CPU/memory metrics tools
Build from Source
cd src
go mod download
CGO_ENABLED=0 go build -ldflags="-w -s" -o k8s-mcp-server main.go
Run Locally
# SSE transport (default)
./k8s-mcp-server
# Streamable HTTP transport — endpoint: http://localhost:8080/mcp
./k8s-mcp-server --mode streamable-http
# Stdio transport (for CLI/editor integrations)
./k8s-mcp-server --mode stdio
# Read-only mode (disables all write operations)
./k8s-mcp-server --read-only
# Disable Helm tools
./k8s-mcp-server --no-helm
# Combine flags
./k8s-mcp-server --mode sse --port 9090 --read-only --no-helm
Docker
# Build
docker compose build
# Run with local kubeconfig in safe read-only HTTP mode
export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
docker run -p 8080:8080 \
-e SERVER_MODE=streamable-http \
-e SERVER_READ_ONLY=true \
-e MCP_REQUIRE_AUTH=true \
-e MCP_AUTH_TOKEN="$MCP_AUTH_TOKEN" \
-e KUBECONFIG=/home/appuser/.kube/config \
-v ~/.kube/config:/home/appuser/.kube/config:ro \
k8s-mcp-server:latest
Docker Compose
services:
k8s-mcp-server:
build: ./src
ports:
- "8080:8080"
volumes:
- ~/.kube:/home/appuser/.kube:ro
environment:
KUBECONFIG: /home/appuser/.kube/config
SERVER_MODE: streamable-http
SERVER_PORT: "8080"
SERVER_READ_ONLY: "true"
MCP_REQUIRE_AUTH: "true"
MCP_AUTH_TOKEN: ${MCP_AUTH_TOKEN:?set MCP_AUTH_TOKEN before starting}
restart: unless-stopped
Configuration
Command-Line Flags
--no-k8s and --no-helm cannot be used together.
HTTP Authentication
For HTTP transports, set MCP_AUTH_TOKEN to require Authorization: Bearer <token> or X-MCP-Token: <token> on MCP requests. /healthz remains unauthenticated for Kubernetes probes.
Set MCP_REQUIRE_AUTH=true to fail startup when an HTTP transport is selected without MCP_AUTH_TOKEN. The default Kubernetes manifest uses this mode.
DevOps Agent Environment Variables
These are optional. When all three are set, the devopsAgent tool is registered. In --read-only server mode, or when MCP_ENABLE_AGENT_WRITE is not enabled, the agent is still available but is forced into inspection-only mode.
Kubeconfig Resolution Order
- Explicit path via
KUBECONFIGenvironment variable - In-cluster ServiceAccount config (when running inside Kubernetes)
~/.kube/configfallback
Health Check
All HTTP modes expose GET /healthz which verifies connectivity to the Kubernetes API server. Returns 200 ok or 503 unhealthy: <error>.
Operational Safety Model
This server is treated as an agent-facing platform component. The hardening work makes the operational contract explicit: default deployments are read-only, HTTP access can require authentication, dangerous capabilities are opt-in, outputs are sanitized, and side effects are auditable.
Safe Defaults
- The Kubernetes deployment uses in-cluster ServiceAccount credentials instead of a mounted kubeconfig.
- The default manifest sets
SERVER_READ_ONLY=true, so write tools and Helm mutations are not registered. - The default manifest sets
MCP_REQUIRE_AUTH=trueand readsMCP_AUTH_TOKENfrom thek8s-mcp-authSecret. /healthzremains unauthenticated for probes; MCP HTTP traffic requiresAuthorization: Bearer <token>orX-MCP-Token: <token>whenMCP_AUTH_TOKENis set.
Capability Gates
- Normal write tools require non-read-only mode.
execInPodadditionally requiresMCP_ENABLE_EXEC=trueor--enable-exec.runKubectlCommandadditionally requiresMCP_ENABLE_KUBECTL=trueor--enable-kubectl.devopsAgentis inspection-only unless the parent server is write-enabled andMCP_ENABLE_AGENT_WRITE=true.- Child opencode MCP sessions inherit
MCP_ENABLE_EXECandMCP_ENABLE_KUBECTLfrom the parent environment when those gates are set. switchContextis not registered because process-global kubeconfig context switching is unsafe for concurrent HTTP MCP usage.
Audit Logging
Every registered tool goes through the audited registration wrapper in main.go. Tool calls log the tool name, capability class, status, duration, and error text when present. This gives operators a stable trail for read, write, destructive, exec, kubectl, log-reading, validation, Helm, and agent activity.
Output Safety
- JSON tool responses pass through the shared sanitizer before returning to clients.
- Pod logs, resource YAML, Helm release output, GPU/operator logs, kubectl output, and agent output are redacted for secret-like values.
- Kubernetes Secret
dataandstringDatavalues are redacted, including short base64 values. - Sensitive key names such as passwords, tokens, authorization headers, certificates, and private keys are redacted recursively in structured output.
- Large responses are truncated before they are returned to the agent.
Idempotent Apply Behavior
createResourceandcreateResourceYAMLuse Kubernetes server-side apply with field managerk8s-mcp-server.createResourceYAMLcan inferkindfrom the manifest when the caller omits it.- Apply handlers validate
apiVersion,kind, and metadata name before submitting side effects. - Dynamic Kubernetes list calls use paginated API reads to avoid oversized list responses.
Timeouts and Bounded Operations
- HTTP transports set read-header, read, idle, and graceful shutdown timeouts.
- Helm Kubernetes REST clients default to a 30-second timeout when no timeout is already configured.
runKubectlCommandanddevopsAgentuse explicit per-call execution timeouts.
Build Reproducibility
- The release Docker image bakes a curated DevOps-engineer skill set under
src/skills/into/home/appuser/.config/opencode/skills/. The set is scoped to Kubernetes monitoring/remediation, shell scripting, and Python/Rust code work; see Agent Skills for the full list. src/.dockerignoreexcludes build output, embedded.git, and.DS_Store. Thesrc/skills/tree is intentionally not excluded so that the audited skill set ships with the image.- Per-user or experimental skills can still be supplied without rebuilding by volume-mounting over
/home/appuser/.config/opencode/skills/at runtime.
Review Artifacts
The structured review deliverables live under docs/review/:
executive-summary.md— readiness assessment and remaining risks.risk-register.csv— decision-ready findings with severity, evidence, owner, effort, and status.architecture-themes.md— cross-cutting design themes and platform standards.remediation-roadmap.md— immediate, near-term, and strategic work.release-readiness-checklist.md— verification gates before broader reliance.
DevOps Agent
The devopsAgent tool launches an autonomous AI agent for Kubernetes cluster management. It uses opencode to run a headless agentic loop. By default the agent is inspection-only. If the parent server is not read-only and MCP_ENABLE_AGENT_WRITE=true is set, the agent can use write-capable k8s and Helm MCP tools to manage workloads autonomously, producing a structured report of actions taken and results.
How It Works
- You call
devopsAgentwith a natural language description of the task - The server spawns
opencode runas a headless subprocess - OpenCode connects to a child k8s-mcp-server (stdio,
--no-agent) for cluster access - The agent autonomously calls MCP tools to accomplish the task
- Returns a structured report: objective, actions taken, current state, issues found, next steps
Prerequisites
- opencode CLI installed (included in Docker image)
- An OpenAI-compatible LLM endpoint accessible from the server
- Environment variables set:
OPENCODE_BASE_URL,OPENCODE_API_KEY,OPENCODE_MODEL
Kubernetes Deployment
Create a Secret with your LLM provider credentials:
kubectl -n k8s-mcp-server create secret generic k8s-mcp-agent-config \
--from-literal=base-url="http://llm.local:8080/v1" \
--from-literal=api-key="sk-your-api-key" \
--from-literal=model="private-llm/llama-3.1-70b" \
--dry-run=client -o yaml | kubectl apply -f -
The deployment manifest references this Secret with optional: true, so the server works fine without it — the devopsAgent tool simply won't be registered.
Docker Compose
Set the env vars in your shell before running, or add them to a .env file:
export OPENCODE_BASE_URL="http://llm.local:8080/v1"
export OPENCODE_API_KEY="sk-your-api-key"
export OPENCODE_MODEL="private-llm/llama-3.1-70b"
docker compose up
Tool Parameters
Agent Skills
OpenCode skills are reusable instruction sets that extend the agent's behavior for specific domains — e.g., how to triage GPU workloads, how to interpret your team's alerting conventions, or domain-specific runbooks.
Bundled Skill Set
The Docker image ships with a curated set of skills scoped to a DevOps-engineer workload: Kubernetes cluster monitoring and remediation, shell scripting, Python/Rust code work, and the NVIDIA NIM/RAG stack this server is typically deployed alongside. The bundled set covers:
- Core DevOps / SRE —
devops-engineer,kubernetes-specialist,sre-engineer,monitoring-expert,chaos-engineer,terraform-engineer,mcp-developer - Languages —
python-pro,rust-engineer,golang-pro - Debugging / quality —
debugging-wizard,code-reviewer,code-documenter,test-master,cli-developer,the-fool - Security —
secure-code-guardian,security-best-practices,security-reviewer,security-threat-model - Data / RAG —
database-optimizer,postgres-pro,sql-pro,pandas-pro,ml-pipeline,rag-architect,jupyter-notebook,prompt-engineer - NVIDIA stack —
nat-*(NeMo Agent Toolkit: agent configuration, evaluation, installation, MCP+serving, optimization, path-checks, telemetry, tools/functions, user-rules, workflow-creation),nvcf-ngc-cli-skill,nvcf-self-managed-cli,nvcf-self-managed-installation,nv-html - Workflow / meta —
gh-address-comments,gh-fix-ci,define-goal,skill-creator,skill-evolution
The runtime image installs the toolchain those skills assume — git, github-cli, python3, py3-yaml, and jq — alongside the existing helm, kubectl, bash, and curl. Skill helper scripts (e.g., skill-creator/scripts/quick_validate.py, gh-fix-ci/scripts/inspect_pr_checks.py) run directly in the container without extra setup.
Adding Skills
Each skill lives in its own subdirectory under src/skills/ and requires a SKILL.md file with YAML frontmatter:
src/skills/
└── my-skill/
└── SKILL.md
src/skills/my-skill/SKILL.md:
---
name: my-skill
description: Brief description of what this skill teaches the agent
---
Your skill instructions here. Describe domain knowledge, investigation strategies,
or specialized runbooks that the agent should follow when relevant.
Frontmatter fields:
The name must match its parent directory name exactly and follow the pattern ^[a-z0-9]+(-[a-z0-9]+)*$.
How Skills Are Loaded
Docker / Kubernetes: The standard image bakes the curated skill set under src/skills/ into /home/appuser/.config/opencode/skills/ during build. The set is audited, scoped to the DevOps-engineer role, and ships with the runtime tools each skill assumes (see Bundled Skill Set). To replace or extend the baked set without rebuilding, mount over the same container path at runtime.
Runtime mount (no rebuild required): Override or supplement the bundled skills by volume-mounting on top of the container path:
# docker-compose.yaml
volumes:
- ./my-skills:/home/appuser/.config/opencode/skills:ro
Local development (without Docker): Place skills in the global opencode config directory:
~/.config/opencode/skills/<name>/SKILL.md
Tool Reference
Kubernetes — Read-Only Tools (31)
Discovery & Core
Monitoring & Observability
Debugging & Troubleshooting
GPU Debugging
Kubernetes — Write Tools
Disabled when --read-only is set. execInPod and runKubectlCommand also require explicit dangerous capability gates.
switchContext is intentionally not registered. Process-global context switching is unsafe for concurrent HTTP MCP usage; run separate server instances or use a request/session-scoped multi-cluster model instead.
Agent Tool
Helm — Read-Only Tools (4)
Helm — Write Tools (5)
Disabled when --read-only is set.
* = required parameter
Resource Identifier Flexibility
The kind parameter across all tools accepts multiple formats. All of these resolve to the same resource:
This works for any resource type, including CRDs.
Deployment
Kubernetes In-Cluster
The included manifest at deploy/k8s-mcp-server.yaml provides a safe-by-default in-cluster deployment. It uses the pod ServiceAccount for Kubernetes access, runs in read-only mode, and requires an MCP bearer token for HTTP MCP requests.
# Create the namespace first so the auth secret can be created.
kubectl create namespace k8s-mcp-server --dry-run=client -o yaml | kubectl apply -f -
# Create the HTTP MCP auth token secret.
kubectl -n k8s-mcp-server create secret generic k8s-mcp-auth \
--from-literal=token="$(openssl rand -hex 32)" \
--dry-run=client -o yaml | kubectl apply -f -
# Apply RBAC, DaemonSet, NetworkPolicy, and Service.
kubectl apply -f deploy/k8s-mcp-server.yaml
The manifest includes:
- Dedicated
k8s-mcp-servernamespace - ServiceAccount with a read-only ClusterRole covering all standard API groups
- DaemonSet with liveness/readiness probes on
/healthz SERVER_READ_ONLY=trueandMCP_REQUIRE_AUTH=true- Hardened security context (non-root, read-only root filesystem, all capabilities dropped)
- Resource requests/limits (4–8 CPU, 4Gi–16Gi memory)
- ClusterIP Service on port 8080
Automated Rebuild & Deploy
./deploy.sh
This script builds the Docker image, pushes it, applies the Kubernetes manifests, syncs the MCP auth secret from MCP_AUTH_TOKEN, and triggers a rolling restart.
MCP Client Configuration
For MCP clients that connect via streamable HTTP (e.g., in-cluster agents):
http://k8s-mcp-server.k8s-mcp-server.svc.cluster.local:8080/mcp
Include the auth token as Authorization: Bearer <token> or X-MCP-Token: <token> for HTTP transports.
Cursor / VS Code
Add to your editor's MCP settings (settings.json):
{
"mcp.mcpServers": {
"k8s-mcp-server": {
"command": "k8s-mcp-server",
"args": ["--mode", "stdio"],
"env": {
"KUBECONFIG": "${env:HOME}/.kube/config"
}
}
}
}
For read-only mode, add "--read-only" to the args array.
Security
Container Hardening
The Docker image runs with a minimal attack surface:
- Non-root user (
appuser, UID 1001) - Read-only root filesystem
- All capabilities dropped
- Minimal base image — Alpine 3.21 with
helmv3.17.3 (SHA256-pinned),kubectlv1.34.1 (SHA256-pinned), the opencode/Bun runtime, and the DevOps-skill toolchain (git,github-cli,python3,py3-yaml,jq,bash,curl) - No privilege escalation allowed
Read-Only Mode
Use --read-only or SERVER_READ_ONLY=true to prevent cluster state changes through this server. This disables Kubernetes and Helm write tools and forces devopsAgent into inspection-only mode. execInPod and runKubectlCommand are additionally disabled unless their explicit capability gates are enabled.
The default Kubernetes manifest sets SERVER_READ_ONLY=true and uses read-only ServiceAccount RBAC.
RBAC
The included ClusterRole (k8s-mcp-server-readonly) grants read access to standard Kubernetes API groups:
- Core — pods, services, configmaps, secrets, namespaces, nodes, events, endpoints, PVs, PVCs, resource quotas, limit ranges, service accounts
- apps — deployments, replicasets, statefulsets, daemonsets
- batch — jobs, cronjobs
- networking.k8s.io — ingresses, network policies, ingress classes
- storage.k8s.io — storage classes, volume attachments
- rbac.authorization.k8s.io — roles, rolebindings, clusterroles, clusterrolebindings
- autoscaling — horizontal pod autoscalers
- policy — pod disruption budgets
- metrics.k8s.io — pod and node metrics
For write operations, create a separate ClusterRole with the necessary verbs and bind it to the ServiceAccount.
Development
Project Structure
├── .github/workflows/ # CI pipeline (build, test, vet, lint)
├── deploy/ # Kubernetes deployment manifests
├── docs/review/ # Review findings, roadmap, and release gates
├── scripts/ # Git hooks
├── src/
│ ├── main.go # Entry point, tool registration
│ ├── Dockerfile # Multi-stage build
│ ├── .dockerignore # Reproducible Docker build exclusions
│ ├── .golangci.yml # Linter configuration
│ ├── tools/ # MCP tool schema definitions
│ ├── handlers/ # Tool request handlers
│ └── pkg/ # Client libraries (k8s, helm, agent)
├── docker-compose.yaml # Local development
├── Makefile # Build, test, lint, format, deploy
├── deploy.sh # Build + deploy script
└── redeploy.sh # Backward-compatible wrapper for deploy.sh
Makefile Targets
Getting Started
# Install the pre-commit hook (runs fmt-check, vet, and tests before each commit)
make hooks
# Run the full check suite locally (same checks as CI)
make check
Verification
The hardening pass was verified with:
make testmake checkdocker compose configkubectl apply --dry-run=client -f deploy/k8s-mcp-server.yamldocker compose build
Test coverage was added for auth environment parsing, HTTP auth decisions, recursive output sanitization, safe handler marshaling, and agent capability-gate propagation.
Adding a New Tool
- Define the schema in
tools/k8s.go(ortools/helm.go):
func MyNewTool() mcp.Tool {
return mcp.NewTool("myNewTool",
mcp.WithDescription("What this tool does"),
mcp.WithString("param", mcp.Required(), mcp.Description("...")),
)
}
-
Implement the client method in
pkg/k8s/client.go(orpkg/helm/client.go) -
Create the handler in
handlers/k8s.go(orhandlers/helm.go) -
Register in
main.gothrough the audited wrapper:
addAuditedTool(s, tools.MyNewTool(), "read", handlers.MyNewHandler(client))
Use the correct capability string for the tool, such as read, read/logs, write, write/destructive, exec, kubectl, or agent. Kubernetes handlers that return cluster data should use the existing marshalSafe path or k8s.SanitizeText so large responses and secrets are handled consistently.
Key Dependencies
License
This project is licensed under the MIT License. See the LICENSE file for details.