kubernetools MCP Server

official

Help AI agents write accurate, up-to-date Kubernetes manifests by giving them the official Kubernetes API reference, so they can look up kinds, fields, and nested types with current specs across the latest and three previous Kubernetes versions

Documentation

kubernetools/mcp-server

Container image for the kubernetools MCP server.

Registry: ghcr.io/kubernetools/mcp-server

Quick start

Anonymous mode (local use)

No authentication required; all connections are rate-limited per source IP at the free-tier limit.

podman run -d \
  -p 3000:3000 \
  -e K8S_VERSIONS=v1.33 \
  ghcr.io/kubernetools/mcp-server:latest

With API key authentication

# 1. Create a key store file
echo '[{"key":"mykey","tier":"free"}]' > keys.json

# 2. Start the server
podman run -d \
  -p 3000:3000 \
  -v "$(pwd)/keys.json:/keys.json:ro" \
  -e K8S_VERSIONS=v1.33,v1.34,v1.35,v1.36 \
  -e KEY_STORE_PATH=/keys.json \
  ghcr.io/kubernetools/mcp-server:latest

Configuration

ParameterEnv varDescription
Kubernetes versionsK8S_VERSIONSComma-separated list of versions to pre-load (e.g. v1.33,v1.34). Defaults to v1.33.
GitHub tokenGITHUB_TOKENPersonal access token (no extra scopes needed). Optional; raises the GitHub API rate limit from 60 to 5 000 req/h — recommended when loading multiple versions.
Key storeKEY_STORE_PATHPath to the API key JSON file (mount it into the container). When omitted, the server runs in anonymous mode: no auth is required and all connections are rate-limited per source IP at the free-tier limit.
Allowed hostsALLOWED_HOSTSComma-separated Host header values to accept (e.g. mcp.example.com,mcp.example.com:443). When omitted, host validation is disabled — suitable for local development only. Always set this in production to prevent DNS-rebinding attacks.
Browser redirectBROWSER_REDIRECT_URLURL to redirect plain browser GET requests to. MCP clients are detected by Accept: text/event-stream; browsers receive 307 to this URL instead. When omitted, browser GETs return 400.
Log levelRUST_LOGLog level filter (e.g. info, debug, mcp=debug).
Port--publishThe server listens on port 3000.

Authentication

API key store

The key store is a flat JSON array loaded once at startup:

[
  { "key": "free-key-abc", "tier": "free" },
  { "key": "paid-key-xyz", "tier": "paid" }
]

Every request must include the key in the Authorization header:

Authorization: Bearer free-key-abc

Requests without a valid key receive 401 Unauthorized.

Anonymous mode

When KEY_STORE_PATH is not set, the server runs without authentication. All connections are accepted and rate-limited per source IP at the free-tier limit. Convenient for local use; do not expose publicly.

Tiers and rate limits

TierLimit
free10 requests / minute, burst 10
paid~1 000 requests / second, burst 1 000 (effectively unlimited)

Requests that exceed the limit receive 429 Too Many Requests. Limits are tracked per API key, not per IP.

Connecting an MCP client

The server implements the MCP Streamable HTTP transport. The endpoint is:

http://<host>:<port>/[?version=<k8s-version>]

The version query parameter selects which Kubernetes version to use for the session. If omitted, the first loaded version is used. An unknown version returns 400 Bad Request.

Claude Desktop

Add the following to claude_desktop_config.json:

{
  "mcpServers": {
    "kubernetools": {
      "url": "http://localhost:3000/?version=v1.36",
      "headers": {
        "Authorization": "Bearer mykey"
      }
    }
  }
}

Available tools

list_resources

Lightweight discovery — returns one entry per resource, sorted by (group, kind, api_version). Use this first to find kind names.

Optional filters: group (e.g. "apps", "core"), api_version (e.g. "v1").

get_resource

Full resource detail — fields, spec, status, and list fields — enough to write a manifest in one call. Required: kind. Optional: group, api_version (defaults to most recent).

Fields with a non-null type_ref and empty sub_fields should be drilled into with get_type.

get_type

Drill into a single composite type referenced via type_ref in get_resource output. Required: type_name (e.g. "Container", "PodFailurePolicy").

Typical query flow

list_resources                          → discover kind names and groups
  └─ get_resource(kind="Deployment")   → see all top-level fields + spec/status
       └─ get_type(type_name="...")    → drill into any complex type_ref

Health check

GET /healthz on port 3000.

  • Returns 503 Service Unavailable (body: loading) while Kubernetes API docs are loading.
  • Returns 200 OK (body: ok) once the server is ready.

This endpoint bypasses authentication and rate limiting.

Use it for startup, readiness, and liveness probes:

startupProbe:
  httpGet:
    path: /healthz
    port: 3000
  failureThreshold: 30   # allow up to 5 min for version loading
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /healthz
    port: 3000
livenessProbe:
  httpGet:
    path: /healthz
    port: 3000
  initialDelaySeconds: 10

Error responses

HTTP statusCause
307 Temporary RedirectBrowser GET when BROWSER_REDIRECT_URL is set
400 Bad RequestBrowser GET when BROWSER_REDIRECT_URL is not set, or version parameter names a version not loaded at startup
401 UnauthorizedMissing or invalid Authorization: Bearer <key> header
429 Too Many RequestsRate limit exceeded for the key's tier

MCP-level errors (unknown tool name, missing required argument, kind not found) are returned as MCP error content inside a normal 200 response.

Examples

Multiple Kubernetes versions

podman run -d \
  -p 3000:3000 \
  -v "$(pwd)/keys.json:/keys.json:ro" \
  -e K8S_VERSIONS=v1.33,v1.34,v1.35,v1.36 \
  -e GITHUB_TOKEN=ghp_... \
  -e KEY_STORE_PATH=/keys.json \
  ghcr.io/kubernetools/mcp-server:latest

Production setup with host validation

podman run -d \
  -p 3000:3000 \
  -v "$(pwd)/keys.json:/keys.json:ro" \
  -e K8S_VERSIONS=v1.36 \
  -e KEY_STORE_PATH=/keys.json \
  -e ALLOWED_HOSTS=mcp.example.com,mcp.example.com:443 \
  -e BROWSER_REDIRECT_URL=https://example.com/docs \
  ghcr.io/kubernetools/mcp-server:latest

Debug logging

podman run -d \
  -p 3000:3000 \
  -e K8S_VERSIONS=v1.33 \
  -e RUST_LOG=debug \
  ghcr.io/kubernetools/mcp-server:latest

Pin to a specific release

podman run -d \
  -p 3000:3000 \
  -e K8S_VERSIONS=v1.33 \
  ghcr.io/kubernetools/mcp-server:0.1.0

Image

Built on registry.access.redhat.com/hi/core-runtime:2.42-openssl — a minimal, distroless glibc + OpenSSL runtime. No shell or package manager.