Skip to main content
Glama

Table of Contents

Related MCP server: Kube MCP

Why helm-mcp?

  • 46 MCP tools covering every Helm CLI command (minus shell completion and help)

  • Dual Helm SDK support — Helm v3 and v4 via native Go SDK (not CLI wrappers)

  • Two transport modes — stdio (default) and Streamable HTTP, stateless by default

  • Cloud provider ready — EKS, GKE, AKS kubeconfig formats work out of the box

  • Security first — Linux process hardening, credential memory zeroing, input validation, path traversal prevention

  • Python wrapper — FastMCP-based proxy that auto-discovers all tools

  • Forward proxy support — respects HTTP_PROXY, HTTPS_PROXY, NO_PROXY

Installation

Pre-built Binaries

Download the latest release for your platform from GitHub Releases:

# macOS (Apple Silicon)
curl -LO https://github.com/SCGIS-Wales/helm-mcp/releases/latest/download/helm-mcp-darwin-arm64
chmod +x helm-mcp-darwin-arm64
sudo mv helm-mcp-darwin-arm64 /usr/local/bin/helm-mcp

# macOS (Intel)
curl -LO https://github.com/SCGIS-Wales/helm-mcp/releases/latest/download/helm-mcp-darwin-amd64
chmod +x helm-mcp-darwin-amd64
sudo mv helm-mcp-darwin-amd64 /usr/local/bin/helm-mcp

# Linux (amd64)
curl -LO https://github.com/SCGIS-Wales/helm-mcp/releases/latest/download/helm-mcp-linux-amd64
chmod +x helm-mcp-linux-amd64
sudo mv helm-mcp-linux-amd64 /usr/local/bin/helm-mcp

# Linux (arm64)
curl -LO https://github.com/SCGIS-Wales/helm-mcp/releases/latest/download/helm-mcp-linux-arm64
chmod +x helm-mcp-linux-arm64
sudo mv helm-mcp-linux-arm64 /usr/local/bin/helm-mcp

Build from Source

Requires Go 1.27+.

git clone https://github.com/SCGIS-Wales/helm-mcp.git
cd helm-mcp
make build

Docker

docker build -t helm-mcp .
docker run -v ~/.kube:/home/helmuser/.kube:ro helm-mcp --mode stdio

Python Package

pip install helm-mcp

See Python Package below for full details.

Quick Start

stdio mode (for Claude Code, Cursor, etc.)

helm-mcp --mode stdio

HTTP mode (Streamable HTTP)

helm-mcp --mode http

HTTP mode listens on 127.0.0.1:8080 by default. To accept remote clients, configure authentication (see Authentication) and bind explicitly, for example --addr 0.0.0.0:8080.

HTTP mode is stateless by default (MCP 2026-07-28): there is no initialize handshake and no Mcp-Session-Id header, so the server can sit behind a plain round-robin load balancer with no sticky sessions and no shared session store. Clients speaking an older protocol revision still work — the SDK negotiates down automatically.

If a client needs the legacy per-session behaviour:

helm-mcp --mode http --stateless=false

Removed in v0.2.0: --mode sse. The HTTP+SSE transport has been deprecated by the MCP specification since 2025-03-26 and is formally Deprecated under the 2026-07-28 feature lifecycle policy. Use --mode http, which serves Streamable HTTP on the same address.

MCP Client Configuration

Claude Desktop

Add to ~/.claude/claude_desktop_config.json:

{
  "mcpServers": {
    "helm": {
      "command": "helm-mcp",
      "args": ["--mode", "stdio"]
    }
  }
}

Claude Code

claude mcp add helm -- helm-mcp --mode stdio

Cursor / Windsurf / VS Code

Add to your MCP server configuration:

{
  "helm-mcp": {
    "command": "helm-mcp",
    "args": ["--mode", "stdio"]
  }
}

Remote / HTTP Clients

Start the server in HTTP mode, then connect any MCP-compatible client to the endpoint:

helm-mcp --mode http
# MCP endpoint: http://localhost:8080/mcp

MCP Protocol Support

helm-mcp implements MCP 2026-07-28 and negotiates down to earlier revisions (2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05) for older clients, so upgrading the server does not require upgrading your client.

Feature

Status

Tools

Supported — 46 tools, with titles, annotations and output schemas

Stateless transport

Supported and the default for --mode http (--stateless=false to opt out)

server/discover

Supported

Deterministic tools/list order

Supported — tools are returned sorted by name so clients and prompt caches keep hitting

Server instructions

Supported — the server explains version selection, cluster targeting and destructive-tool safety

Resources / Prompts

Not applicable — every Helm operation is a tool call

Tasks extension

Not implemented. Attractive for long helm install --wait runs, but client support is still thin. Tracked as a follow-up.

MRTR / Elicitation

Not implemented, deliberately. Helm tools take all their input as arguments, so there is no mid-call question to ask.

Roots / Sampling / Logging

Not implemented. All three are deprecated as of 2026-07-28 (SEP-2577).

HTTP+SSE transport

Removed in v0.2.0 — deprecated by the specification. Use --mode http.

Available Tools (46)

Release Management (14)

Tool

Description

helm_install

Install a Helm chart as a new release

helm_upgrade

Upgrade a release to a new chart version or values

helm_uninstall

