Skip to content

Commit da26c46

Browse files
committed
🧱(mcp) add server deployment
Provide the helm chart declaration to deploy the MCP server.
1 parent d85d768 commit da26c46

File tree

7 files changed

+419
-3
lines changed

7 files changed

+419
-3
lines changed

bin/Tiltfile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,19 @@ docker_build(
3939
]
4040
)
4141

42+
docker_build(
43+
'localhost:5001/impress-mcp-server:latest',
44+
context='../src/mcp_server',
45+
dockerfile='../src/mcp_server/Dockerfile',
46+
)
47+
4248
k8s_resource('impress-docs-backend-migrate', resource_deps=['postgres-postgresql'])
4349
k8s_resource('impress-docs-backend-createsuperuser', resource_deps=['impress-docs-backend-migrate'])
4450
k8s_resource('impress-docs-backend', resource_deps=['impress-docs-backend-migrate'])
45-
k8s_yaml(local('cd ../src/helm && helmfile -n impress -e dev template .'))
51+
52+
# helmfile in docker mount the current working directory and the helmfile.yaml
53+
# requires the keycloak config in another directory
54+
k8s_yaml(local('cd .. && helmfile -n impress -e ${DEV_ENV:-dev} template --file ./src/helm/helmfile.yaml'))
4655

