Skip to content

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:

helm install minimal-platform ./mcp-mesh-platform -f values-minimal.yaml

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

  1. Use Dependency Conditions: Make components optional with conditions
  2. Global Values: Share common configuration in global section
  3. Value Validation: Add schema validation for complex values
  4. Atomic Deployments: Use --atomic flag for all-or-nothing deploys
  5. 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