Uninstall a release and remove associated resources

helm_rollback

Rollback a release to a previous revision

helm_list

List releases (supports filters, sorting, pagination)

helm_status

Display release status, revision, chart, and values

helm_history

Show revision history of a release

helm_test

Run the test suite for a release

helm_get_all

Get all info (values, manifest, hooks, notes) for a release

helm_get_hooks

Get hooks for a release

helm_get_manifest

Get the Kubernetes manifest for a release

helm_get_metadata

Get metadata for a release

helm_get_notes

Get notes for a release

helm_get_values

Get values for a release (user-supplied or computed)

Chart Management (14)

Tool

Description

helm_create

Create a new chart with the given name

helm_lint

Lint a chart for issues and best practices

helm_template

Render templates locally without installing

helm_package

Package a chart directory into an archive (.tgz)

helm_pull

Download a chart from a repository or OCI registry

helm_push

Push a chart archive to an OCI registry

helm_verify

Verify a chart has a valid provenance file

helm_show_all

Show all chart info (Chart.yaml, values, README, CRDs)

helm_show_chart

Show Chart.yaml of a chart

helm_show_crds

Show CRDs of a chart

helm_show_readme

Show README of a chart

helm_show_values

Show default values of a chart

helm_dependency_build

Build charts/ directory from Chart.lock

helm_dependency_list

List dependencies for a chart

Repository Management (5)

Tool

Description

helm_repo_add

Add a chart repository

helm_repo_list

List configured chart repositories

helm_repo_update

Update chart repository indexes

helm_repo_remove

Remove chart repositories

helm_repo_index

Generate an index file for chart archives

Registry / OCI (2)

Tool

Description

helm_registry_login

Login to an OCI registry

helm_registry_logout

Logout from an OCI registry

Search (2)

Tool

Description

helm_search_hub

Search Artifact Hub for charts

helm_search_repo

Search locally configured repositories

Plugin Management (6)

Tool

Description

helm_plugin_install

Install a Helm plugin

helm_plugin_list

List installed plugins

helm_plugin_uninstall

Uninstall a plugin

helm_plugin_update

Update a plugin

helm_plugin_package

Package a plugin directory into a signed archive (v4 only)

helm_plugin_verify

Verify a packaged plugin's signature and provenance (v4 only)

Environment (2)

Tool

Description

helm_env

Print Helm environment information

helm_version

Print Helm SDK version information

Dependency Update (1)

Tool

Description

helm_dependency_update

Update charts/ based on Chart.yaml

Tool Annotations

Every tool carries MCP annotations so a client — or an agent deciding what to call — can tell a read at a glance from a change to your cluster.

Annotation

Meaning

Tools

readOnlyHint: true

Only reads state; safe to call freely

helm_list, helm_status, helm_history, all helm_get_*, all helm_show_*, helm_repo_list, helm_plugin_list, helm_dependency_list, helm_env, helm_version, helm_lint, helm_template, helm_verify, helm_plugin_verify, helm_search_*

destructiveHint: true

Can remove or replace live state — call only on explicit user intent

helm_uninstall, helm_rollback, helm_upgrade, helm_repo_remove, helm_plugin_uninstall

openWorldHint: true

Reaches beyond the configured cluster (Artifact Hub, OCI registries, chart repos)

helm_search_hub, helm_search_repo, helm_pull, helm_push, all helm_show_*, helm_template, helm_registry_*

idempotentHint: true

Repeating the call with the same arguments has no additional effect

all read-only tools, plus helm_repo_add, helm_repo_update, helm_dependency_*, helm_plugin_update

Everything not listed as destructive is annotated destructiveHint: false, which matters because the MCP specification defaults that hint to true.

Annotations are hints, not a security boundary — a client may ignore them. For a real boundary, use the OIDC scopes and roles described under Security.

Structured Output

Eleven tools publish an outputSchema and return structuredContent alongside the human-readable text, so clients can consume results without parsing prose:

helm_list, helm_status, helm_history, helm_get_metadata, helm_get_values, helm_repo_list, helm_search_repo, helm_search_hub, helm_plugin_list, helm_env, helm_version.

// helm_list
{
  "releases": [
    { "name": "my-app", "namespace": "default", "revision": 3, "status": "deployed", ... }
  ],
  "count": 1
}

The existing JSON text content is unchanged, so clients that parse it keep working. Collections are wrapped in an object with a count rather than returned as a bare array. Tools whose output is free-form YAML or text — helm_template, helm_lint, helm_get_manifest, helm_show_*, helm_package, helm_dependency_list — have no output schema, because there is no useful shape to declare.

Helm CLI Coverage

Complete mapping of every helm CLI command to its helm-mcp MCP tool equivalent.

Helm Command

MCP Tool

Status

helm create

helm_create

Covered

helm dependency build

helm_dependency_build

Covered

helm dependency list

helm_dependency_list

Covered

helm dependency update

helm_dependency_update

Covered

helm env

helm_env

Covered

helm get all

helm_get_all

Covered

helm get hooks

helm_get_hooks

Covered

helm get manifest

helm_get_manifest

Covered

helm get metadata

helm_get_metadata

Covered

helm get notes

helm_get_notes

Covered

helm get values

helm_get_values

Covered

helm history

helm_history

