Kubernetes & Helm¶
This guide covers deploying the MPL proxy to Kubernetes using the official Helm chart located at helm/mpl-proxy/. The chart supports standalone deployment, sidecar injection, autoscaling, observability, and network policies.
Prerequisites¶
- Kubernetes 1.24+
- Helm 3.10+
kubectlconfigured for your cluster- (Optional) Prometheus Operator for ServiceMonitor support
Helm Chart Overview¶
helm/mpl-proxy/
├── Chart.yaml # Chart metadata (v0.1.0)
├── values.yaml # Default configuration values
└── templates/
├── _helpers.tpl # Template helpers
├── configmap.yaml # MPL config and registry
├── deployment.yaml # Proxy deployment
├── hpa.yaml # Horizontal Pod Autoscaler
├── service.yaml # ClusterIP service
├── serviceaccount.yaml # RBAC service account
└── servicemonitor.yaml # Prometheus ServiceMonitor
Installation¶
Basic Install¶
Production Install¶
helm install mpl-proxy ./helm/mpl-proxy \
--set mpl.upstream=http://mcp-server:8080 \
--set mpl.mode=strict \
--set mpl.requiredProfile=qom-strict-argcheck \
--set autoscaling.enabled=true \
--set serviceMonitor.enabled=true \
--set networkPolicy.enabled=true
Install with Custom Values File¶
Upgrade¶
Uninstall¶
Values Reference¶
Image Configuration¶
image:
repository: ghcr.io/skelf-research/mpl-proxy
tag: "" # Defaults to Chart.appVersion (0.1.0)
pullPolicy: IfNotPresent
imagePullSecrets: []
Replicas¶
MPL Configuration¶
mpl:
# Proxy mode: transparent (log only) or strict (block invalid)
mode: transparent
# Registry URL or path for SType schemas
registry: "https://github.com/Skelf-Research/mpl/raw/main/registry"
# Required QoM profile for validation
requiredProfile: qom-basic
# Enable schema validation
enforceSchema: true
# Enable assertion checks
enforceAssertions: true
# Enable the policy engine
policyEngine: false
Transport¶
transport:
# Listen port for proxy traffic
port: 9443
# Upstream MCP/A2A server address
upstream: ""
# Protocol: http, websocket, grpc
protocol: http
Observability¶
observability:
# Prometheus metrics port
metricsPort: 9100
# Log level: trace, debug, info, warn, error
logLevel: info
# Log format: json, text
logFormat: json
Service¶
Ingress¶
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: mpl-proxy.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
Ingress with TLS
Resources¶
Autoscaling (HPA)¶
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
Health Probes¶
probes:
liveness:
enabled: true
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readiness:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
ServiceMonitor (Prometheus Operator)¶
Network Policy¶
Sidecar Pattern¶
The most common production pattern is injecting the MPL proxy as a sidecar container alongside your existing MCP/A2A server pod. This ensures all traffic is validated without changing your application code.
Sidecar Deployment Example¶
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server-with-mpl
spec:
replicas: 3
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9100"
spec:
containers:
# Your existing MCP server
- name: mcp-server
image: your-org/mcp-server:latest
ports:
- containerPort: 8080
# MPL sidecar proxy
- name: mpl-proxy
image: ghcr.io/skelf-research/mpl-proxy:0.1.0
args: ["--config", "/config/mpl-config.yaml"]
ports:
- name: proxy
containerPort: 9443
- name: metrics
containerPort: 9100
volumeMounts:
- name: mpl-config
mountPath: /config
readOnly: true
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
livenessProbe:
httpGet:
path: /health
port: proxy
initialDelaySeconds: 10
readinessProbe:
httpGet:
path: /health
port: proxy
initialDelaySeconds: 5
volumes:
- name: mpl-config
configMap:
name: mpl-sidecar-config
Sidecar ConfigMap¶
apiVersion: v1
kind: ConfigMap
metadata:
name: mpl-sidecar-config
data:
mpl-config.yaml: |
transport:
listen: "0.0.0.0:9443"
upstream: "localhost:8080" # Same pod, via localhost
protocol: http
mpl:
registry: "https://github.com/Skelf-Research/mpl/raw/main/registry"
mode: strict
required_profile: qom-strict-argcheck
enforce_schema: true
enforce_assertions: true
observability:
metrics_port: 9100
log_format: json
log_level: info
Sidecar Traffic Flow
External traffic hits the MPL sidecar on port 9443. After validation, the proxy forwards to the MCP server on localhost:8080 within the same pod. This pattern requires updating your Service to target port 9443 instead of 8080.
ServiceMonitor for Prometheus Operator¶
When using the Prometheus Operator, enable the ServiceMonitor to auto-discover MPL metrics:
serviceMonitor:
enabled: true
namespace: monitoring # Namespace where Prometheus runs
interval: 30s
scrapeTimeout: 10s
labels:
release: prometheus # Match your Prometheus selector
This creates a ServiceMonitor resource:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: mpl-proxy
labels:
release: prometheus
spec:
endpoints:
- port: metrics
interval: 30s
scrapeTimeout: 10s
path: /metrics
selector:
matchLabels:
app.kubernetes.io/name: mpl-proxy
Network Policies¶
Restrict traffic to only expected sources and destinations:
networkPolicy:
enabled: true
ingress:
- from:
- podSelector:
matchLabels:
app: ai-agent
ports:
- port: 9443
protocol: TCP
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- port: 9100
protocol: TCP
egress:
- to:
- podSelector:
matchLabels:
app: mcp-server
ports:
- port: 8080
protocol: TCP
DNS Access
If network policies are enabled, ensure egress to kube-dns (port 53) is allowed, or the proxy will fail to resolve upstream hostnames.
Pod Disruption Budget¶
For high-availability deployments, add a PodDisruptionBudget to prevent all replicas from being evicted simultaneously:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: mpl-proxy-pdb
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: mpl-proxy
Recommendation
With autoscaling.minReplicas: 2 or higher, set minAvailable: 1 to ensure at least one proxy remains running during node drains and cluster upgrades.
ConfigMap for Registry and Config¶
The Helm chart automatically creates a ConfigMap from your mpl-config.yaml. To also bundle the registry schemas:
apiVersion: v1
kind: ConfigMap
metadata:
name: mpl-proxy-config
data:
mpl-config.yaml: |
transport:
listen: "0.0.0.0:9443"
upstream: "mcp-server:8080"
protocol: http
mpl:
registry: "/config/registry"
mode: strict
required_profile: qom-strict-argcheck
enforce_schema: true
enforce_assertions: true
observability:
metrics_port: 9100
log_format: json
log_level: info
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mpl-proxy-registry
data:
calendar-event.schema.json: |
{ "$schema": "https://json-schema.org/draft/2020-12/schema", ... }
finance-transaction.schema.json: |
{ "$schema": "https://json-schema.org/draft/2020-12/schema", ... }
Mount both in the deployment:
volumeMounts:
- name: config
mountPath: /config/mpl-config.yaml
subPath: mpl-config.yaml
- name: registry
mountPath: /config/registry
readOnly: true
volumes:
- name: config
configMap:
name: mpl-proxy-config
- name: registry
configMap:
name: mpl-proxy-registry
Security Context¶
The chart applies a restrictive security context by default:
podSecurityContext:
fsGroup: 1000
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
Security Defaults
- Runs as non-root user (UID 1000)
- Drops all Linux capabilities
- Read-only root filesystem
- Writable
/tmpvia emptyDir volume
Example: Full Production Values¶
image:
repository: ghcr.io/skelf-research/mpl-proxy
tag: "0.1.0"
replicaCount: 3
mpl:
mode: strict
requiredProfile: qom-strict-argcheck
enforceSchema: true
enforceAssertions: true
policyEngine: true
transport:
port: 9443
upstream: http://mcp-server.default.svc.cluster.local:8080
protocol: http
observability:
metricsPort: 9100
logLevel: info
logFormat: json
resources:
limits:
cpu: "1"
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
probes:
liveness:
enabled: true
initialDelaySeconds: 10
periodSeconds: 10
readiness:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
serviceMonitor:
enabled: true
interval: 15s
labels:
release: prometheus
networkPolicy:
enabled: true
ingress:
- from:
- podSelector:
matchLabels:
role: ai-agent
ports:
- port: 9443
egress:
- to:
- podSelector:
matchLabels:
app: mcp-server
ports:
- port: 8080
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
hosts:
- host: mpl.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: mpl-tls
hosts:
- mpl.example.com
Deploy:
Verifying the Deployment¶
# Check pods are running
kubectl get pods -l app.kubernetes.io/name=mpl-proxy
# Check the service
kubectl get svc mpl-proxy
# View proxy logs
kubectl logs -l app.kubernetes.io/name=mpl-proxy -f
# Test health endpoint
kubectl port-forward svc/mpl-proxy 9443:9443
curl http://localhost:9443/health
# Check metrics
kubectl port-forward svc/mpl-proxy 9100:9100
curl http://localhost:9100/metrics
Next Steps¶
- Production Checklist -- Verify all requirements before going live
- Monitoring & Metrics -- Configure dashboards and alerts
- Troubleshooting -- Debug common Kubernetes issues