MCP server: One MCP server.

Helm

@scgis-walesMCP server · External

The full Helm CLI surface as 46 MCP tools, backed by the Helm v3 and v4 Go SDKs.

Freeno charge per call

Runs on your machine from its package; the steps are below.

What it does

  1. Reads
  2. Writes
  3. High impact

At most: moves money, runs code or commands, deletes data, or sends messages for you.

What it costs

Free

Odel charges nothing per call. The publisher may charge for its own service.

Runs locally over stdio

This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).

PyPIhelm-mcpv0.5.0

Run

uvx helm-mcp

MCP client config

{
  "mcpServers": {
    "helm": {
      "command": "uvx",
      "args": [
        "helm-mcp"
      ]
    }
  }
}
Dockerghcr.io/scgis-wales/helm-mcp:0.5.0

Run

docker run -i --rm ghcr.io/scgis-wales/helm-mcp:0.5.0

MCP client config

{
  "mcpServers": {
    "helm": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "ghcr.io/scgis-wales/helm-mcp:0.5.0"
      ]
    }
  }
}
README35 min read

Helm MCP

CI/CD Pipeline Go Report Card PyPI version Python versions Downloads License: MIT

An open-source MCP (Model Context Protocol) server that gives AI assistants full access to Helm — the Kubernetes package manager. Built with the native Helm Go SDK, supporting both Helm 3.x and 4.x in a single binary.

Use natural language to manage your Kubernetes deployments.
Connect helm-mcp to Claude, Cursor, VS Code, or any MCP-compatible client to install charts, manage releases, search repositories, and more — all through conversation.


Table of Contents

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:

bash
# 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.2+.

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

Docker

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

Python Package

bash
pip install helm-mcp

See Python Package below for full details.

Quick Start

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

bash
helm-mcp --mode stdio

HTTP mode (Streamable HTTP)

bash
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:

bash
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:

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

Claude Code

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

Cursor / Windsurf / VS Code

Add to your MCP server configuration:

json
{
  "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:

bash
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.

FeatureStatus
ToolsSupported: 46 tools, each with a top level title, annotations and (where the result has a stable shape) an output schema
ProgressSupported. helm_install, helm_upgrade, helm_uninstall, helm_rollback and helm_test send notifications/progress heartbeats every 10 seconds when the request carries a progress token, so clients that reset their timeout on progress keep waiting for long wait operations
CancellationSupported. In HTTP mode a dropped request cancels the running tool call, as Ctrl+C cancels the helm CLI (--propagate-cancellation=false to let it finish)
Stateless transportSupported and the default for --mode http (--stateless=false to opt out)
server/discoverSupported
Deterministic tools/list orderSupported — tools are returned sorted by name so clients and prompt caches keep hitting
Server instructionsSupported — the server explains version selection, cluster targeting and destructive-tool safety
Resources / PromptsNot applicable — every Helm operation is a tool call
Tasks extensionNot implemented. Attractive for long helm install --wait runs, but client support is still thin. Tracked as a follow-up.
MRTR / ElicitationNot implemented, deliberately. Helm tools take all their input as arguments, so there is no mid-call question to ask.
Roots / Sampling / LoggingNot implemented. All three are deprecated as of 2026-07-28 (SEP-2577).
HTTP+SSE transportRemoved in v0.2.0 — deprecated by the specification. Use --mode http.

Available Tools (46)

Release Management (14)

ToolDescription
helm_installInstall a Helm chart as a new release
helm_upgradeUpgrade a release to a new chart version or values
helm_uninstallUninstall a release and remove associated resources
helm_rollbackRollback a release to a previous revision
helm_listList releases (supports filters, sorting, pagination)
helm_statusDisplay release status, revision, chart, and values
helm_historyShow revision history of a release
helm_testRun the test suite for a release
helm_get_allGet all info (values, manifest, hooks, notes) for a release
helm_get_hooksGet hooks for a release
helm_get_manifestGet the Kubernetes manifest for a release
helm_get_metadataGet metadata for a release
helm_get_notesGet notes for a release
helm_get_valuesGet values for a release (user-supplied or computed)

Chart Management (14)

ToolDescription
helm_createCreate a new chart, optionally from a starter
helm_lintLint a chart for issues and best practices
helm_templateRender templates and hooks locally without installing
helm_packagePackage a chart directory into an archive (.tgz)
helm_pullDownload a chart from a repository or OCI registry
helm_pushPush a chart archive to an OCI registry
helm_verifyVerify a chart has a valid provenance file
helm_show_allShow all chart info (Chart.yaml, values, README, CRDs)
helm_show_chartShow Chart.yaml of a chart
helm_show_crdsShow CRDs of a chart
helm_show_readmeShow README of a chart
helm_show_valuesShow default values of a chart
helm_dependency_buildBuild charts/ directory from Chart.lock
helm_dependency_listList dependencies for a chart

Repository Management (5)

ToolDescription
helm_repo_addAdd a chart repository
helm_repo_listList configured chart repositories
helm_repo_updateUpdate chart repository indexes
helm_repo_removeRemove chart repositories
helm_repo_indexGenerate an index file for chart archives

Registry / OCI (2)

ToolDescription
helm_registry_loginLogin to an OCI registry
helm_registry_logoutLogout from an OCI registry

Search (2)

ToolDescription
helm_search_hubSearch Artifact Hub for charts
helm_search_repoSearch locally configured repositories

Plugin Management (6)

ToolDescription
helm_plugin_installInstall a Helm plugin
helm_plugin_listList installed plugins
helm_plugin_uninstallUninstall a plugin
helm_plugin_updateUpdate a plugin
helm_plugin_packagePackage a plugin directory into a signed archive (v4 only)
helm_plugin_verifyVerify a packaged plugin's signature and provenance (v4 only)

Environment (2)

ToolDescription
helm_envPrint Helm environment information
helm_versionPrint Helm SDK version information

Dependency Update (1)

ToolDescription
helm_dependency_updateUpdate 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.

AnnotationMeaningTools
readOnlyHint: trueOnly reads state; safe to call freelyhelm_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: trueCan remove or replace live state — call only on explicit user intenthelm_uninstall, helm_rollback, helm_upgrade, helm_repo_remove, helm_plugin_uninstall
openWorldHint: trueReaches 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: trueRepeating the call with the same arguments has no additional effectall 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.

jsonc
// 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 CommandMCP ToolStatus
helm createhelm_createCovered
helm dependency buildhelm_dependency_buildCovered
helm dependency listhelm_dependency_listCovered
helm dependency updatehelm_dependency_updateCovered
helm envhelm_envCovered
helm get allhelm_get_allCovered
helm get hookshelm_get_hooksCovered
helm get manifesthelm_get_manifestCovered
helm get metadatahelm_get_metadataCovered
helm get noteshelm_get_notesCovered
helm get valueshelm_get_valuesCovered
helm historyhelm_historyCovered
helm installhelm_installCovered
helm linthelm_lintCovered
helm listhelm_listCovered
helm packagehelm_packageCovered
helm plugin installhelm_plugin_installCovered
helm plugin listhelm_plugin_listCovered
helm plugin uninstallhelm_plugin_uninstallCovered
helm plugin packagehelm_plugin_packageCovered (v4 only)
helm plugin updatehelm_plugin_updateCovered
helm plugin verifyhelm_plugin_verifyCovered (v4 only)
helm pullhelm_pullCovered
helm pushhelm_pushCovered
helm registry loginhelm_registry_loginCovered
helm registry logouthelm_registry_logoutCovered
helm repo addhelm_repo_addCovered
helm repo indexhelm_repo_indexCovered
helm repo listhelm_repo_listCovered
helm repo removehelm_repo_removeCovered
helm repo updatehelm_repo_updateCovered
helm rollbackhelm_rollbackCovered
helm search hubhelm_search_hubCovered
helm search repohelm_search_repoCovered
helm show allhelm_show_allCovered
helm show charthelm_show_chartCovered
helm show crdshelm_show_crdsCovered
helm show readmehelm_show_readmeCovered
helm show valueshelm_show_valuesCovered
helm statushelm_statusCovered
helm templatehelm_templateCovered
helm testhelm_testCovered
helm uninstallhelm_uninstallCovered
helm upgradehelm_upgradeCovered
helm verifyhelm_verifyCovered
helm versionhelm_versionCovered
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".

Flag coverage

Commands are covered with their flags, not just their names. Every argument a tool accepts reaches the Helm SDK, and the arguments that change rendering or apply behaviour are covered by tests that render or dry run real charts. The flags below are deliberately not exposed:

FlagReason
--post-renderer on Helm v3A v3 post-renderer executes any binary on the server host. Helm v4 post-renderers are installed plugins and are exposed as post_renderer
--set and --set-jsonCovered by the inline values object, which is deep merged like --set
template --output-dirhelm_template is read only; use helm_install with output_dir
create --chart-api-version v3Experimental in Helm 4.3 and behind a feature gate
history --show-rollback-revision, get --template, version --templatePresentation only; the structured results already carry the data
search hub --endpoint, search --fail-on-no-result, repo index --jsonLow value for agents; may be added on request
push --username/--password, plugin install TLS and registry authUse helm_registry_login first, which stores credentials for later calls

Kubernetes Authentication

Every tool accepts these authentication fields via the GlobalInput:

FieldJSON KeyDescription
KubeconfigkubeconfigPath to kubeconfig file (defaults to $KUBECONFIG or ~/.kube/config)
Contextkube_contextKubernetes context name to use
API Serverkube_apiserverOverride the API server URL from kubeconfig
Bearer Tokenkube_tokenBearer token for API authentication
TLS Server Namekube_tls_server_nameServer name for TLS certificate validation
Insecure TLSkube_insecure_tlsSkip TLS certificate verification
NamespacenamespaceTarget 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:

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

Or with direct token authentication:

json
{
  "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:

json
{
  "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:

json
{
  "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:

json
{
  "helm_version": "v4",
  "release_name": "my-release"
}
  • "v4" (default): the Helm v4 SDK, with server side apply, plugin based post-renderers and signed plugins
  • "v3": the Helm v3 SDK, for backward compatibility

v4 only features

These fields need helm_version: "v4". Passing one with "v3" is an error rather than a silent no-op.

FieldToolsNotes
server_side_applyinstall, upgrade, rollbackSee Server side apply. false is accepted on v3, which always applies client side
force_conflictsinstall, upgrade, rollbackTake ownership of fields owned by other field managers
wait_strategyinstall, upgrade, rollback, uninstallSee below
post_renderer, post_renderer_argsinstall, upgrade, templateThe name of an installed post-renderer plugin
descriptionrollbackDescription recorded on the rollback revision
verify, keyringplugin installverify: false installs unsigned plugins such as helm-diff

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

Server side apply

Helm 4 applies with Kubernetes server side apply by default. helm_install follows that: leave server_side_apply unset for server side apply, or set it to false for client side apply. On helm_upgrade and helm_rollback an unset value keeps Helm's auto mode, which reuses the apply method recorded on the previous release. Release results report it as apply_method ("ssa" or "csa").

Before v0.5.0 the v4 engine forced client side apply on every install and rollback unless server_side_apply was true. If you depend on client side apply, pass server_side_apply: false.

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_strategyBehaviour
"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, enable_dns, take_ownership and hide_secret; output_dir, use_release_name and force_replace on helm_install; reset_then_reuse_values on helm_upgrade; ignore_not_found and description on helm_uninstall; all, uninstalling, time_format and selector on helm_list; show_resources on helm_status; logs on helm_test; and jsonpath on the helm_show_* tools.

Chart sources

helm_install, helm_upgrade, helm_template, helm_pull and the helm_show_* tools accept Helm's chart location flags: repo, username, password, ca_file, cert_file, key_file, insecure_skip_tls_verify, plain_http, pass_credentials, verify and keyring. TLS and credential options also apply to oci:// charts.

verify checks the chart provenance against the keyring, which defaults to $GNUPGHOME/pubring.gpg (or ~/.gnupg) as in the CLI. On helm_pull, prov downloads the provenance file without verifying it (helm pull --prov).

Upgrade or install

helm_upgrade with install: true installs the release when it does not exist, or replaces it when its last revision was uninstalled, exactly like helm upgrade --install. create_namespace applies to that install.

Values

Values are combined in the order Helm uses: values_files, then the inline values object (deep merged, like --set), then set_string, set_file and set_literal, each a list of key=value strings. set_file reads local files only, and every path it would read is validated, including paths inside {a,b} lists and further comma separated pairs.

Chart dependencies

dependency_update on helm_install, helm_upgrade and helm_template fetches missing dependencies before rendering, as the CLI flag does. Without it, a chart whose dependencies are not vendored is reported as an error instead of rendering without its subcharts.

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

bash
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:

bash
helm-mcp-python --setup

Usage as a Server

python
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

python
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

bash
# 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:

python
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:

CategoryVariables
ProxyHTTP_PROXY, HTTPS_PROXY, NO_PROXY (and lowercase variants)
KubernetesKUBECONFIG, KUBERNETES_SERVICE_HOST, KUBERNETES_SERVICE_PORT
HelmHELM_CACHE_HOME, HELM_CONFIG_HOME, HELM_DATA_HOME, HELM_PLUGINS, HELM_DEBUG
AWSAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_REGION, AWS_PROFILE
GCPGOOGLE_APPLICATION_CREDENTIALS, CLOUDSDK_COMPUTE_ZONE
AzureAZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID
TLSSSL_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

VariableTypeDefaultDescription
HELM_MCP_RETRY_ENABLEDbooltrueEnable proxy-level retry middleware
HELM_MCP_RETRY_MAX_RETRIESint2Maximum retry attempts
HELM_MCP_RETRY_BASE_DELAYfloat1.0Initial backoff delay (seconds)
HELM_MCP_RETRY_MAX_DELAYfloat30.0Maximum backoff delay (seconds)
HELM_MCP_RETRY_BACKOFF_MULTIPLIERfloat2.0Backoff multiplier
HELM_MCP_RATE_LIMIT_ENABLEDboolfalseEnable token-bucket rate limiting
HELM_MCP_RATE_LIMIT_MAX_RPSfloat10.0Maximum requests per second (must be greater than 0)
HELM_MCP_RATE_LIMIT_BURSTint20Burst capacity (at least 1; invalid values fail at startup)
HELM_MCP_CACHE_ENABLEDboolfalseEnable TTL-based response caching
HELM_MCP_CACHE_TOOL_TTLint300Tool call cache TTL (seconds)
HELM_MCP_CACHE_LIST_TTLint60Tool list cache TTL (seconds)
HELM_MCP_ERROR_HANDLING_ENABLEDbooltrueEnable structured error responses
HELM_MCP_ERROR_INCLUDE_TRACEBACKboolfalseInclude tracebacks in errors
HELM_MCP_TIMING_ENABLEDbooltrueEnable request timing
HELM_MCP_TIMING_DETAILEDboolfalseUse detailed timing middleware
HELM_MCP_CIRCUIT_BREAKER_ENABLEDbooltrueEnable circuit breaker on tool calls
HELM_MCP_CIRCUIT_BREAKER_FAILURE_THRESHOLDint5Failures before circuit opens
HELM_MCP_CIRCUIT_BREAKER_RESET_TIMEOUTfloat30.0Seconds before half-open retry
HELM_MCP_TENACITY_ENABLEDbooltrueEnable tenacity retry with jitter
HELM_MCP_TENACITY_MAX_ATTEMPTSint3Maximum retry attempts
HELM_MCP_TENACITY_MIN_WAITfloat0.5Minimum wait between retries (seconds)
HELM_MCP_TENACITY_MAX_WAITfloat10.0Maximum wait between retries (seconds)
HELM_MCP_TENACITY_MULTIPLIERfloat1.5Exponential backoff base
HELM_MCP_BULKHEAD_ENABLEDbooltrueEnable concurrency limiter
HELM_MCP_BULKHEAD_MAX_CONCURRENTint10Maximum concurrent tool calls
HELM_MCP_OTEL_ENABLEDboolfalseEnable OpenTelemetry tracing
HELM_MCP_OTEL_SERVICE_NAMEstrhelm-mcpOTel service name
HELM_MCP_OTEL_EXPORTERstrconsoleOTel exporter (console or otlp)

CLI Flags

bash
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

python
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:

bash
pip install helm-mcp[otel]

Then enable tracing:

bash
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:

bash
# 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:

PatternDescription
Circuit breakerThree-state (Closed/Open/HalfOpen) pattern to fail fast when backends are unavailable. Configurable failure threshold and recovery timeout.
Retry with backoffExponential backoff with jitter for transient failures. Context-aware cancellation and retryable error filtering.
Per-tool timeoutsCategory-based default timeouts: query (30s), mutate (120s), chart (60s), repo (60s). Respects existing context deadlines.

Known Limitations

Plugin tools use the Helm 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, because Helm's plugin packages are internal and not importable.

  • Helm v4 verifies plugin signatures by default. Plugins that are not signed, such as helm-diff, install with helm_plugin_install and 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, and with the default helm_version: "v4" it must be a Helm v4 CLI: a v3 CLI rejects v4 flags such as --verify=false.

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.

MechanismWhat 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 droppingDrops 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 zeroingZeroCredentials() 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.

bash
# 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

MechanismWhy Skipped
Seccomp BPFThe 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 isolationThe process needs access to ~/.kube/config, cloud credential files, DNS, and network. Namespace isolation would break core functionality.
Cgroup resource limitsA 5-minute pluginExecTimeout already bounds runaway operations, and the IDE manages process lifecycle.
AppArmor/SELinux profilesHigh 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, set_file entries, keyrings, CA/cert/key files, passphrase files, output and untar directories, chart paths for helm_lint and helm_package, and the chart name and starter for helm_create
  • 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
  • A dropped request cancels its tool call (--propagate-cancellation, on by default), so abandoned operations do not run until the write timeout
  • 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)

bash
# 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

VariableRequiredDescription
HELM_MCP_OIDC_ISSUERYes (for OIDC)OIDC issuer URL. Token iss claim must match.
HELM_MCP_OIDC_AUDIENCEYes (for OIDC)Expected aud claim. Tokens issued for other resources are rejected.
HELM_MCP_OIDC_JWKS_URLNoJWKS endpoint for signature verification. Auto-discovered from issuer if omitted.
HELM_MCP_REQUIRED_SCOPESNoComma-separated OAuth2 scopes required in the scp claim.
HELM_MCP_REQUIRED_ROLESNoComma-separated app roles required in the roles claim.
HELM_MCP_ALLOWED_CLIENTSNoComma-separated client app IDs allowed in azp/appid claim.
HELM_MCP_SESSION_TTLNoSession cache inactivity TTL (Go duration, e.g., 5m, 15m). Default: 5m.
HELM_MCP_AUTH_TOKENNoStatic 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:

CheckDescription
SignatureRSA 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

bash
# 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:

PatternSupportedDetails
EKS OIDC Identity ProviderYesConfigure 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)YesPod-level identity via projected service account tokens. This is M2M (client credentials), not user-delegated.
EKS Pod IdentityYes (EKS 1.34+)Simplified pod identity using EKS Pod Identity Agent. Also M2M, not user-delegated.
OBO → Kubernetes APIPartialEntra 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:

bash
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.2+
  • Python 3.12+ (for the Python package)
  • golangci-lint v2 (optional, for linting)

Build

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

Test

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

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

Lint

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

Security Check

bash
make security     # Run govulncheck

Coverage

bash
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, package, verify
    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.

Similar modules

Browse Cloud infrastructure