Skip to content

Commit d9c47f1

Browse files
authored
feat(helm): add Helm chart for Kubernetes MCP Server deployment (#440)
Added a Helm chart to simplify deployment of the Kubernetes MCP server with support for both Kubernetes and OpenShift clusters. Signed-off-by: Andrew Block <andy.block@gmail.com>
1 parent 7d1ed7a commit d9c47f1

File tree

13 files changed

+513
-0
lines changed

13 files changed

+513
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ node_modules/
66

77
.npmrc
88
kubernetes-mcp-server
9+
!charts/kubernetes-mcp-server
910
!cmd/kubernetes-mcp-server
1011
!pkg/kubernetes-mcp-server
1112
npm/kubernetes-mcp-server/README.md

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,10 @@ In case multi-cluster support is enabled (default) and you have access to multip
477477

478478
<!-- AVAILABLE-TOOLSETS-TOOLS-END -->
479479

480+
## Helm Chart
481+
482+
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).
483+
480484
## 🧑‍💻 Development <a id="development"></a>
481485

482486
### Running with mcp-inspector
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: v2
2+
name: kubernetes-mcp-server
3+
description: Helm Chart for the Kubernetes MCP Server
4+
home: https://github.com/containers/kubernetes-mcp-server
5+
keywords:
6+
- kubernetes
7+
- mcp
8+
maintainers:
9+
- name: Andrew Block
10+
email: ablock@redhat.com
11+
- name: Marc Nuri
12+
email: marc.nuri@redhat.com
13+
version: 0.1.0
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# kubernetes-mcp-server
2+
3+
![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square)
4+
5+
Helm Chart for the Kubernetes MCP Server
6+
7+
**Homepage:** <https://github.com/containers/kubernetes-mcp-server>
8+
9+
## Maintainers
10+
11+
| Name | Email | Url |
12+
| ---- | ------ | --- |
13+
| Andrew Block | <ablock@redhat.com> | |
14+
| Marc Nuri | <marc.nuri@redhat.com> | |
15+
16+
## Installing the Chart
17+
18+
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.
19+
20+
Install the Chart using the following command from the root of this directory:
21+
22+
```shell
23+
helm upgrade -i -n kubernetes-mcp-server --create-namespace kubernetes-mcp-server . --set openshift=true --set ingress.host=<hostname>
24+
```
25+
26+
### Optimized OpenShift Deployment
27+
28+
Functionality has bee added to the Chart to simplify the deployment to OpenShift Cluster.
29+
30+
## Values
31+
32+
| Key | Type | Default | Description |
33+
|-----|------|---------|-------------|
34+
| affinity | object | `{}` | |
35+
| config.port | string | `"{{ .Values.service.port }}"` | |
36+
| configFilePath | string | `"/etc/kubernetes-mcp-server/config.toml"` | |
37+
| defaultPodSecurityContext | object | `{"seccompProfile":{"type":"RuntimeDefault"}}` | Default Security Context for the Pod when one is not provided |
38+
| defaultSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"runAsNonRoot":true}` | Default Security Context for the Container when one is not provided |
39+
| extraVolumeMounts | list | `[]` | Additional volumeMounts on the output Deployment definition. |
40+
| extraVolumes | list | `[]` | Additional volumes on the output Deployment definition. |
41+
| fullnameOverride | string | `""` | |
42+
| 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/ |
43+
| image.pullPolicy | string | `"IfNotPresent"` | This sets the pull policy for images. |
44+
| image.version | string | `"latest"` | This sets the tag or sha digest for the image. |
45+
| 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/ |
46+
| 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/ |
47+
| livenessProbe | object | `{"httpGet":{"path":"/healthz","port":"http"}}` | Liveness and readiness probes for the container. |
48+
| nameOverride | string | `""` | |
49+
| nodeSelector | object | `{}` | |
50+
| openshift | bool | `false` | Enable OpenShift specific features |
51+
| podAnnotations | object | `{}` | For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ |
52+
| podLabels | object | `{}` | For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ |
53+
| podSecurityContext | object | `{}` | Define the Security Context for the Pod |
54+
| readinessProbe.httpGet.path | string | `"/healthz"` | |
55+
| readinessProbe.httpGet.port | string | `"http"` | |
56+
| replicaCount | int | `1` | This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/ |
57+
| resources | object | `{"limits":{"cpu":"100m","memory":"128Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}` | Resource requests and limits for the container. |
58+
| securityContext | object | `{}` | Define the Security Context for the Container |
59+
| 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/ |
60+
| 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 |
61+
| 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 |
62+
| 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/ |
63+
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
64+
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
65+
| serviceAccount.name | string | `""` | If not set and create is true, a name is generated using the fullname template |
66+
| tolerations | list | `[]` | |
67+
68+
## Updating the README
69+
70+
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.
71+
72+
Execute the following command to regenerate the documentation from within the Helm Chart directory.
73+
74+
```shell
75+
helm-docs -t README.md.gotpl
76+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{{ template "chart.header" . }}
2+
{{ template "chart.deprecationWarning" . }}
3+
4+
{{ template "chart.badgesSection" . }}
5+
6+
{{ template "chart.description" . }}
7+
8+
{{ template "chart.homepageLine" . }}
9+
10+
{{ template "chart.maintainersSection" . }}
11+
12+
{{ template "chart.sourcesSection" . }}
13+
14+
{{ template "chart.requirementsSection" . }}
15+
16+
## Installing the Chart
17+
18+
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.
19+
20+
Install the Chart using the following command from the root of this directory:
21+
22+
```shell
23+
helm upgrade -i -n kubernetes-mcp-server --create-namespace kubernetes-mcp-server . --set openshift=true --set ingress.host=<hostname>
24+
```
25+
26+
### Optimized OpenShift Deployment
27+
28+
Functionality has bee added to the Chart to simplify the deployment to OpenShift Cluster.
29+
30+
{{ template "chart.valuesSection" . }}
31+
32+
## Updating the README
33+
34+
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.
35+
36+
Execute the following command to regenerate the documentation from within the Helm Chart directory.
37+
38+
```shell
39+
helm-docs -t README.md.gotpl
40+
```
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "kubernetes-mcp-server.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11+
If release name contains chart name it will be used as a full name.
12+
*/}}
13+
{{- define "kubernetes-mcp-server.fullname" -}}
14+
{{- if .Values.fullnameOverride }}
15+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16+
{{- else }}
17+
{{- $name := default .Chart.Name .Values.nameOverride }}
18+
{{- if contains $name .Release.Name }}
19+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
20+
{{- else }}
21+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22+
{{- end }}
23+
{{- end }}
24+
{{- end }}
25+
26+
{{/*
27+
Create chart name and version as used by the chart label.
28+
*/}}
29+
{{- define "kubernetes-mcp-server.chart" -}}
30+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31+
{{- end }}
32+
33+
{{/*
34+
Common labels
35+
*/}}
36+
{{- define "kubernetes-mcp-server.labels" -}}
37+
helm.sh/chart: {{ include "kubernetes-mcp-server.chart" . }}
38+
{{ include "kubernetes-mcp-server.selectorLabels" . }}
39+
{{- if .Chart.AppVersion }}
40+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41+
{{- end }}
42+
app.kubernetes.io/managed-by: {{ .Release.Service }}
43+
{{- end }}
44+
45+
{{/*
46+
Selector labels
47+
*/}}
48+
{{- define "kubernetes-mcp-server.selectorLabels" -}}
49+
app.kubernetes.io/name: {{ include "kubernetes-mcp-server.name" . }}
50+
app.kubernetes.io/instance: {{ .Release.Name }}
51+
{{- end }}
52+
53+
{{/*
54+
Create the name of the service account to use
55+
*/}}
56+
{{- define "kubernetes-mcp-server.serviceAccountName" -}}
57+
{{- if .Values.serviceAccount.create }}
58+
{{- default (include "kubernetes-mcp-server.fullname" .) .Values.serviceAccount.name }}
59+
{{- else }}
60+
{{- default "default" .Values.serviceAccount.name }}
61+
{{- end }}
62+
{{- end }}
63+
64+
{{/*
65+
Create the image path for the passed in image field
66+
*/}}
67+
{{- define "kubernetes-mcp-server.image" -}}
68+
{{- if eq (substr 0 7 .version) "sha256:" -}}
69+
{{- printf "%s/%s@%s" .registry .repository .version -}}
70+
{{- else -}}
71+
{{- printf "%s/%s:%s" .registry .repository .version -}}
72+
{{- end -}}
73+
{{- end -}}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ include "kubernetes-mcp-server.fullname" . }}
5+
namespace: {{ .Release.Namespace }}
6+
labels:
7+
{{- include "kubernetes-mcp-server.labels" . | nindent 4 }}
8+
data:
9+
config.toml: |
10+
{{- tpl (toToml .Values.config) . | replace ".0" "" | nindent 4 }}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "kubernetes-mcp-server.fullname" . }}
5+
namespace: {{ .Release.Namespace }}
6+
labels:
7+
{{- include "kubernetes-mcp-server.labels" . | nindent 4 }}
8+
spec:
9+
replicas: {{ .Values.replicaCount }}
10+
selector:
11+
matchLabels:
12+
{{- include "kubernetes-mcp-server.selectorLabels" . | nindent 6 }}
13+
template:
14+
metadata:
15+
annotations:
16+
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
17+
{{- with .Values.podAnnotations }}
18+
{{- tpl (toYaml .) . | nindent 8 }}
19+
{{- end }}
20+
labels:
21+
{{- include "kubernetes-mcp-server.labels" . | nindent 8 }}
22+
{{- with .Values.podLabels }}
23+
{{- tpl (toYaml .) . | nindent 8 }}
24+
{{- end }}
25+
spec:
26+
{{- with .Values.imagePullSecrets }}
27+
imagePullSecrets:
28+
{{- tpl (toYaml .) . | nindent 8 }}
29+
{{- end }}
30+
serviceAccountName: {{ include "kubernetes-mcp-server.serviceAccountName" . }}
31+
securityContext:
32+
{{- tpl (toYaml (default .Values.defaultPodSecurityContext .Values.podSecurityContext)) . | nindent 8 }}
33+
containers:
34+
- name: {{ .Chart.Name }}
35+
securityContext:
36+
{{- tpl (toYaml (default .Values.defaultSecurityContext .Values.securityContext)) . | nindent 12 }}
37+
image: "{{ template "kubernetes-mcp-server.image" .Values.image }}"
38+
imagePullPolicy: {{ .Values.image.pullPolicy }}
39+
ports:
40+
- name: http
41+
containerPort: {{ .Values.service.port }}
42+
protocol: TCP
43+
args:
44+
- "--config"
45+
- "{{ .Values.configFilePath }}"
46+
{{- with .Values.livenessProbe }}
47+
livenessProbe:
48+
{{- tpl (toYaml .) . | nindent 12 }}
49+
{{- end }}
50+
{{- with .Values.readinessProbe }}
51+
readinessProbe:
52+
{{- tpl (toYaml .) . | nindent 12 }}
53+
{{- end }}
54+
{{- with .Values.resources }}
55+
resources:
56+
{{- tpl (toYaml .) . | nindent 12 }}
57+
{{- end }}
58+
volumeMounts:
59+
- name: config
60+
mountPath: {{ .Values.configFilePath | dir }}
61+
{{- with .Values.extraVolumeMounts }}
62+
{{- tpl (toYaml .) . | nindent 12 }}
63+
{{- end }}
64+
{{- with .Values.extraVolumeMounts }}
65+
{{- tpl (toYaml .) . | nindent 12 }}
66+
{{- end }}
67+
volumes:
68+
- name: config
69+
configMap:
70+
name: {{ include "kubernetes-mcp-server.fullname" . }}
71+
{{- with .Values.extraVolumes }}
72+
{{- tpl (toYaml .) . | nindent 8 }}
73+
{{- end }}
74+
{{- with .Values.nodeSelector }}
75+
nodeSelector:
76+
{{- tpl (toYaml .) . | nindent 8 }}
77+
{{- end }}
78+
{{- with .Values.affinity }}
79+
affinity:
80+
{{- tpl (toYaml .) . | nindent 8 }}
81+
{{- end }}
82+
{{- with .Values.tolerations }}
83+
tolerations:
84+
{{- tpl (toYaml .) . | nindent 8 }}
85+
{{- end }}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{{- if .Values.ingress.enabled -}}
2+
{{- $host := required "Ingress hostname must be specified" (tpl .Values.ingress.host .) }}
3+
apiVersion: networking.k8s.io/v1
4+
kind: Ingress
5+
metadata:
6+
name: {{ include "kubernetes-mcp-server.fullname" . }}
7+
namespace: {{ .Release.Namespace }}
8+
labels:
9+
{{- include "kubernetes-mcp-server.labels" . | nindent 4 }}
10+
annotations:
11+
{{- if eq .Values.openshift true }}
12+
route.openshift.io/termination: {{ .Values.ingress.termination }}
13+
{{- end }}
14+
{{- with .Values.ingress.annotations }}
15+
{{- toYaml . | nindent 4 }}
16+
{{- end }}
17+
spec:
18+
{{- with .Values.ingress.className }}
19+
ingressClassName: {{ . }}
20+
{{- end }}
21+
{{- if .Values.ingress.tls }}
22+
tls:
23+
- hosts:
24+
- "{{ $host }}"
25+
secretName: {{ .Values.ingress.tls.secretName }}
26+
{{- end }}
27+
rules:
28+
- host: "{{ $host }}"
29+
http:
30+
paths:
31+
- path: {{ .Values.ingress.path }}
32+
pathType: {{ .Values.ingress.pathType }}
33+
backend:
34+
service:
35+
name: {{ include "kubernetes-mcp-server.fullname" $ }}
36+
port:
37+
number: {{ $.Values.service.port }}
38+
{{- end }}

0 commit comments

Comments
 (0)