# kubernetes-mcp-server

![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![AppVersion: latest](https://img.shields.io/badge/AppVersion-latest-informational?style=flat-square)

Helm Chart for the Kubernetes MCP Server

**Homepage:** <https://github.com/containers/kubernetes-mcp-server>

## Maintainers

| Name | Email | Url |
| ---- | ------ | --- |
| Andrew Block | <ablock@redhat.com> |  |
| Marc Nuri | <marc.nuri@redhat.com> |  |

## Installing the Chart

The Chart can be installed quickly and easily to a Kubernetes cluster. Since an _Ingress_ is added as part of the default install of the Chart, the `ingress.host` Value must be specified unless you disable Ingress and use another exposure mechanism (for example Gateway API; see below).

Install the Chart using the following command from the root of this directory:

```shell
helm upgrade -i -n kubernetes-mcp-server --create-namespace kubernetes-mcp-server oci://ghcr.io/containers/charts/kubernetes-mcp-server --set ingress.host=<hostname>
```

### Gateway API (HTTPRoute)

If your platform uses [Gateway API](https://gateway.networking.k8s.io/) instead of classic Ingress, set `ingress.enabled` to `false` and enable `httpRoute` with `parentRefs`, `rules` (optional `matches`, `filters`, and `timeouts` are passed through), and optionally `hostnames`. The chart adds `backendRefs` pointing at the release `Service` and `service.port`. `parentRefs` and `hostnames` are rendered with `tpl` on their YAML so you can reference `Release` metadata in values. `httpRoute.parentRefs` is required when `httpRoute.enabled=true`; leaving it empty fails at `helm template` / `helm install` time.

```yaml
ingress:
  enabled: false
httpRoute:
  enabled: true
  parentRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: my-gateway
      namespace: gateway-system
      sectionName: http
  hostnames:
    - mcp.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
```

### Optimized OpenShift Deployment

Functionality has been added to the Chart to simplify the deployment to OpenShift Cluster.

### RBAC Configuration

The chart supports creating custom RBAC resources. Set `rbac.create: false` to disable all RBAC resource creation.

To bind to an existing cluster role (e.g., `view`, `edit`, `admin`), use `roleRef.external: true`:

```yaml
rbac:
  extraClusterRoleBindings:
    - name: use-view-role
      roleRef:
        name: view
        external: true
```

### Additional Containers (Sidecars)

The chart supports adding additional containers to the deployment pod. These can be used for various purposes such as MCP proxying.

To add extra containers, define them in the `extraContainers` value using standard Kubernetes container specifications:

```yaml
extraContainers:
  - name: sidecar
    image: quay.io/prometheus/busybox:latest
    resources:
      requests:
        cpu: 10m
        memory: 32Mi
      limits:
        cpu: 50m
        memory: 64Mi
  - name: metrics-exporter
    image: quay.io/prometheus/prometheus:latest
    ports:
      - containerPort: 9090
```

Each container accepts any valid Kubernetes container field including `image`, `command`, `args`, `env`, `volumeMounts`, `resources`, `ports`, and more.

## Values

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| affinity | object | `{}` |  |
| config.port | string | `"{{ .Values.service.port }}"` |  |
| configFilePath | string | `"/etc/kubernetes-mcp-server/config.toml"` |  |
| defaultPodSecurityContext | object | `{"seccompProfile":{"type":"RuntimeDefault"}}` | Default Security Context for the Pod when one is not provided |
| defaultSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true}` | Default Security Context for the Container when one is not provided |
| extraArgs | list | `[]` | Note: For TLS configuration, use the tls section above instead of extraArgs. |
| extraContainers | list | `[]` | Note: sidecars do not inherit defaultSecurityContext or the /tmp emptyDir mount; configure them per-container if needed. |
| extraVolumeMounts | list | `[]` | Additional volumeMounts on the output Deployment definition. |
| extraVolumes | list | `[]` | Additional volumes on the output Deployment definition. |
| fullnameOverride | string | `""` |  |
| httpRoute | object | `{"annotations":{},"enabled":false,"hostnames":[],"labels":{},"parentRefs":[],"rules":[{"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}` | Disabled by default. When enabled, set `parentRefs`, `rules` (with `matches` / optional `filters` / `timeouts`), and optionally `hostnames`; the chart appends `backendRefs` to the release Service. |
| image | object | `{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"containers/kubernetes_mcp_server","version":"latest"}` | This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/ |
| image.pullPolicy | string | `"IfNotPresent"` | This sets the pull policy for images. |
| image.version | string | `"latest"` | This sets the tag or sha digest for the image. |
| imagePullSecrets | list | `[]` | This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ |
| ingress | object | `{"annotations":{},"className":"","enabled":true,"host":"","hosts":null,"path":"/","pathType":"ImplementationSpecific","termination":"edge","tls":null}` | This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/ |
| initContainers | list | `[]` | Init containers to run before the main container starts. Each container is defined as a complete container spec. Supports tpl for templating. |
| livenessProbe | object | `{"httpGet":{"path":"/healthz","port":"http"}}` | Liveness and readiness probes for the container. |
| metrics | object | `{"prometheusRule":{"additionalRules":[],"annotations":{},"defaultRules":{"enabled":true},"enabled":false,"labels":{}},"serviceMonitor":{"annotations":{},"enabled":false,"interval":"","labels":{},"metricRelabelings":[],"relabelings":[],"scheme":"","scrapeTimeout":"","tlsConfig":{}}}` | Metrics and monitoring configuration |
| metrics.prometheusRule | object | `{"additionalRules":[],"annotations":{},"defaultRules":{"enabled":true},"enabled":false,"labels":{}}` | PrometheusRule configuration for recording rules Recording rules aggregate high-cardinality metrics for efficient querying and Telemeter compatibility |
| metrics.prometheusRule.additionalRules | list | `[]` | Additional custom recording rules (appended to default rules if enabled) Example: additionalRules:   - name: custom-mcp-rules     rules:       - record: my_custom_metric         expr: sum(some_metric) |
| metrics.prometheusRule.annotations | object | `{}` | Annotations for the PrometheusRule |
| metrics.prometheusRule.defaultRules | object | `{"enabled":true}` | Default recording rules configuration |
| metrics.prometheusRule.defaultRules.enabled | bool | `true` | Enable default recording rules that aggregate MCP metrics These rules create aggregates at two levels:  Cluster-level (for Telemeter): - cluster:k8s_mcp_tool_calls:sum - Total tool calls across all tools - cluster:k8s_mcp_tool_errors:sum - Total tool errors across all tools - cluster:k8s_mcp_http_requests:sum - Total HTTP requests  Namespace-level (for multi-tenant RBAC, grouped by namespace label): - namespace:k8s_mcp_tool_calls:sum - Tool calls by namespace - namespace:k8s_mcp_tool_errors:sum - Tool errors by namespace - namespace:k8s_mcp_http_requests:sum - HTTP requests by namespace |
| metrics.prometheusRule.enabled | bool | `false` | Enable PrometheusRule for recording rules |
| metrics.prometheusRule.labels | object | `{}` | Additional labels for the PrometheusRule |
| metrics.serviceMonitor | object | `{"annotations":{},"enabled":false,"interval":"","labels":{},"metricRelabelings":[],"relabelings":[],"scheme":"","scrapeTimeout":"","tlsConfig":{}}` | ServiceMonitor configuration for Prometheus Operator monitoring |
| metrics.serviceMonitor.annotations | object | `{}` | Annotations for the ServiceMonitor |
| metrics.serviceMonitor.enabled | bool | `false` | Enable ServiceMonitor for Prometheus scraping |
| metrics.serviceMonitor.interval | string | `""` | Scrape interval (e.g., "30s", "1m") |
| metrics.serviceMonitor.labels | object | `{}` | Additional labels for the ServiceMonitor (useful for prometheus-operator serviceMonitorSelector) |
| metrics.serviceMonitor.metricRelabelings | list | `[]` | Metric relabeling rules |
| metrics.serviceMonitor.relabelings | list | `[]` | Relabeling rules for metrics |
| metrics.serviceMonitor.scheme | string | `""` | Scheme to use for scraping (http or https) |
| metrics.serviceMonitor.scrapeTimeout | string | `""` | Scrape timeout (e.g., "10s") |
| metrics.serviceMonitor.tlsConfig | object | `{}` | TLS configuration for scraping |
| nameOverride | string | `""` |  |
| nodeSelector | object | `{}` |  |
| openshift | bool | `false` | Enable OpenShift specific features |
| podAnnotations | object | `{}` | For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ |
| podLabels | object | `{}` | For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ |
| podSecurityContext | object | `{}` | Define the Security Context for the Pod |
| rbac | object | `{"create":true,"extraClusterRoleBindings":[],"extraClusterRoles":[],"extraRoleBindings":[],"extraRoles":[]}` | When using long names, they will be automatically truncated. |
| rbac.create | bool | `true` | contents of extraClusterRoles, extraClusterRoleBindings, extraRoles, and extraRoleBindings. |
| rbac.extraClusterRoleBindings | list | `[]` | without prefixing the release fullname. |
| rbac.extraClusterRoles | list | `[]` | "<release-fullname>-<name>" with the specified rules. |
| rbac.extraRoleBindings | list | `[]` | Use roleRef.external: true to reference existing roles without prefixing the release fullname. |
| rbac.extraRoles | list | `[]` | "<release-fullname>-<name>" in the specified namespace. |
| readinessProbe.httpGet.path | string | `"/healthz"` |  |
| readinessProbe.httpGet.port | string | `"http"` |  |
| replicaCount | int | `1` | This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/ |
| resources | object | `{"limits":{"cpu":"100m","memory":"128Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}` | Resource requests and limits for the container. |
| securityContext | object | `{}` | Define the Security Context for the Container |
| service | object | `{"annotations":{},"port":8080,"targetPort":"http","type":"ClusterIP"}` | This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/ |
| service.annotations | object | `{}` | Annotations to add to the service |
| service.port | int | `8080` | This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports |
| service.targetPort | string | `"http"` | Target port for the service. Useful when deploying with a proxy sidecar or exposing a different port. Set this to the sidecar's port to route traffic through the proxy before reaching the main container. |
| service.type | string | `"ClusterIP"` | This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types |
| serviceAccount | object | `{"annotations":{},"automountToken":true,"create":true,"name":""}` | This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/ |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
| serviceAccount.automountToken | bool | `true` | Whether to auto-mount the ServiceAccount token into the pod. Required for in-cluster Kubernetes API access (default). Set to false for OAuth passthrough deployments where the server uses the user's token instead of the ServiceAccount token. |
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
| serviceAccount.name | string | `""` | If not set and create is true, a name is generated using the fullname template |
| tls | object | `{"certFile":"tls.crt","enabled":false,"keyFile":"tls.key","mountPath":"/etc/tls","secretName":""}` | This is the recommended way to enable TLS instead of using extraArgs. |
| tls.certFile | string | `"tls.crt"` | Name of the certificate file within the secret (default: tls.crt) |
| tls.enabled | bool | `false` | Enable TLS for the MCP server |
| tls.keyFile | string | `"tls.key"` | Name of the key file within the secret (default: tls.key) |
| tls.mountPath | string | `"/etc/tls"` | Path where the TLS secret will be mounted inside the container |
| tls.secretName | string | `""` | The secret should be of type kubernetes.io/tls with tls.crt and tls.key. |
| tolerations | list | `[]` |  |

## Updating the README

The contents of the README.md file is generated using [helm-docs](https://github.com/norwoodj/helm-docs). Whenever changes are introduced to the Chart and its _Values_, the documentation should be regenerated.

Execute the following command to regenerate the documentation from within the Helm Chart directory.

```shell
helm-docs -t README.md.gotmpl
```