4756
migration = '''
4857
set -eu

src/helm/env.d/dev/values.impress.yaml.gotmpl

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,25 @@ backend:
2929
DJANGO_EMAIL_PORT: 1025
3030
DJANGO_EMAIL_USE_SSL: False
3131
LOGGING_LEVEL_HANDLERS_CONSOLE: ERROR
32-
LOGGING_LEVEL_LOGGERS_ROOT: INFO
33-
LOGGING_LEVEL_LOGGERS_APP: INFO
32+
LOGGING_LEVEL_LOGGERS_ROOT: DEBUG
33+
LOGGING_LEVEL_LOGGERS_APP: DEBUG
3434
OIDC_USERINFO_SHORTNAME_FIELD: "given_name"
3535
OIDC_USERINFO_FULLNAME_FIELDS: "given_name,usual_name"
36+
OIDC_OP_URL: https://keycloak.127.0.0.1.nip.io/realms/people
3637
OIDC_OP_JWKS_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/certs
3738
OIDC_OP_AUTHORIZATION_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/auth
3839
OIDC_OP_TOKEN_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/token
3940
OIDC_OP_USER_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/userinfo
4041
OIDC_OP_LOGOUT_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/logout
42+
OIDC_OP_INTROSPECTION_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/token/introspect
4143
OIDC_RP_CLIENT_ID: impress
4244
OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
4345
OIDC_RP_SIGN_ALGO: RS256
4446
OIDC_RP_SCOPES: "openid email"
47+
OIDC_RS_CLIENT_ID: impress
48+
OIDC_RS_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
49+
OIDC_RS_SIGNING_ALGO: RS256
50+
OIDC_RS_SCOPES: "openid,profile,email"
4551
LOGIN_REDIRECT_URL: https://impress.127.0.0.1.nip.io
4652
LOGIN_REDIRECT_URL_FAILURE: https://impress.127.0.0.1.nip.io
4753
LOGOUT_REDIRECT_URL: https://impress.127.0.0.1.nip.io
@@ -171,3 +177,20 @@ ingressMedia:
171177
serviceMedia:
172178
host: minio.impress.svc.cluster.local
173179
port: 9000
180+
181+
182+
mcpServer:
183+
replicas: 1
184+
185+
image:
186+
repository: localhost:5001/impress-mcp-server
187+
pullPolicy: Always
188+
tag: "latest"
189+
190+
envVars:
191+
DOCS_API_URL: https://impress.127.0.0.1.nip.io/
192+
SERVER_TRANSPORT: STREAMABLE_HTTP
193+
194+
ingressMcpServer:
195+
enabled: true
196+
host: impress.127.0.0.1.nip.io

src/helm/impress/templates/_helpers.tpl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,15 @@ Requires top level scope
178178
{{ include "impress.fullname" . }}-celery-worker
179179
{{- end }}
180180

181+
{{/*
182+
Full name for the MCP server
183+
184+
Requires top level scope
185+
*/}}
186+
{{- define "impress.mcpServer.fullname" -}}
187+
{{ include "impress.fullname" . }}-mcp-server
188+
{{- end }}
189+
181190
{{/*
182191
Usage : {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" .Values.path.to.the.image1) }}
183192
*/}}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{{- if .Values.ingressMcpServer.enabled -}}
2+
{{- $fullName := include "impress.fullname" . -}}
3+
{{- if and .Values.ingressMcpServer.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
4+
{{- if not (hasKey .Values.ingressMcpServer.annotations "kubernetes.io/ingress.class") }}
5+
{{- $_ := set .Values.ingressMcpServer.annotations "kubernetes.io/ingress.class" .Values.ingressMcpServer.className}}
6+
{{- end }}
7+
{{- end }}
8+
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
9+
apiVersion: networking.k8s.io/v1
10+
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
11+
apiVersion: networking.k8s.io/v1beta1
12+
{{- else -}}
13+
apiVersion: extensions/v1beta1
14+
{{- end }}
15+
kind: Ingress
16+
metadata:
17+
name: {{ $fullName }}-mcp-server
18+
namespace: {{ .Release.Namespace | quote }}
19+
labels:
20+
{{- include "impress.labels" . | nindent 4 }}
21+
{{- with .Values.ingressMcpServer.annotations }}
22+
annotations:
23+
{{- toYaml . | nindent 4 }}
24+
{{- end }}
25+
spec:
26+
{{- if and .Values.ingressMcpServer.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
27+
ingressClassName: {{ .Values.ingressMcpServer.className }}
28+
{{- end }}
29+
{{- if .Values.ingressMcpServer.tls.enabled }}
30+
tls:
31+
{{- if .Values.ingressMcpServer.host }}
32+
- secretName: {{ .Values.ingressMcpServer.tls.secretName | default (printf "%s-tls" $fullName) | quote }}
33+
hosts:
34+
- {{ .Values.ingressMcpServer.host | quote }}
35+
{{- end }}
36+
{{- range .Values.ingressMcpServer.tls.additional }}
37+
- hosts:
38+
{{- range .hosts }}
39+
- {{ . | quote }}
40+
{{- end }}
41+
secretName: {{ .secretName }}
42+
{{- end }}
43+
{{- end }}
44+
rules:
45+
{{- if .Values.ingressMcpServer.host }}
46+
- host: {{ .Values.ingressMcpServer.host | quote }}
47+
http:
48+
paths:
49+
- path: {{ .Values.ingressMcpServer.path | quote }}
50+
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
51+
pathType: Prefix
52+
{{- end }}
53+
backend:
54+
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
55+
service:
56+
name: {{ include "impress.mcpServer.fullname" . }}
57+
port:
58+
number: {{ .Values.mcpServer.service.port }}
59+
{{- else }}
60+
serviceName: {{ include "impress.mcpServer.fullname" . }}
61+
servicePort: {{ .Values.mcpServer.service.port }}
62+
{{- end }}
63+
{{- with .Values.ingressMcpServer.customBackends }}
64+
{{- toYaml . | nindent 10 }}
65+
{{- end }}
66+
{{- end }}
67+
{{- range .Values.ingressMcpServer.hosts }}
68+
- host: {{ . | quote }}
69+
http:
70+
paths:
71+
- path: {{ $.Values.ingressMcpServer.path | quote }}
72+
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
73+
pathType: Prefix
74+
{{- end }}
75+
backend:
76+
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
77+
service:
78+
name: {{ include "impress.mcpServer.fullname" $ }}
79+
port:
80+
number: {{ $.Values.mcpServer.service.port }}
81+
{{- else }}
82+
serviceName: {{ include "impress.mcpServer.fullname" $ }}
83+
servicePort: {{ $.Values.mcpServer.service.port }}
84+
{{- end }}
85+
{{- with $.Values.ingressMcpServer.customBackends }}
86+
{{- toYaml . | nindent 10 }}
87+
{{- end }}
88+
{{- end }}
89+
{{- end }}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
{{- $envVars := include "impress.common.env" (list . .Values.mcpServer) -}}
2+
{{- $fullName := include "impress.mcpServer.fullname" . -}}
3+
{{- $component := "mcp-server" -}}
4+
apiVersion: apps/v1
5+
kind: Deployment
6+
metadata:
7+
name: {{ $fullName }}
8+
namespace: {{ .Release.Namespace | quote }}
9+
annotations:
10+
{{- with .Values.mcpServer.dpAnnotations }}
11+
{{- toYaml . | nindent 4 }}
12+
{{- end }}
13+
labels:
14+
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
15+
spec:
16+
replicas: {{ .Values.backend.replicas }}
17+
selector:
18+
matchLabels:
19+
{{- include "impress.common.selectorLabels" (list . $component) | nindent 6 }}
20+
template:
21+
metadata:
22+
annotations:
23+
{{- with .Values.backend.podAnnotations }}
24+
{{- toYaml . | nindent 8 }}
25+
{{- end }}
26+
labels:
27+
{{- include "impress.common.selectorLabels" (list . $component) | nindent 8 }}
28+
spec:
29+
{{- if $.Values.image.credentials }}
30+
imagePullSecrets:
31+
- name: {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" $.Values.image.credentials) }}
32+
{{- end}}
33+
shareProcessNamespace: {{ .Values.backend.shareProcessNamespace }}
34+
containers:
35+
{{- with .Values.mcpServer.sidecars }}
36+
{{- toYaml . | nindent 8 }}
37+
{{- end }}
38+
- name: {{ .Chart.Name }}
39+
image: "{{ (.Values.mcpServer.image | default dict).repository }}:{{ (.Values.mcpServer.image | default dict).tag }}"
40+
imagePullPolicy: {{ (.Values.mcpServer.image | default dict).pullPolicy }}
41+
{{- with .Values.mcpServer.command }}
42+
command:
43+
{{- toYaml . | nindent 12 }}
44+
{{- end }}
45+
{{- with .Values.mcpServer.args }}
46+
args:
47+
{{- toYaml . | nindent 12 }}
48+
{{- end }}
49+
env:
50+
{{- if $envVars}}
51+
{{- $envVars | indent 12 }}
52+
{{- end }}
53+
{{- with .Values.mcpServer.securityContext }}
54+
securityContext:
55+
{{- toYaml . | nindent 12 }}
56+
{{- end }}
57+
ports:
58+
- name: http
59+
containerPort: {{ .Values.mcpServer.service.targetPort }}
60+
protocol: TCP
61+
{{- if .Values.mcpServer.probes.liveness }}
62+
livenessProbe:
63+
{{- include "impress.probes.abstract" (merge .Values.mcpServer.probes.liveness (dict "targetPort" .Values.mcpServer.service.targetPort )) | nindent 12 }}
64+
{{- end }}
65+
{{- if .Values.mcpServer.probes.readiness }}
66+
readinessProbe:
67+
{{- include "impress.probes.abstract" (merge .Values.mcpServer.probes.readiness (dict "targetPort" .Values.mcpServer.service.targetPort )) | nindent 12 }}
68+
{{- end }}
69+
{{- if .Values.mcpServer.probes.startup }}
70+
startupProbe:
71+
{{- include "impress.probes.abstract" (merge .Values.mcpServer.probes.startup (dict "targetPort" .Values.mcpServer.service.targetPort )) | nindent 12 }}
72+
{{- end }}
73+
{{- with .Values.mcpServer.resources }}
74+
resources:
75+
{{- toYaml . | nindent 12 }}
76+
{{- end }}
77+
volumeMounts:
78+
{{- range $index, $value := .Values.mountFiles }}
79+
- name: "files-{{ $index }}"
80+
mountPath: {{ $value.path }}
81+
subPath: content
82+
{{- end }}
83+
{{- range $name, $volume := .Values.mcpServer.persistence }}
84+
- name: "{{ $name }}"
85+
mountPath: "{{ $volume.mountPath }}"
86+
{{- end }}
87+
{{- range .Values.mcpServer.extraVolumeMounts }}
88+
- name: {{ .name }}
89+
mountPath: {{ .mountPath }}
90+
subPath: {{ .subPath | default "" }}
91+
readOnly: {{ .readOnly }}
92+
{{- end }}
93+
{{- with .Values.mcpServer.nodeSelector }}
94+
nodeSelector:
95+
{{- toYaml . | nindent 8 }}
96+
{{- end }}
97+
{{- with .Values.mcpServer.affinity }}
98+
affinity:
99+
{{- toYaml . | nindent 8 }}
100+
{{- end }}
101+
{{- with .Values.mcpServer.tolerations }}
102+
tolerations:
103+
{{- toYaml . | nindent 8 }}
104+
{{- end }}
105+
volumes:
106+
{{- range $index, $value := .Values.mountFiles }}
107+
- name: "files-{{ $index }}"
108+
configMap:
109+
name: "{{ include "impress.fullname" $ }}-files-{{ $index }}"
110+
{{- end }}
111+
{{- range $name, $volume := .Values.mcpServer.persistence }}
112+
- name: "{{ $name }}"
113+
{{- if eq $volume.type "emptyDir" }}
114+
emptyDir: {}
115+
{{- else }}
116+
persistentVolumeClaim:
117+
claimName: "{{ $fullName }}-{{ $name }}"
118+
{{- end }}
119+
{{- end }}
120+
{{- range .Values.mcpServer.extraVolumes }}
121+
- name: {{ .name }}
122+
{{- if .existingClaim }}
123+
persistentVolumeClaim:
124+
claimName: {{ .existingClaim }}
125+
{{- else if .hostPath }}
126+
hostPath:
127+
{{ toYaml .hostPath | nindent 12 }}
128+
{{- else if .csi }}
129+
csi:
130+
{{- toYaml .csi | nindent 12 }}
131+
{{- else if .configMap }}
132+
configMap:
133+
{{- toYaml .configMap | nindent 12 }}
134+
{{- else if .emptyDir }}
135+
emptyDir:
136+
{{- toYaml .emptyDir | nindent 12 }}
137+
{{- else }}
138+
emptyDir: {}
139+
{{- end }}
140+
{{- end }}
141+
---
142+
{{ if .Values.mcpServer.pdb.enabled }}
143+
apiVersion: policy/v1
144+
kind: PodDisruptionBudget
145+
metadata:
146+
name: {{ $fullName }}
147+
namespace: {{ .Release.Namespace | quote }}
148+
spec:
149+
maxUnavailable: 1
150+
selector:
151+
matchLabels:
152+
{{- include "impress.common.selectorLabels" (list . $component) | nindent 6 }}
153+
{{ end }}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{{- $envVars := include "impress.common.env" (list . .Values.mcpServer) -}}
2+
{{- $fullName := include "impress.mcpServer.fullname" . -}}
3+
{{- $component := "mcp-server" -}}
4+
apiVersion: v1
5+
kind: Service
6+
metadata:
7+
name: {{ $fullName }}
8+
namespace: {{ .Release.Namespace | quote }}
9+
labels:
10+
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
11+
annotations:
12+
{{- toYaml $.Values.mcpServer.service.annotations | nindent 4 }}
13+
spec:
14+
type: {{ .Values.mcpServer.service.type }}
15+
ports:
16+
- port: {{ .Values.mcpServer.service.port }}
17+
targetPort: {{ .Values.mcpServer.service.targetPort }}
18+
protocol: TCP
19+
name: http
20+
selector:
21+
{{- include "impress.common.selectorLabels" (list . $component) | nindent 4 }}

0 commit comments

Comments
 (0)