Multi-Agent Deployment¶
Deploy multiple MCP Mesh agents with the registry using individual charts
Overview¶
While MCP Mesh doesn't currently have an umbrella chart, you can deploy multiple agents alongside the registry using the individual charts. This guide shows how to deploy a complete platform using the existing mcp-mesh-registry
and mcp-mesh-agent
charts, and provides a template for creating your own umbrella chart.
We'll show how to deploy the registry and multiple agents systematically, and then show how to create an umbrella chart for automated deployment.
Key Concepts¶
- Multi-Chart Deployment: Using individual charts to build a platform
- Dependency Management: Deploying components in the correct order
- Value Consistency: Ensuring compatible configuration across charts
- Service Discovery: Connecting agents to the registry
- Chart Aliases: Deploying multiple instances of the same chart
Step-by-Step Guide¶
Step 1: Deploy the Registry First¶
Start by deploying the registry service:
# From the project root/helm directory
cd helm
# Create namespace
kubectl create namespace mcp-mesh
# Deploy the registry
helm install mcp-registry ./mcp-mesh-registry \
--namespace mcp-mesh \
--values values-registry.yaml
# Wait for registry to be ready
kubectl wait --for=condition=available deployment/mcp-registry \
-n mcp-mesh --timeout=300s
# Verify registry is running
kubectl get pods -n mcp-mesh -l app.kubernetes.io/name=mcp-mesh-registry
Create registry values file:
# values-registry.yaml
replicaCount: 1
image:
repository: mcp-mesh-base
tag: "0.5"
pullPolicy: Never
service:
port: 8000
registry:
host: "0.0.0.0"
port: 8000
database:
type: sqlite
path: /data/registry.db
persistence:
enabled: true
size: 5Gi
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "500m"
Step 2: Deploy Multiple Agents¶
Deploy various agents that connect to the registry:
# Deploy hello-world agent
helm install hello-world-agent ./mcp-mesh-agent \
--namespace mcp-mesh \
--values values-hello-world.yaml
# Deploy system agent
helm install system-agent ./mcp-mesh-agent \
--namespace mcp-mesh \
--values values-system.yaml
# Deploy weather agent
helm install weather-agent ./mcp-mesh-agent \
--namespace mcp-mesh \
--values values-weather.yaml
# Verify all agents are running
kubectl get pods -n mcp-mesh
Create agent values files:
# values-hello-world.yaml
agent:
name: hello-world-agent
script: hello_world.py
http:
port: 8080
registryUrl: "http://mcp-registry-mcp-mesh-registry:8000"
capabilities:
- greeting
- translation
image:
repository: mcp-mesh-base
tag: "0.5"
pullPolicy: Never
resources:
requests:
memory: "128Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "100m"
# values-system.yaml
agent:
name: system-agent
script: system_agent.py
http:
port: 8080
registryUrl: "http://mcp-registry-mcp-mesh-registry:8000"
capabilities:
- file_operations
- system_info
image:
repository: mcp-mesh-base
tag: "0.5"
pullPolicy: Never
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "200m"
# values-weather.yaml
agent:
name: weather-agent
script: weather_agent.py
http:
port: 8080
registryUrl: "http://mcp-registry-mcp-mesh-registry:8000"
capabilities:
- weather_forecast
- weather_current
dependencies:
- location_service
image:
repository: mcp-mesh-base
tag: "0.5"
pullPolicy: Never
env:
WEATHER_API_KEY: "your-api-key"
CACHE_TTL: "300"
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
resources:
requests:
memory: "128Mi"
cpu: "50m"
limits:
memory: "512Mi"
cpu: "200m"
Step 3: Create an Umbrella Chart (Optional)¶
For future use, you can create an umbrella chart to deploy everything together:
# Create umbrella chart directory
mkdir mcp-mesh-platform
cd mcp-mesh-platform
# Create Chart.yaml
cat > Chart.yaml << 'EOF'
apiVersion: v2
name: mcp-mesh-platform
description: Complete MCP Mesh platform deployment
type: application
version: 1.0.0
appVersion: "1.0.0"
keywords:
- mcp-mesh
- platform
- microservices
dependencies:
# Core registry
- name: mcp-mesh-registry
version: "0.5.6"
repository: "file://../mcp-mesh-registry"
condition: registry.enabled
# Agents using aliases for multiple instances
- name: mcp-mesh-agent
version: "0.5.6"
repository: "file://../mcp-mesh-agent"
alias: hello-world-agent
condition: agents.helloWorld.enabled
- name: mcp-mesh-agent
version: "0.5.6"
repository: "file://../mcp-mesh-agent"
alias: system-agent
condition: agents.system.enabled
- name: mcp-mesh-agent
version: "0.5.6"
repository: "file://../mcp-mesh-agent"
alias: weather-agent
condition: agents.weather.enabled
EOF
Create umbrella chart values:
# values.yaml
# Global settings
global:
imageRegistry: ""
namespace: mcp-mesh
# Registry configuration
registry:
enabled: true
mcp-mesh-registry:
replicaCount: 1
image:
repository: mcp-mesh-base
tag: "0.5.6"
pullPolicy: Never
service:
port: 8000
persistence:
enabled: true
size: 5Gi
# Agent configurations
agents:
helloWorld:
enabled: true
system:
enabled: true
weather:
enabled: true
hello-world-agent:
agent:
name: hello-world-agent
script: hello_world.py
registryUrl: "http://mcp-mesh-platform-mcp-mesh-registry:8000"
capabilities:
- greeting
- translation
image:
repository: mcp-mesh-base
tag: "0.5.6"
pullPolicy: Never
system-agent:
agent:
name: system-agent
script: system_agent.py
registryUrl: "http://mcp-mesh-platform-mcp-mesh-registry:8000"
capabilities:
- file_operations
- system_info
image:
repository: mcp-mesh-base
tag: "0.5.6"
pullPolicy: Never
weather-agent:
agent:
name: weather-agent
script: weather_agent.py
registryUrl: "http://mcp-mesh-platform-mcp-mesh-registry:8000"
capabilities:
- weather_forecast
- weather_current
image:
repository: mcp-mesh-base
tag: "0.5.6"
pullPolicy: Never
env:
WEATHER_API_KEY: "your-api-key"
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
Deploy the platform:
# Update dependencies
helm dependency update ./mcp-mesh-platform
# Deploy the complete platform
helm install mcp-platform ./mcp-mesh-platform \
--namespace mcp-mesh \
--create-namespace
Platform-wide labels */}} {% raw %}{{- define "mcp-mesh-platform.labels" -}}{% endraw %} app.kubernetes.io/part-of: mcp-mesh-platform app.kubernetes.io/managed-by: {% raw %}{{ .Release.Service }}{% endraw %} helm.sh/chart: {% raw %}{{ include "mcp-mesh-platform.chart" . }}{% endraw %} {% raw %}{{- end }}{% endraw %}
{{/* Registry URL for agents */}} {% raw %}{{- define "mcp-mesh-platform.registryUrl" -}}{% endraw %} {% raw %}{{- if .Values.registry.externalUrl -}}{% endraw %} {% raw %}{{- .Values.registry.externalUrl -}}{% endraw %} {% raw %}{{- else -}}{% endraw %} http://{% raw %}{{ .Release.Name }}{% endraw %}-mcp-mesh-registry:{% raw %}{{ .Values.registry.service.port | default 8080 }}{% endraw %} {% raw %}{{- end -}}{% endraw %} {% raw %}{{- end }}{% endraw %}
{{/* Database connection string */}} {% raw %}{{- define "mcp-mesh-platform.databaseUrl" -}}{% endraw %} {% raw %}{{- if .Values.postgresql.enabled -}}{% endraw %} postgresql://{% raw %}{{ .Values.global.postgresql.auth.username | default "postgres" }}{% endraw %}:{% raw %}{{ .Values.global.postgresql.auth.postgresPassword }}{% endraw %}@{% raw %}{{ .Release.Name }}{% endraw %}-postgresql:5432/{% raw %}{{ .Values.global.postgresql.auth.database }}{% endraw %} {% raw %}{{- else -}}{% endraw %} {% raw %}{{- required "External database URL required when postgresql.enabled=false" .Values.registry.database.externalUrl -}}{% endraw %} {% raw %}{{- end -}}{% endraw %} {% raw %}{{- end }}{% endraw %}
### Step 4: Add Platform-Level Resources
Create additional resources for the platform:
```yaml
# templates/namespace.yaml
{% raw %}{{- if .Values.createNamespace }}{% endraw %}
apiVersion: v1
kind: Namespace
metadata:
name: {% raw %}{{ .Release.Namespace }}{% endraw %}
labels:
{% raw %}{{- include "mcp-mesh-platform.labels" . | nindent 4 }}{% endraw %}
{% raw %}{{- end }}{% endraw %}
---
# templates/resourcequota.yaml
{% raw %}{{- if .Values.resourceQuota.enabled }}{% endraw %}
apiVersion: v1
kind: ResourceQuota
metadata:
name: {% raw %}{{ .Release.Name }}{% endraw %}-quota
namespace: {% raw %}{{ .Release.Namespace }}{% endraw %}
spec:
hard:
{% raw %}{{- toYaml .Values.resourceQuota.hard | nindent 4 }}{% endraw %}
{% raw %}{{- end }}{% endraw %}
---
# templates/networkpolicy.yaml
{% raw %}{{- if .Values.networkPolicies.enabled }}{% endraw %}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {% raw %}{{ .Release.Name }}{% endraw %}-default-deny
namespace: {% raw %}{{ .Release.Namespace }}{% endraw %}
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
egress:
# Allow DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow intra-namespace
{% raw %}{{- if .Values.networkPolicies.allowIntraNamespace }}{% endraw %}
- to:
- podSelector: {}
{% raw %}{{- end }}{% endraw %}
ingress:
# Allow from allowed namespaces
{% raw %}{{- range .Values.networkPolicies.allowedNamespaces }}{% endraw %}
- from:
- namespaceSelector:
matchLabels:
name: {% raw %}{{ . }}{% endraw %}
{% raw %}{{- end }}{% endraw %}
# Allow intra-namespace
{% raw %}{{- if .Values.networkPolicies.allowIntraNamespace }}{% endraw %}
- from:
- podSelector: {}
{% raw %}{{- end }}{% endraw %}
{% raw %}{{- end }}{% endraw %}
Step 5: Create Deployment Scripts¶
Add convenient deployment scripts:
# templates/NOTES.txt
{% raw %}{{- $registryUrl := include "mcp-mesh-platform.registryUrl" . -}}{% endraw %}
MCP Mesh Platform has been deployed!
Registry URL: {% raw %}{{ $registryUrl }}{% endraw %}
To access the services:
1. Registry:
{% raw %}{{- if .Values.registry.ingress.enabled }}{% endraw %}
URL: http://{% raw %}{{ (index .Values.registry.ingress.hosts 0).host }}{% endraw %}
{% raw %}{{- else }}{% endraw %}
kubectl port-forward -n {% raw %}{{ .Release.Namespace }}{% endraw %} svc/{% raw %}{{ .Release.Name }}{% endraw %}-mcp-mesh-registry 8080:8080
{% raw %}{{- end }}{% endraw %}
2. Grafana Dashboard:
{% raw %}{{- if .Values.monitoring.grafana.enabled }}{% endraw %}
kubectl port-forward -n {% raw %}{{ .Release.Namespace }}{% endraw %} svc/{% raw %}{{ .Release.Name }}{% endraw %}-grafana 3000:80
Username: admin
Password: {% raw %}{{ .Values.monitoring.grafana.adminPassword }}{% endraw %}
{% raw %}{{- end }}{% endraw %}
3. Prometheus:
{% raw %}{{- if .Values.monitoring.prometheus.enabled }}{% endraw %}
kubectl port-forward -n {% raw %}{{ .Release.Namespace }}{% endraw %} svc/{% raw %}{{ .Release.Name }}{% endraw %}-prometheus-server 9090:80
{% raw %}{{- end }}{% endraw %}
Deployed Agents:
{% raw %}{{- if .Values.agents.weather.enabled }}{% endraw %}
- Weather Agent: {% raw %}{{ .Values.agents.weather.replicaCount }}{% endraw %} replicas
{% raw %}{{- end }}{% endraw %}
{% raw %}{{- if .Values.agents.analytics.enabled }}{% endraw %}
- Analytics Agent: {% raw %}{{ .Values.agents.analytics.replicaCount }}{% endraw %} replicas
{% raw %}{{- end }}{% endraw %}
{% raw %}{{- if .Values.agents.notification.enabled }}{% endraw %}
- Notification Agent: {% raw %}{{ .Values.agents.notification.replicaCount }}{% endraw %} replicas
{% raw %}{{- end }}{% endraw %}
To check platform status:
helm status {% raw %}{{ .Release.Name }}{% endraw %} -n {% raw %}{{ .Release.Namespace }}{% endraw %}
To view all platform resources:
kubectl get all -n {% raw %}{{ .Release.Namespace }}{% endraw %} -l app.kubernetes.io/part-of=mcp-mesh-platform
Configuration Options¶
Section | Key | Description | Default |
---|---|---|---|
global | imageRegistry | Override all image registries | "" |
registry | enabled | Deploy registry | true |
postgresql | enabled | Deploy PostgreSQL | true |
agents.* | enabled | Enable specific agents | varies |
monitoring.* | enabled | Enable monitoring components | false |
Examples¶
Example 1: Minimal Platform Deployment¶
# values-minimal.yaml
# Deploy only registry and one agent
registry:
enabled: true
replicaCount: 1
persistence:
enabled: false
postgresql:
enabled: false
registry:
database:
type: sqlite
agents:
weather:
enabled: true
replicaCount: 1
analytics:
enabled: false
notification:
enabled: false
monitoring:
prometheus:
enabled: false
grafana:
enabled: false
Deploy:
Example 2: Production Platform¶
# values-production.yaml
global:
imageRegistry: "myregistry.io"
imagePullSecrets:
- name: regcred
registry:
enabled: true
replicaCount: 5
persistence:
enabled: true
size: 100Gi
storageClass: fast-ssd
resources:
requests:
memory: "2Gi"
cpu: "1"
limits:
memory: "4Gi"
cpu: "2"
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- mcp-mesh-registry
topologyKey: kubernetes.io/hostname
postgresql:
enabled: true
auth:
postgresPassword: "${POSTGRES_PASSWORD}"
primary:
persistence:
size: 200Gi
storageClass: fast-ssd
metrics:
enabled: true
agents:
weather:
enabled: true
replicaCount: 10
autoscaling:
enabled: true
minReplicas: 10
maxReplicas: 50
analytics:
enabled: true
replicaCount: 20
persistence:
enabled: true
size: 1Ti
storageClass: standard
monitoring:
prometheus:
enabled: true
server:
persistentVolume:
size: 100Gi
retention: "30d"
grafana:
enabled: true
persistence:
enabled: true
size: 20Gi
Best Practices¶
- Use Dependency Conditions: Make components optional with conditions
- Global Values: Share common configuration in global section
- Value Validation: Add schema validation for complex values
- Atomic Deployments: Use
--atomic
flag for all-or-nothing deploys - Version Lock: Pin all dependency versions for reproducibility
Common Pitfalls¶
Pitfall 1: Circular Dependencies¶
Problem: Charts depend on each other causing deadlock
Solution: Design clear dependency hierarchy:
# Good: Clear hierarchy
postgresql -> registry -> agents
# Bad: Circular dependency
agentA -> agentB -> agentA
Pitfall 2: Value Conflicts¶
Problem: Sub-chart values override each other
Solution: Use proper nesting:
# Correct: Each chart has its own section
weather-agent:
agent:
name: weather
analytics-agent:
agent:
name: analytics
Testing¶
Test Platform Deployment¶
#!/bin/bash
# test-platform.sh
NAMESPACE="mcp-mesh-test"
echo "Testing platform deployment..."
# Create namespace
kubectl create namespace $NAMESPACE
# Dry run first
helm install test-platform ./mcp-mesh-platform \
--namespace $NAMESPACE \
--dry-run --debug
# Install with atomic flag
helm install test-platform ./mcp-mesh-platform \
--namespace $NAMESPACE \
--atomic \
--timeout 10m
# Wait for all pods
kubectl wait --for=condition=ready pod --all -n $NAMESPACE --timeout=300s
# Run tests
helm test test-platform -n $NAMESPACE
# Cleanup
helm uninstall test-platform -n $NAMESPACE
kubectl delete namespace $NAMESPACE
Validate Platform Health¶
# test_platform_health.py
import requests
import kubernetes
from kubernetes import client, config
def test_platform_components():
"""Verify all platform components are healthy"""
config.load_kube_config()
v1 = client.CoreV1Api()
namespace = "mcp-mesh"
# Check registry
registry_pods = v1.list_namespaced_pod(
namespace,
label_selector="app.kubernetes.io/name=mcp-mesh-registry"
)
assert len(registry_pods.items) >= 1
assert all(p.status.phase == "Running" for p in registry_pods.items)
# Check agents
for agent in ["weather", "analytics", "notification"]:
agent_pods = v1.list_namespaced_pod(
namespace,
label_selector=f"app.kubernetes.io/name={agent}-agent"
)
assert len(agent_pods.items) >= 1
# Check monitoring
prometheus_pods = v1.list_namespaced_pod(
namespace,
label_selector="app.kubernetes.io/name=prometheus"
)
assert len(prometheus_pods.items) >= 1
print("All platform components healthy!")
if __name__ == "__main__":
test_platform_components()
Monitoring and Debugging¶
Monitor Platform Deployment¶
# Watch deployment progress
watch -n 2 'helm status test-platform -n mcp-mesh'
# View all platform resources
kubectl get all -n mcp-mesh -l app.kubernetes.io/part-of=mcp-mesh-platform
# Check dependency status
helm dependency list ./mcp-mesh-platform
# View rendered templates
helm template test-platform ./mcp-mesh-platform | less
Debug Deployment Issues¶
# Check events
kubectl get events -n mcp-mesh --sort-by='.lastTimestamp'
# View helm release details
helm get all test-platform -n mcp-mesh
# Check values being used
helm get values test-platform -n mcp-mesh
# Debug specific subchart
helm template test-platform ./mcp-mesh-platform \
--show-only charts/mcp-mesh-registry/templates/statefulset.yaml
๐ง Troubleshooting¶
Issue 1: Dependency Download Failures¶
Symptoms: Error: failed to download "postgresql"
Cause: Repository not added or network issues
Solution:
# Add required repositories
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
# Update dependencies
helm dependency update ./mcp-mesh-platform
Issue 2: Resource Conflicts¶
Symptoms: Error: rendered manifests contain a resource that already exists
Cause: Previous installation remnants
Solution:
# Check existing resources
kubectl get all -n mcp-mesh -l app.kubernetes.io/managed-by=Helm
# Force upgrade
helm upgrade --install test-platform ./mcp-mesh-platform \
--force \
--namespace mcp-mesh
For more issues, see the section troubleshooting guide.
โ ๏ธ Known Limitations¶
- Large Deployments: May hit ConfigMap size limits with many agents
- Cross-Namespace: Umbrella charts work best in single namespace
- Dependency Versions: Must manually update subchart versions
- Values Complexity: Deep nesting can be hard to manage
๐ TODO¶
- Add backup/restore jobs to platform
- Create platform operator for dynamic agent management
- Add service mesh integration
- Document disaster recovery procedures
- Add cost optimization configurations
Summary¶
You can now deploy the complete MCP Mesh platform with an umbrella chart:
Key takeaways:
- ๐ Single chart deploys entire platform
- ๐ Dependencies managed automatically
- ๐ Flexible configuration through values
- ๐ Production-ready with monitoring included
Next Steps¶
Let's explore customizing deployments with values files.
Continue to Customizing Values โ
๐ก Tip: Use helm dependency build
instead of update
to use local Chart.lock file for reproducible builds
๐ Reference: Helm Dependencies Documentation
๐งช Try It: Create a custom platform chart that includes your own agents alongside the standard ones