diff --git a/.gitignore b/.gitignore
index 12b624e7..4efe7690 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ node_modules/
.npmrc
kubernetes-mcp-server
+!charts/kubernetes-mcp-server
!cmd/kubernetes-mcp-server
!pkg/kubernetes-mcp-server
npm/kubernetes-mcp-server/README.md
diff --git a/README.md b/README.md
index 4c916af9..fb71ea9f 100644
--- a/README.md
+++ b/README.md
@@ -342,6 +342,10 @@ In case multi-cluster support is enabled (default) and you have access to multip
+## Helm Chart
+
+A [Helm Chart](https://helm.sh) is available to simplify the deployment of the Kubernetes MCP server. Additional details can be found in he [chart README](./charts/kubernetes-mcp-server/README.md).
+
## 🧑💻 Development
### Running with mcp-inspector
diff --git a/charts/kubernetes-mcp-server/.helmignore b/charts/kubernetes-mcp-server/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/charts/kubernetes-mcp-server/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/kubernetes-mcp-server/Chart.yaml b/charts/kubernetes-mcp-server/Chart.yaml
new file mode 100644
index 00000000..a66650bb
--- /dev/null
+++ b/charts/kubernetes-mcp-server/Chart.yaml
@@ -0,0 +1,13 @@
+apiVersion: v2
+name: kubernetes-mcp-server
+description: Helm Chart for the Kubernetes MCP Server
+home: https://github.com/containers/kubernetes-mcp-server
+keywords:
+ - kubernetes
+ - mcp
+maintainers:
+ - name: Andrew Block
+ email: ablock@redhat.com
+ - name: Marc Nuri
+ email: marc.nuri@redhat.com
+version: 0.1.0
diff --git a/charts/kubernetes-mcp-server/README.md b/charts/kubernetes-mcp-server/README.md
new file mode 100644
index 00000000..f20b7fbc
--- /dev/null
+++ b/charts/kubernetes-mcp-server/README.md
@@ -0,0 +1,76 @@
+# kubernetes-mcp-server
+
+
+
+Helm Chart for the Kubernetes MCP Server
+
+**Homepage:**
+
+## Maintainers
+
+| Name | Email | Url |
+| ---- | ------ | --- |
+| Andrew Block | | |
+| Marc Nuri | | |
+
+## 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.
+
+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 . --set openshift=true --set ingress.host=
+```
+
+### Optimized OpenShift Deployment
+
+Functionality has bee added to the Chart to simplify the deployment to OpenShift Cluster.
+
+## 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"]},"runAsNonRoot":true}` | Default Security Context for the Container when one is not provided |
+| extraVolumeMounts | list | `[]` | Additional volumeMounts on the output Deployment definition. |
+| extraVolumes | list | `[]` | Additional volumes on the output Deployment definition. |
+| fullnameOverride | string | `""` | |
+| image | object | `{"pullPolicy":"IfNotPresent","registry":"quay.io","repository":"manusa/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/ |
+| livenessProbe | object | `{"httpGet":{"path":"/healthz","port":"http"}}` | Liveness and readiness probes for the container. |
+| 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 |
+| 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 | `{"port":8080,"type":"ClusterIP"}` | This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/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.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":{},"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.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 |
+| 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.gotpl
+```
diff --git a/charts/kubernetes-mcp-server/README.md.gotpl b/charts/kubernetes-mcp-server/README.md.gotpl
new file mode 100644
index 00000000..9e6ba772
--- /dev/null
+++ b/charts/kubernetes-mcp-server/README.md.gotpl
@@ -0,0 +1,40 @@
+{{ template "chart.header" . }}
+{{ template "chart.deprecationWarning" . }}
+
+{{ template "chart.badgesSection" . }}
+
+{{ template "chart.description" . }}
+
+{{ template "chart.homepageLine" . }}
+
+{{ template "chart.maintainersSection" . }}
+
+{{ template "chart.sourcesSection" . }}
+
+{{ template "chart.requirementsSection" . }}
+
+## 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.
+
+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 . --set openshift=true --set ingress.host=
+```
+
+### Optimized OpenShift Deployment
+
+Functionality has bee added to the Chart to simplify the deployment to OpenShift Cluster.
+
+{{ template "chart.valuesSection" . }}
+
+## 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.gotpl
+```
diff --git a/charts/kubernetes-mcp-server/templates/_helpers.tpl b/charts/kubernetes-mcp-server/templates/_helpers.tpl
new file mode 100644
index 00000000..991c9331
--- /dev/null
+++ b/charts/kubernetes-mcp-server/templates/_helpers.tpl
@@ -0,0 +1,73 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "kubernetes-mcp-server.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "kubernetes-mcp-server.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "kubernetes-mcp-server.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "kubernetes-mcp-server.labels" -}}
+helm.sh/chart: {{ include "kubernetes-mcp-server.chart" . }}
+{{ include "kubernetes-mcp-server.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "kubernetes-mcp-server.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "kubernetes-mcp-server.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "kubernetes-mcp-server.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "kubernetes-mcp-server.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create the image path for the passed in image field
+*/}}
+{{- define "kubernetes-mcp-server.image" -}}
+{{- if eq (substr 0 7 .version) "sha256:" -}}
+{{- printf "%s/%s@%s" .registry .repository .version -}}
+{{- else -}}
+{{- printf "%s/%s:%s" .registry .repository .version -}}
+{{- end -}}
+{{- end -}}
diff --git a/charts/kubernetes-mcp-server/templates/configmap.yaml b/charts/kubernetes-mcp-server/templates/configmap.yaml
new file mode 100644
index 00000000..9d066b35
--- /dev/null
+++ b/charts/kubernetes-mcp-server/templates/configmap.yaml
@@ -0,0 +1,10 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ include "kubernetes-mcp-server.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "kubernetes-mcp-server.labels" . | nindent 4 }}
+data:
+ config.toml: |
+ {{- tpl (toToml .Values.config) . | replace ".0" "" | nindent 4 }}
diff --git a/charts/kubernetes-mcp-server/templates/deployment.yaml b/charts/kubernetes-mcp-server/templates/deployment.yaml
new file mode 100644
index 00000000..e6bc8a34
--- /dev/null
+++ b/charts/kubernetes-mcp-server/templates/deployment.yaml
@@ -0,0 +1,85 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "kubernetes-mcp-server.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "kubernetes-mcp-server.labels" . | nindent 4 }}
+spec:
+ replicas: {{ .Values.replicaCount }}
+ selector:
+ matchLabels:
+ {{- include "kubernetes-mcp-server.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ annotations:
+ checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
+ {{- with .Values.podAnnotations }}
+ {{- tpl (toYaml .) . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "kubernetes-mcp-server.labels" . | nindent 8 }}
+ {{- with .Values.podLabels }}
+ {{- tpl (toYaml .) . | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- with .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- tpl (toYaml .) . | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "kubernetes-mcp-server.serviceAccountName" . }}
+ securityContext:
+ {{- tpl (toYaml (default .Values.defaultPodSecurityContext .Values.podSecurityContext)) . | nindent 8 }}
+ containers:
+ - name: {{ .Chart.Name }}
+ securityContext:
+ {{- tpl (toYaml (default .Values.defaultSecurityContext .Values.securityContext)) . | nindent 12 }}
+ image: "{{ template "kubernetes-mcp-server.image" .Values.image }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - name: http
+ containerPort: {{ .Values.service.port }}
+ protocol: TCP
+ args:
+ - "--config"
+ - "{{ .Values.configFilePath }}"
+ {{- with .Values.livenessProbe }}
+ livenessProbe:
+ {{- tpl (toYaml .) . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.readinessProbe }}
+ readinessProbe:
+ {{- tpl (toYaml .) . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.resources }}
+ resources:
+ {{- tpl (toYaml .) . | nindent 12 }}
+ {{- end }}
+ volumeMounts:
+ - name: config
+ mountPath: {{ .Values.configFilePath | dir }}
+ {{- with .Values.extraVolumeMounts }}
+ {{- tpl (toYaml .) . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.extraVolumeMounts }}
+ {{- tpl (toYaml .) . | nindent 12 }}
+ {{- end }}
+ volumes:
+ - name: config
+ configMap:
+ name: {{ include "kubernetes-mcp-server.fullname" . }}
+ {{- with .Values.extraVolumes }}
+ {{- tpl (toYaml .) . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+ {{- tpl (toYaml .) . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+ {{- tpl (toYaml .) . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+ {{- tpl (toYaml .) . | nindent 8 }}
+ {{- end }}
diff --git a/charts/kubernetes-mcp-server/templates/ingress.yaml b/charts/kubernetes-mcp-server/templates/ingress.yaml
new file mode 100644
index 00000000..e6179fcb
--- /dev/null
+++ b/charts/kubernetes-mcp-server/templates/ingress.yaml
@@ -0,0 +1,38 @@
+{{- if .Values.ingress.enabled -}}
+{{- $host := required "Ingress hostname must be specified" (tpl .Values.ingress.host .) }}
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ include "kubernetes-mcp-server.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "kubernetes-mcp-server.labels" . | nindent 4 }}
+ annotations:
+ {{- if eq .Values.openshift true }}
+ route.openshift.io/termination: {{ .Values.ingress.termination }}
+ {{- end }}
+ {{- with .Values.ingress.annotations }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- with .Values.ingress.className }}
+ ingressClassName: {{ . }}
+ {{- end }}
+ {{- if .Values.ingress.tls }}
+ tls:
+ - hosts:
+ - "{{ $host }}"
+ secretName: {{ .Values.ingress.tls.secretName }}
+ {{- end }}
+ rules:
+ - host: "{{ $host }}"
+ http:
+ paths:
+ - path: {{ .Values.ingress.path }}
+ pathType: {{ .Values.ingress.pathType }}
+ backend:
+ service:
+ name: {{ include "kubernetes-mcp-server.fullname" $ }}
+ port:
+ number: {{ $.Values.service.port }}
+{{- end }}
diff --git a/charts/kubernetes-mcp-server/templates/service.yaml b/charts/kubernetes-mcp-server/templates/service.yaml
new file mode 100644
index 00000000..20bccf21
--- /dev/null
+++ b/charts/kubernetes-mcp-server/templates/service.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "kubernetes-mcp-server.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "kubernetes-mcp-server.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "kubernetes-mcp-server.selectorLabels" . | nindent 4 }}
diff --git a/charts/kubernetes-mcp-server/templates/serviceaccount.yaml b/charts/kubernetes-mcp-server/templates/serviceaccount.yaml
new file mode 100644
index 00000000..b75f47bf
--- /dev/null
+++ b/charts/kubernetes-mcp-server/templates/serviceaccount.yaml
@@ -0,0 +1,13 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "kubernetes-mcp-server.serviceAccountName" . }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "kubernetes-mcp-server.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- tpl (toYaml .) . | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/charts/kubernetes-mcp-server/values.yaml b/charts/kubernetes-mcp-server/values.yaml
new file mode 100644
index 00000000..64f5fc6a
--- /dev/null
+++ b/charts/kubernetes-mcp-server/values.yaml
@@ -0,0 +1,121 @@
+# -- Enable OpenShift specific features
+openshift: false
+
+# -- This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
+replicaCount: 1
+
+# -- This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
+image:
+ registry: quay.io
+ repository: manusa/kubernetes_mcp_server
+ # -- This sets the tag or sha digest for the image.
+ version: latest
+ # -- This sets the pull policy for images.
+ pullPolicy: IfNotPresent
+
+# -- 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/
+imagePullSecrets: []
+# This is to override the chart name.
+nameOverride: ""
+fullnameOverride: ""
+
+# -- This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
+serviceAccount:
+ # -- Specifies whether a service account should be created
+ create: true
+ # -- Annotations to add to the service account
+ annotations: {}
+ # -- The name of the service account to use.
+ # -- If not set and create is true, a name is generated using the fullname template
+ name: ""
+
+# -- This is for setting Kubernetes Annotations to a Pod.
+# -- For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+podAnnotations: {}
+# -- This is for setting Kubernetes Labels to a Pod.
+# -- For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
+podLabels: {}
+
+# -- Default Security Context for the Pod when one is not provided
+defaultPodSecurityContext:
+ seccompProfile:
+ type: RuntimeDefault
+
+# -- Define the Security Context for the Pod
+podSecurityContext: {}
+
+# -- Default Security Context for the Container when one is not provided
+defaultSecurityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ runAsNonRoot: true
+
+# -- Define the Security Context for the Container
+securityContext: {}
+
+# -- This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/
+service:
+ # -- This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
+ type: ClusterIP
+ # -- This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports
+ port: 8080
+
+# -- This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
+ingress:
+ enabled: true
+ className: ""
+ annotations: {}
+ host: ""
+ path: /
+ pathType: ImplementationSpecific
+ termination: edge
+ hosts:
+ tls:
+ #secretName: ""
+
+# -- Resource requests and limits for the container.
+resources:
+ limits:
+ cpu: 100m
+ memory: 128Mi
+ requests:
+ cpu: 100m
+ memory: 128Mi
+
+# -- Liveness and readiness probes for the container.
+livenessProbe:
+ httpGet:
+ path: /healthz
+ port: http
+readinessProbe:
+ httpGet:
+ path: /healthz
+ port: http
+
+# -- Additional volumes on the output Deployment definition.
+extraVolumes: []
+# - name: foo
+# secret:
+# secretName: mysecret
+# optional: false
+
+# -- Additional volumeMounts on the output Deployment definition.
+extraVolumeMounts: []
+# - name: foo
+# mountPath: "/etc/foo"
+# readOnly: true
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+# Path to the configuration file inside the container
+configFilePath: /etc/kubernetes-mcp-server/config.toml
+
+# MCP Server configuration options. See https://github.com/containers/kubernetes-mcp-server/blob/main/pkg/config/config.go for details.
+config:
+ port: "{{ .Values.service.port }}"