Skip to main content
Glama
gaelsg

k8s-mcp-server

by gaelsg

k8s-mcp-server

Servidor MCP de solo lectura sobre un cluster Kubernetes (k3s) propio. Idea 6 (última) del roadmap big-tech: cierra el círculo conectando todo lo construido en las ideas anteriores — el cluster corre en un LXC provisionado por proxmox-iac, su credencial vive en vault-secrets, y sus tools quedan disponibles para el Diagnostician de devops-multiagent.

Arquitectura

  • Cluster: k3s v1.36.4+k3s1, un solo nodo, LXC dedicado (192.168.8.92, provisionado con OpenTofu). Ver proxmox-iac/environments/k3s/.

  • Credencial: no es el kubeconfig admin que genera k3s por defecto. Es un kubeconfig separado, de solo lectura, para una ServiceAccount propia (mcp-agent, namespace kube-system) atada al ClusterRole built-in view + un ClusterRole custom acotado a nodes (view no cubre recursos a nivel de cluster). El kubeconfig admin nunca salió del nodo.

  • Secretos: el kubeconfig de solo lectura vive en Vault (secret/k8s-mcp-server), igual que el resto del roadmap desde la Idea 2 — secrets_loader.py hace login AppRole y lo escribe a un archivo temporal (0600) antes de que el cliente de Kubernetes lo use.

  • Cliente: librería oficial kubernetes (Python), no kubectl shelleado — mismo criterio que proxmoxer en proxmox-mcp-server.

Related MCP server: Kubernetes Read Only MCP Server

Tools expuestas (todas de solo lectura, sin guardrail en código — no hace falta, no existen tools de escritura)

  • list_k8s_nodes()

  • list_k8s_pods(namespace=None)

  • list_k8s_deployments(namespace=None)

  • list_k8s_services(namespace=None)

  • get_k8s_pod_logs(namespace, pod_name, tail_lines=50)

Prefijo k8s_ deliberado — mismo criterio que docker_tools.py en proxmox-mcp-server (list_docker_containers en vez de list_containers): evita que un agente con varios servidores MCP conectados (Proxmox tiene su propio list_nodes) no sepa cuál tool corresponde a qué sistema.

Setup

uv sync
cp .env.example .env
# completar VAULT_ROLE_ID/VAULT_SECRET_ID (ver vault-secrets/scripts/onboard-k8s-mcp-server.sh)

Registro en Claude Code:

claude mcp add k8s --scope user -- uv run --directory ~/projects/k8s-mcp-server k8s-mcp-server

Ejecutar

uv run k8s-mcp-server

Tracing

Con OTEL_EXPORTER_OTLP_ENDPOINT seteado (ver .env.example), cada tools/call queda como un span en Jaeger, correlacionado con el trace del agente que lo llamó. Ver devops-multiagent para el detalle completo.

Supply chain (build, scan, SBOM, firma)

.github/workflows/supply-chain.yml — en cada push a master: Trivy sobre uv.lock (dependencias), build con docker/build-push-action, push a ghcr.io/gaelsg/k8s-mcp-server, Trivy sobre la imagen construida, SBOM (CycloneDX, via syft), firma keyless con cosign (OIDC de GitHub, sin llave privada), y attestation del SBOM. Corre en un runner GitHub-hosted, no el self-hosted de devops-multiagent — build/scan/firma no necesitan alcanzar la red del homelab, y separarlo de un runner persistente reduce superficie. Todas las actions de terceros fijadas por SHA de commit, no por tag mutable.

cosign verify \
  --certificate-identity-regexp "https://github.com/gaelsg/k8s-mcp-server/.github/workflows/supply-chain.yml.*" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/gaelsg/k8s-mcp-server:latest

Encontrado y corregido durante la implementación (no solo teoría): Trivy detectó msgpack/setuptools con CVEs HIGH reales en la imagen — venían empaquetados dentro de pip mismo (nunca usados por el proyecto), resuelto sacando pip de la imagen final. Una corrida posterior en el runner de CI (base image recién pulleada, distinta al cache local) encontró un CVE HIGH real en openssl/libssl3t64 con fix disponible, no visible en el build local — resuelto con apt-get upgrade en el stage final.

Notas / gotchas reales encontrados

  • load_kube_config() del cliente oficial de Kubernetes no lee la variable KUBECONFIG sola (a diferencia de kubectl) — hay que pasarle config_file= explícito.

  • read_namespaced_pod_log() tiene un bug conocido: deserializa la respuesta como el repr() de bytes ("b'...'") en vez de decodificarla. Se evita pidiendo la respuesta cruda (_preload_content=False) y decodificando a mano.

  • El ClusterRole built-in view no incluye recursos a nivel de cluster (nodes, namespaces, etc.) — solo recursos namespaced. Hizo falta un ClusterRole adicional acotado (get/list/watch sobre nodes) para que list_k8s_nodes() funcione.

Detalle completo, incluyendo el incidente de keyctl durante el provisionamiento del LXC, en docs/29110/idea6-k8s/.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gaelsg/k8s-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server