Covered

helm install

helm_install

Covered

helm lint

helm_lint

Covered

helm list

helm_list

Covered

helm package

helm_package

Covered

helm plugin install

helm_plugin_install

Covered

helm plugin list

helm_plugin_list

Covered

helm plugin uninstall

helm_plugin_uninstall

Covered

helm plugin package

helm_plugin_package

Covered (v4 only)

helm plugin update

helm_plugin_update

Covered

helm plugin verify

helm_plugin_verify

Covered (v4 only)

helm pull

helm_pull

Covered

helm push

helm_push

Covered

helm registry login

helm_registry_login

Covered

helm registry logout

helm_registry_logout

Covered

helm repo add

helm_repo_add

Covered

helm repo index

helm_repo_index

Covered

helm repo list

helm_repo_list

Covered

helm repo remove

helm_repo_remove

Covered

helm repo update

helm_repo_update

Covered

helm rollback

helm_rollback

Covered

helm search hub

helm_search_hub

Covered

helm search repo

helm_search_repo

Covered

helm show all

helm_show_all

Covered

helm show chart

helm_show_chart

Covered

helm show crds

helm_show_crds

Covered

helm show readme

helm_show_readme

Covered

helm show values

helm_show_values

Covered

helm status

helm_status

Covered

helm template

helm_template

Covered

helm test

helm_test

Covered

helm uninstall

helm_uninstall

Covered

helm upgrade

helm_upgrade

Covered

helm verify

helm_verify

Covered

helm version

helm_version

Covered

helm completion

—

Not applicable (shell utility)

helm help

—

Not applicable (shell utility)

46 of 46 operational Helm commands are covered. The only excluded commands (completion, help) are shell utilities that have no meaning in an MCP context.

helm plugin package and helm plugin verify were introduced in Helm v4 alongside signed plugin distribution, so they require helm_version: "v4".

Kubernetes Authentication

Every tool accepts these authentication fields via the GlobalInput:

Field

JSON Key

Description

Kubeconfig

kubeconfig

Path to kubeconfig file (defaults to $KUBECONFIG or ~/.kube/config)

Context

kube_context

Kubernetes context name to use

API Server

kube_apiserver

Override the API server URL from kubeconfig

Bearer Token

kube_token

Bearer token for API authentication

TLS Server Name

kube_tls_server_name

Server name for TLS certificate validation

Insecure TLS

kube_insecure_tls

Skip TLS certificate verification

Namespace

namespace

Target Kubernetes namespace

EKS (AWS)

EKS uses exec-based authentication in kubeconfig. The standard kubeconfig generated by aws eks update-kubeconfig works out of the box:

{
  "kubeconfig": "/home/user/.kube/config",
  "kube_context": "arn:aws:eks:us-east-1:123456789:cluster/my-cluster"
}

Or with direct token authentication:

{
  "kube_apiserver": "https://ABCDEF.gr7.us-east-1.eks.amazonaws.com",
  "kube_token": "<bearer-token-from-aws-eks-get-token>"
}

GKE (Google Cloud)

GKE kubeconfig generated by gcloud container clusters get-credentials works out of the box:

{
  "kubeconfig": "/home/user/.kube/config",
  "kube_context": "gke_my-project_us-central1_my-cluster"
}

AKS (Azure)

AKS kubeconfig generated by az aks get-credentials works out of the box:

{
  "kubeconfig": "/home/user/.kube/config",
  "kube_context": "my-aks-cluster"
}

Helm Version Selection

Every tool supports a helm_version field to select between Helm v3 and v4:

{
  "helm_version": "v4",
  "release_name": "my-release"
}
  • "v4" (default) — Uses Helm v4 SDK with Server-Side Apply, WASM plugins, label selectors

  • "v3" — Uses Helm v3 SDK for backward compatibility

v4-Only Features

These fields are only available when using helm_version: "v4". Passing one with "v3" is an error rather than a silent no-op.

  • server_side_apply — Use Kubernetes server-side apply

  • take_ownership — Skip Helm annotation checks

  • hide_secret — Hide secrets in dry-run output

  • force_conflicts — Force conflict resolution

  • selector — Label selector for list operations

  • show_resources — Show resources table in status

  • reset_then_reuse_values — Reset then reuse values in upgrade

  • wait_strategy — See below

Two tools are v4-only in their entirety: helm_plugin_package and helm_plugin_verify.

Waiting: wait and wait_strategy

Helm v4 replaced v3's boolean --wait with a strategy enum, so helm_install, helm_upgrade, helm_rollback and helm_uninstall accept a wait_strategy field alongside wait:

wait_strategy

Behaviour

"watcher"

Wait using kstatus — what helm --wait does in v4

"legacy"

Wait using the v3 readiness checks

"hookOnly"

Only wait for hooks; the CLI default when --wait is omitted

wait_strategy takes precedence when set. Otherwise wait: true maps to "watcher" and wait: false maps to "hookOnly".

Before v0.2.0 the v4 engine hardcoded the watcher strategy, so wait: false was silently ignored and every v4 operation waited. If you relied on that behaviour, set wait: true (or wait_strategy: "watcher") explicitly.

Available on both v3 and v4

rollback_on_failure (Helm's --atomic) is supported by both engines, and is available on helm_install and helm_upgrade. So are devel, sub_notes, hide_notes, skip_schema_validation, disable_openapi_validation and enable_dns; output_dir and use_release_name on helm_install; ignore_not_found and description on helm_uninstall; and all, uninstalling and time_format on helm_list.

Python Package

A Python wrapper is available that uses FastMCP to create a transparent proxy around the helm-mcp Go binary. New tools added to the Go binary are automatically available in Python without code changes.

Installation

pip install helm-mcp

Requires Python 3.12+. The Go binary is bundled inside platform-specific wheels — no Go toolchain is required. Supported platforms: linux-amd64, linux-arm64, darwin-amd64, darwin-arm64, windows-amd64. The binary is extracted from the wheel on first use, with SHA256 checksum verification to protect against tampering.

You can verify the binary is available:

helm-mcp-python --setup

Usage as a Server

from helm_mcp import create_server

# stdio mode (default, for MCP clients)
server = create_server()
server.run()

# HTTP mode
server = create_server()
server.run(transport="http", host="127.0.0.1", port=8080)

Usage as a Client

import asyncio
from helm_mcp import create_client

async def main():
    async with create_client() as client:
        # List all available tools
        tools = await client.list_tools()
        print(f"Available tools: {len(tools)}")

        # List Helm releases
        result = await client.call_tool("helm_list", {"namespace": "default"})
        print(result)

        # Install a chart
        result = await client.call_tool("helm_install", {
            "release_name": "my-app",
            "chart": "bitnami/nginx",
            "namespace": "default",
        })
        print(result)

asyncio.run(main())

CLI

# stdio mode (for MCP clients like Claude Code)
helm-mcp-python

# HTTP mode (loopback only by default; the Python proxy has no authentication)
helm-mcp-python --transport http --port 8080

# Bind to all interfaces only behind your own authenticating proxy
helm-mcp-python --transport http --host 0.0.0.0 --allow-remote

# Custom binary path
helm-mcp-python --binary /usr/local/bin/helm-mcp

Integrating with FastMCP

The Python package is built on FastMCP and returns standard FastMCP server/client objects. You can compose it with other FastMCP servers:

from fastmcp import FastMCP
from helm_mcp import create_server as create_helm_server

# Create a composite server
app = FastMCP("my-platform")

# Mount helm-mcp as a sub-server
helm = create_helm_server()
app.mount("helm", helm)

# Add your own tools alongside Helm
@app.tool()
def my_custom_tool(param: str) -> str:
    return f"Custom: {param}"

app.run()

Binary Discovery

The Python package locates the helm-mcp Go binary in this order:

  1. HELM_MCP_BINARY environment variable

  2. Bundled binary in the package bin/ directory

  3. Auto-download from GitHub Releases (with SHA256 checksum verification)

  4. helm-mcp on PATH

Environment Variables

The proxy forwards these environment variables to the Go subprocess:

Category

Variables

Proxy

HTTP_PROXY, HTTPS_PROXY, NO_PROXY (and lowercase variants)

Kubernetes

KUBECONFIG, KUBERNETES_SERVICE_HOST, KUBERNETES_SERVICE_PORT

Helm

HELM_CACHE_HOME, HELM_CONFIG_HOME, HELM_DATA_HOME, HELM_PLUGINS, HELM_DEBUG

AWS

AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_REGION, AWS_PROFILE

GCP

GOOGLE_APPLICATION_CREDENTIALS, CLOUDSDK_COMPUTE_ZONE

Azure

AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID

TLS

SSL_CERT_FILE, SSL_CERT_DIR

Resilience Configuration (Python)

The Python package includes a comprehensive resilience stack built on FastMCP middleware, circuitbreaker, and tenacity. All settings are configurable via HELM_MCP_* environment variables with sensible defaults.

Environment Variables

Variable

Type

Default

Description

HELM_MCP_RETRY_ENABLED

bool

true

Enable proxy-level retry middleware

HELM_MCP_RETRY_MAX_RETRIES

int

2

Maximum retry attempts

HELM_MCP_RETRY_BASE_DELAY

float

1.0

Initial backoff delay (seconds)

HELM_MCP_RETRY_MAX_DELAY

float

30.0

Maximum backoff delay (seconds)

HELM_MCP_RETRY_BACKOFF_MULTIPLIER

float

2.0

Backoff multiplier

HELM_MCP_RATE_LIMIT_ENABLED

bool

false

Enable token-bucket rate limiting

HELM_MCP_RATE_LIMIT_MAX_RPS

float

10.0

Maximum requests per second

HELM_MCP_RATE_LIMIT_BURST

int

20

Burst capacity

HELM_MCP_CACHE_ENABLED

bool

false

Enable TTL-based response caching

HELM_MCP_CACHE_TOOL_TTL

int

300

Tool call cache TTL (seconds)

HELM_MCP_CACHE_LIST_TTL

int

60

Tool list cache TTL (seconds)

HELM_MCP_ERROR_HANDLING_ENABLED

bool

true

Enable structured error responses

HELM_MCP_ERROR_INCLUDE_TRACEBACK

bool

false

Include tracebacks in errors

HELM_MCP_TIMING_ENABLED

bool

true

Enable request timing

HELM_MCP_TIMING_DETAILED

bool

false

Use detailed timing middleware

HELM_MCP_CIRCUIT_BREAKER_ENABLED

bool

true

Enable circuit breaker on tool calls

HELM_MCP_CIRCUIT_BREAKER_FAILURE_THRESHOLD

int

5

Failures before circuit opens

HELM_MCP_CIRCUIT_BREAKER_RESET_TIMEOUT

float

30.0

Seconds before half-open retry

HELM_MCP_TENACITY_ENABLED

bool

true

Enable tenacity retry with jitter

HELM_MCP_TENACITY_MAX_ATTEMPTS

int

3

Maximum retry attempts

HELM_MCP_TENACITY_MIN_WAIT

float

0.5

Minimum wait between retries (seconds)

HELM_MCP_TENACITY_MAX_WAIT

float

10.0

Maximum wait between retries (seconds)

HELM_MCP_TENACITY_MULTIPLIER

float

1.5

Exponential backoff base

HELM_MCP_BULKHEAD_ENABLED

bool

true

Enable concurrency limiter

HELM_MCP_BULKHEAD_MAX_CONCURRENT

int

10

Maximum concurrent tool calls

HELM_MCP_OTEL_ENABLED

bool

false

Enable OpenTelemetry tracing

HELM_MCP_OTEL_SERVICE_NAME

str

helm-mcp

OTel service name

HELM_MCP_OTEL_EXPORTER

str

console

OTel exporter (console or otlp)

CLI Flags

helm-mcp-python --no-retry                   # Disable proxy retry middleware
helm-mcp-python --rate-limit 50              # Enable rate limiting at 50 rps
helm-mcp-python --cache                      # Enable response caching
helm-mcp-python --no-circuit-breaker         # Disable circuit breaker
helm-mcp-python --bulkhead-max 5             # Limit to 5 concurrent tool calls
helm-mcp-python --otel                       # Enable OpenTelemetry tracing

Programmatic Configuration

from helm_mcp import create_server, HelmClient
from helm_mcp.resilience import (
    ResilienceConfig,
    RateLimitConfig,
    CircuitBreakerConfig,
    BulkheadConfig,
)

# Server with custom resilience
config = ResilienceConfig(
    rate_limit=RateLimitConfig(enabled=True, max_requests_per_second=50),
    circuit_breaker=CircuitBreakerConfig(failure_threshold=3),
    bulkhead=BulkheadConfig(max_concurrent=20),
)
server = create_server(resilience=config)

# Client with custom resilience
async with HelmClient(resilience=config) as helm:
    releases = await helm.list(namespace="default")

OpenTelemetry

FastMCP emits traces via the OpenTelemetry API. To receive actual trace data, install the SDK:

pip install helm-mcp[otel]

Then enable tracing:

export HELM_MCP_OTEL_ENABLED=true
export HELM_MCP_OTEL_EXPORTER=otlp        # or "console"
export HELM_MCP_OTEL_SERVICE_NAME=helm-mcp

Response Payload Management

Large Helm outputs (manifests, values, template renders) can overflow LLM context windows. helm-mcp includes two layers of response size management to prevent this.

Response Truncation

All tool responses are automatically truncated when they exceed a configurable size limit. The default is 256 KB (~64K tokens). Truncated responses include metadata indicating the original size and a suggestion to use more specific queries.

Configure the limit via CLI flag or environment variable:

# CLI flag (in bytes, 0 to disable)
helm-mcp --mode stdio --max-response-bytes 524288

# Environment variable
export HELM_MCP_MAX_RESPONSE_BYTES=524288
helm-mcp --mode stdio

The CLI flag takes precedence over the environment variable.

Manifest Sanitisation

Tools that return Kubernetes YAML (helm_get_manifest, helm_get_all, helm_get_hooks, helm_template) automatically strip noisy fields before returning results. This typically reduces manifest sizes by 40-60% without losing meaningful information.

Fields stripped:

  • metadata.managedFields — internal Kubernetes bookkeeping (often the largest single field)

  • kubectl.kubernetes.io/last-applied-configuration — redundant copy of the entire object

  • deployment.kubernetes.io/revision — internal controller annotation

  • control-plane.alpha.kubernetes.io/leader — leader election data

This sanitisation is always active and cannot be disabled, as these fields are never useful for LLM interactions. The original unsanitised data remains available through direct kubectl access.

Resilience Primitives

The internal/resilience package provides additional production resilience patterns:

Pattern

Description

Circuit breaker

Three-state (Closed/Open/HalfOpen) pattern to fail fast when backends are unavailable. Configurable failure threshold and recovery timeout.

Retry with backoff

Exponential backoff with jitter for transient failures. Context-aware cancellation and retryable error filtering.

Per-tool timeouts

Category-based default timeouts: query (30s), mutate (120s), chart (60s), repo (60s). Respects existing context deadlines.

Known Limitations

Plugin Verification Required (Helm v4 CLI)

All six plugin tools shell out to the system helm CLI, which is the only part of helm-mcp that does not go through the Helm Go SDK — Helm's plugin packages are internal and not importable. Helm v4 requires plugin source verification by default, and plugins that do not support it (like helm-diff) need --verify=false, which the MCP tools do not yet expose.

  • Workaround: install those plugins directly via helm plugin install <url> --verify=false

  • The published container image ships a pinned helm CLI, so plugin tools work there. If you run the binary directly, helm must be on PATH.

Security

Process Hardening (Linux)

When running on Linux, helm-mcp applies process-level hardening at startup to reduce the attack surface of the stdio transport. MCP servers running as IDE child processes inherit full user privileges — these mitigations limit what an attacker can do if the process is compromised.

Mechanism

What It Does

PR_SET_DUMPABLE(0)

Blocks ptrace attach, core dumps, and /proc/pid/mem reads. Prevents other processes from inspecting credentials in memory.

Capability dropping

Drops all Linux capabilities from the bounding set. No-op for non-root users (the common case), but protects against privilege escalation when running in misconfigured Docker/Kubernetes environments.

Credential memory zeroing

ZeroCredentials() is called via defer after every tool handler completes, overwriting bearer tokens and passwords in memory. This is defense-in-depth — Go strings are immutable and the GC may retain copies, but it reduces the credential lifetime in our code paths.

Hardening is best-effort and non-fatal — failures are logged (with --debug) but never crash the process. On non-Linux platforms (macOS, Windows), hardening is skipped with an informational log message.

# Verify hardening is active (Linux)
helm-mcp --mode stdio --debug 2>&1 | grep "security hardening"

# Disable for debugging (e.g., when using strace or delve)
helm-mcp --mode stdio --no-harden

Mechanisms Evaluated but Not Implemented

Mechanism

Why Skipped

Seccomp BPF

The server uses exec.CommandContext for plugins and network I/O for Kubernetes API and registries. The syscall surface is too wide to filter safely without breaking Helm SDK internals across kernel versions.

Namespace isolation

The process needs access to ~/.kube/config, cloud credential files, DNS, and network. Namespace isolation would break core functionality.

Cgroup resource limits

A 5-minute pluginExecTimeout already bounds runaway operations, and the IDE manages process lifecycle.

AppArmor/SELinux profiles

High maintenance burden for dynamic file paths. Better deployed as an external artifact, not embedded in the binary.

Credential Scrubbing

All error messages are automatically scrubbed to remove:

  • Bearer tokens (including EKS, GKE, and Azure JWT tokens)

  • Basic authentication credentials

  • URL-embedded passwords (https://user:password@host)

Input Validation

Every tool handler calls ValidateGlobalInput before executing, ensuring namespace and kubeconfig fields are validated on every request.

The security package provides validators for:

  • Release names (DNS-1123 compliant)

  • Namespace names

  • Kubeconfig file paths (path traversal prevention, symlink detection, sensitive path rejection — /etc/shadow, /proc/, /dev/, /sys/ are blocked)

  • URLs (scheme validation + SSRF protection: DNS resolution with private IP blocking for 0.0.0.0/8, 127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, IPv4-mapped addresses, NAT64, and IPv6 unspecified/loopback/link-local/unique-local ranges). Applied to repository URLs, URL chart references, remote values files, and OCI push targets

  • File paths (traversal prevention, symlink rejection including symlinked parent directories, sensitive path rejection). Applied to values files, keyrings, CA/cert/key files, passphrase files, and output directories

  • Repository names (alphanumeric, dots, dashes, underscores; prevents cache path traversal)

  • Timeout durations (max 24h)

  • Plugin names (alphanumeric + dashes/underscores, no leading dash to prevent argument injection)

File Permissions

  • Repository configuration files are written with 0600 (owner read/write only)

  • Config directories are created with 0700 (owner only)

HTTP Server Hardening

When running in HTTP mode:

  • Listens on 127.0.0.1:8080 unless --addr says otherwise

  • ReadHeaderTimeout: 10s and ReadTimeout: 30s — prevent slow client attacks

  • WriteTimeout: 30m (--write-timeout) — long enough for helm_install/helm_upgrade with wait; raise it if clients use longer Helm timeouts

  • IdleTimeout: 120s — reclaims idle connections

  • MaxHeaderBytes: 1MB — prevents header-based DoS

  • Graceful shutdown with 5-second timeout

Authentication (OIDC/OAuth2)

When running in HTTP mode, helm-mcp supports OAuth2/OIDC authentication with JWT validation, claims-based authorization, and structured audit logging. This aligns with the MCP Security Best Practices.

Authentication is fully opt-in. When no OIDC or token environment variables are set, the server runs without authentication (same as previous versions). Stdio mode is never affected by authentication configuration.

Quick Start — Entra ID (Azure AD / ADFS)

# Required: issuer and audience
export HELM_MCP_OIDC_ISSUER="https://login.microsoftonline.com/{tenant-id}/v2.0"
export HELM_MCP_OIDC_AUDIENCE="api://helm-mcp-server"

# Optional: restrict access by scopes, roles, or client app IDs
export HELM_MCP_REQUIRED_SCOPES="helm.read,helm.write"
export HELM_MCP_REQUIRED_ROLES="HelmOperator"
export HELM_MCP_ALLOWED_CLIENTS="client-app-id-1,client-app-id-2"

# Optional: explicit JWKS URL (auto-discovered from issuer if omitted)
export HELM_MCP_OIDC_JWKS_URL="https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys"

helm-mcp --mode http --addr 0.0.0.0:8080

Authentication events (auth_success, auth_failure) are written to stderr as JSON at Info level, independent of --debug.

Environment Variables

Variable

Required

Description

HELM_MCP_OIDC_ISSUER

Yes (for OIDC)

OIDC issuer URL. Token iss claim must match.

HELM_MCP_OIDC_AUDIENCE

Yes (for OIDC)

Expected aud claim. Tokens issued for other resources are rejected.

HELM_MCP_OIDC_JWKS_URL

No

JWKS endpoint for signature verification. Auto-discovered from issuer if omitted.

HELM_MCP_REQUIRED_SCOPES

No

Comma-separated OAuth2 scopes required in the scp claim.

HELM_MCP_REQUIRED_ROLES

No

Comma-separated app roles required in the roles claim.

HELM_MCP_ALLOWED_CLIENTS

No

Comma-separated client app IDs allowed in azp/appid claim.

HELM_MCP_SESSION_TTL

No

Session cache inactivity TTL (Go duration, e.g., 5m, 15m). Default: 5m.

HELM_MCP_AUTH_TOKEN

No

Static bearer token (legacy). Lower priority than OIDC.

Authentication Priority

  1. OIDC/OAuth2 — if HELM_MCP_OIDC_ISSUER is set, JWT validation with JWKS is enabled.

  2. Static bearer token — if only HELM_MCP_AUTH_TOKEN is set, constant-time comparison is used.

  3. No auth — if neither is set, the server accepts all requests (suitable for local stdio usage).

Token Validation

Every incoming JWT is validated for:

Check

Description

Signature

RSA signature verified against JWKS public keys (RS256/384/512). Keys are cached for 1 hour with automatic refresh on kid miss (key rotation).

Issuer (iss)

Must exactly match HELM_MCP_OIDC_ISSUER.

Audience (aud)

Must match HELM_MCP_OIDC_AUDIENCE. Tokens issued for other APIs are rejected — this is the core defense against token passthrough.

Expiry (exp)

Required. Expired tokens are rejected.

Authorized Party (azp/appid)

Checked against HELM_MCP_ALLOWED_CLIENTS if configured. Supports both OIDC azp and Entra ID v1 appid claims.

Scopes (scp)

Space-separated scopes checked against HELM_MCP_REQUIRED_SCOPES.

Roles (roles)

Array of app roles checked against HELM_MCP_REQUIRED_ROLES.

Session Cache

Validated tokens are cached in-memory to avoid redundant JWKS lookups:

  • Inactivity TTL: 5 minutes by default (configurable via HELM_MCP_SESSION_TTL, e.g., 5m, 10m, 1h)

  • Token expiry: Cached tokens are never used beyond their exp claim

  • Cache key: SHA-256 hash of the raw bearer token (prevents raw token storage in memory)

  • Sliding window: Each access resets the inactivity timer

  • Maximum entries: 10,000 (oldest evicted on overflow)

Audit Logging

When OIDC authentication is enabled, structured audit events are emitted via slog for every authentication attempt:

level=INFO msg=security_audit audit.event_type=auth_success audit.principal_id=oid-123 audit.principal_name=user@example.com audit.tenant_id=tenant-abc audit.client_app_id=client-1 audit.scopes="helm.read helm.write" audit.token_id=uti-xyz audit.remote_addr=10.0.0.1:54321

Audit events include: principal ID/name, tenant ID, client app ID, scopes, roles, token ID, session ID, action, resource, result, duration, and remote address. Enable --debug for full audit visibility, or configure your log aggregator to capture security_audit messages.

On-Behalf-Of (OBO) Token Exchange

When helm-mcp needs to call a downstream API (such as the Kubernetes API) on behalf of the authenticated user, it exchanges the incoming token for a new one scoped to that downstream service. This avoids forwarding the original token, which could be misused if the downstream service is compromised or if the token's audience doesn't match.

How It Works

User        MCP Client        helm-mcp (MCP Server)      Kubernetes API
 │              │                      │                        │
 ├─(SSO)──────▶│ gets token            │                        │
 │              │ aud=helm-mcp          │                        │
 │              ├─(Bearer token)──────▶│                        │
 │              │                      ├─OBO exchange──────────▶│
 │              │                      │ grant_type=jwt-bearer   │
 │              │                      │ assertion=user token    │
 │              │                      │ scope=K8s API scopes    │
 │              │                      │◀─new token──────────────│
 │              │                      │  aud=Kubernetes API     │
 │              │                      ├─(K8s API call)────────▶│
 │              │                      │  with OBO token         │

Each hop in the chain:

  1. Validates the incoming token's audience (must match this server)

  2. Exchanges it via OBO for a new token targeted at the next service

  3. Preserves the original user's identity in the new token's claims

  4. Triggers a fresh Conditional Access evaluation (if configured in Entra ID)

OBO Configuration

# OBO token exchange (for downstream API calls with user context)
export HELM_MCP_OBO_TOKEN_URL="https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token"
export HELM_MCP_OBO_CLIENT_ID="helm-mcp-app-id"
export HELM_MCP_OBO_CLIENT_SECRET="helm-mcp-client-secret"

Why Not Forward the Token?

Forwarding a user's token to downstream services is tempting but problematic. The MCP Security Best Practices explicitly discourages it. With OBO, each service gets a token minted for its own audience, scoped to only the permissions it needs. If a token is intercepted, the blast radius is limited to a single service rather than the entire chain.

AWS EKS and OBO Support

AWS EKS 1.34+ with OIDC

AWS EKS supports OIDC identity providers for cluster authentication starting from EKS 1.21, with significant improvements in 1.34+. However, EKS does not natively support the Entra ID OBO flow for Kubernetes API authentication. The authentication patterns differ:

Pattern

Supported

Details

EKS OIDC Identity Provider

Yes

Configure Entra ID as an OIDC provider in EKS. Users authenticate directly with Entra ID tokens where aud = EKS cluster. No OBO needed — the token is issued directly for the cluster.

IRSA (IAM Roles for Service Accounts)

Yes

Pod-level identity via projected service account tokens. This is M2M (client credentials), not user-delegated.

EKS Pod Identity

Yes (EKS 1.34+)

Simplified pod identity using EKS Pod Identity Agent. Also M2M, not user-delegated.

OBO → Kubernetes API

Partial

Entra ID OBO can issue tokens for any registered resource. If EKS is configured with Entra ID as an OIDC provider and the Kubernetes API is registered as an app in Entra ID, OBO-issued tokens can authenticate to EKS. Requires custom --oidc-issuer-url, --oidc-client-id, and --oidc-username-claim configuration on the EKS OIDC provider.

Recommended pattern for EKS: Configure Entra ID as the EKS OIDC identity provider. The MCP client authenticates the user and obtains a token with aud = helm-mcp. helm-mcp validates this token, then performs an OBO exchange to get a new token with aud = EKS cluster OIDC client ID. This OBO-issued token is used for Kubernetes API calls, preserving user identity and enabling per-user RBAC.

AWS Labs MCP and OBO

AWS Labs MCP servers (e.g., awslabs/mcp) and Amazon Bedrock AgentCore do not implement OAuth2 OBO or RFC 8693 Token Exchange. AgentCore uses a different model:

  • User identity propagation: Via an opaque X-Amzn-Bedrock-AgentCore-Runtime-User-Id HTTP header — not a cryptographically signed token

  • Outbound authentication: OAuth Authorization Code (3Lo) or Client Credentials (2Lo), but these are separate auth events, not delegated identity propagation

  • Token Vault: Stores refresh tokens for third-party services, but this is agent-scoped, not user-delegated

This means AWS Labs MCP servers cannot participate in an Entra ID OBO chain natively. If your architecture requires user-delegated identity propagation through AWS-hosted MCP servers, you must implement OBO exchange as a custom middleware layer, or use helm-mcp's built-in OBO support as a reference implementation.

Forward Proxy Support

helm-mcp respects standard proxy environment variables:

export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
export NO_PROXY=localhost,127.0.0.1,.internal.company.com

Development

Prerequisites

  • Go 1.27+

  • Python 3.12+ (for the Python package)

  • golangci-lint v2 (optional, for linting)

Build

make build        # Build binary
make install      # Install to $GOPATH/bin
make build-all    # Cross-compile for Linux/macOS (amd64/arm64)

Test

# Go tests
make test         # Run all tests with race detection and coverage
make test-short   # Run tests without integration tests

# Python tests (33 tests)
cd python && pip install -e ".[dev]" && pytest -v tests/

Lint

make lint         # Run golangci-lint + go vet
make vet          # Run go vet only

Security Check

make security     # Run govulncheck

Coverage

make coverage     # Generate coverage report (coverage.html)

Architecture

cmd/helm-mcp/          Entry point, transport selection, CLI flags
internal/
  helmengine/           Engine interface and shared types
    v3/                 Helm v3 SDK implementation
    v4/                 Helm v4 SDK implementation
  tools/                MCP tool handlers
    release/            Install, upgrade, uninstall, rollback, list, status, etc.
    chart/              Create, lint, template, package, pull, push, show, etc.
    repo/               Add, list, update, remove, index
    registry/           Login, logout
    search/             Hub, repo
    plugin/             Install, list, uninstall, update
    env/                Env, version
  security/             Process hardening, input validation, credential scrubbing
  resilience/           Response budget, circuit breaker, retry, timeouts, manifest sanitisation
  server/               MCP server creation and tool registration
python/                 FastMCP-based Python wrapper
  src/helm_mcp/         Python package source
  tests/                Python tests

Contributing

We welcome contributions from the community! Whether it's bug reports, feature requests, documentation improvements, or code contributions — all help is appreciated.

See CONTRIBUTING.md for detailed guidelines on:

  • Setting up your development environment

  • Running tests and linters

  • Submitting pull requests

  • Commit message conventions

Community

License

This project is licensed under the MIT License — free to use, modify, and distribute.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to interact with and manage Kubernetes clusters, supporting operations on pods, deployments, services, configmaps, secrets, namespaces, metrics, and events with built-in safety features for destructive actions.
    9
    3 npm
    1
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Enables AI assistants to manage Docker containers and Kubernetes resources through natural language, supporting operations like container management, image building, and pod/deployment/service management.
    9
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    MCP server that gives AI assistants full access to Kubernetes clusters and Helm, exposing 73 tools for managing pods, deployments, services, configs, secrets, logs, exec, port-forwarding, Helm lifecycle, and more.
    -