Déployer des tunnels MCP avec Helm
Installez la pile de tunnel sur un cluster Kubernetes à l'aide du chart Helm d'Anthropic.
Le chart Helm d'Anthropic installe la pile de tunnel sous la forme d'un seul Deployment et l'attache à votre tunnel : soit un tunnel que le hook de configuration du chart crée pour vous, soit un tunnel existant que vous avez créé dans la Claude Console.
Avant de commencer
Vous avez besoin de :
- Un tunnel. Avec l'accès programmatique, le hook de configuration du chart en crée un pour vous lorsque vous ne fournissez pas d'ID de tunnel ; pour vous attacher plutôt à un tunnel existant, créez-le dans la Console et notez l'ID du tunnel (
tnl_...). Le provisionnement manuel part toujours d'un tunnel créé dans la Console ; vous avez également besoin de son jeton de tunnel et de son domaine de tunnel. - Un moyen pour le chart de s'authentifier auprès de l'API Tunnels.
- Accès programmatique (recommandé). Le composant de configuration s'authentifie via Workload Identity Federation, récupère le jeton de tunnel, génère une CA, l'enregistre auprès d'Anthropic et stocke le tout dans un Secret. Vous avez besoin d'une règle de fédération limitée à
workspace:manage_tunnels. - Manuel. Ignorez l'accès programmatique. Vous obtiendrez le jeton de tunnel depuis la Console, générerez vous-même une CA et un certificat serveur, enregistrerez la CA dans la Console et fournirez les identifiants au cluster sous forme de Secrets.
- Accès programmatique (recommandé). Le composant de configuration s'authentifie via Workload Identity Federation, récupère le jeton de tunnel, génère une CA, l'enregistre auprès d'Anthropic et stocke le tout dans un Secret. Vous avez besoin d'une règle de fédération limitée à
- Un cluster Kubernetes sur lequel vous pouvez déployer avec
helmetkubectl. L'onglet Sans accès programmatique utilise égalementopenssl(1.1.1 ou ultérieur). - Une connectivité réseau sortante du cluster vers
api.anthropic.com(443 TCP) et la périphérie du tunnel (7844 TCP et UDP). Consultez l'ensemble des exigences réseau. - Un ou plusieurs serveurs MCP en cours d'exécution et accessibles depuis le cluster aux adresses que vous configurerez sous
gateway.config.routes. Si vous n'en avez pas encore, utilisez le serveur d'exemple.
Facultatif : utiliser un serveur MCP d'exemple
Si vous ne disposez pas d'un serveur MCP pour les tests, utilisez ce serveur minimal :
kubectl create namespace mcp-tunnel --dry-run=client -o yaml | kubectl apply -f -
kubectl -n mcp-tunnel apply -f - <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: hello-mcp-src
data:
hello_server.py: |
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-server", host="0.0.0.0", port=9000)
@mcp.tool()
def hello(name: str = "world") -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="streamable-http")
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-mcp
spec:
replicas: 1
selector:
matchLabels: { app: hello-mcp }
template:
metadata:
labels: { app: hello-mcp }
spec:
containers:
- name: hello-mcp
image: python:3.13-slim
command: ["sh", "-c", "pip install --quiet mcp && python /app/hello_server.py"]
volumeMounts:
- { name: src, mountPath: /app }
ports:
- { containerPort: 9000 }
volumes:
- name: src
configMap: { name: hello-mcp-src }
---
apiVersion: v1
kind: Service
metadata:
name: hello-mcp
spec:
selector: { app: hello-mcp }
ports:
- { port: 9000, targetPort: 9000 }
EOFLes étapes d'installation qui suivent indiquent où ajouter la route correspondante.
Installation
Le composant de configuration échange le jeton ServiceAccount projeté du cluster via votre règle de fédération, récupère le jeton de tunnel, génère une CA et un certificat serveur, et enregistre la CA auprès d'Anthropic. Un CronJob quotidien renouvelle le certificat serveur si nécessaire, de sorte que vous ne manipulez aucun secret à la main.
Configurer Workload Identity Federation pour le cluster
Suivez Utiliser WIF avec Kubernetes pour enregistrer l'émetteur OIDC de votre cluster et créer une règle de fédération. Le composant de configuration s'exécute sous son propre ServiceAccount dans le namespace de la release ; le nom exact suit la convention
fullnamede Helm, donc pour tout nom de release autre quemcp-tunnel, exécutezhelm template <release> ... | grep -A2 'kind: ServiceAccount'pour le confirmer avant de créer la règle. La suite de ce guide suppose le nom de releasemcp-tunneldans le namespacemcp-tunnel, où le ServiceAccount estmcp-tunnel-setup.Champ Valeur Sujet system:serviceaccount:mcp-tunnel:mcp-tunnel-setupAudience api.anthropic.com(la valeur par défaut du chart ; sans schéma)Portée workspace:manage_tunnelsSi le tunnel se trouve dans un espace de travail autre que celui par défaut de l'organisation, ajoutez également le compte de service de la règle à cet espace de travail (l'API Tunnels effectue l'autorisation en fonction des appartenances du compte de service aux espaces de travail).
Notez l'ID de la règle (
fdrl_...) ; vous le définirez commeapi.wif.federationRuleId.Récupérer les valeurs par défaut
helm show values \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.1.0 > values.yamlConfigurer l'attachement au tunnel et les routes
Modifiez
values.yamlet définissez les clésapi.wif.*avec l'ID de la règle de fédération et l'ID de l'organisation, ainsi qu'une entréeroutespour chaque serveur MCP en amont :values.yamlapi: wif: federationRuleId: "fdrl_..." organizationId: "00000000-0000-0000-0000-000000000000" # Set when the tunnel is in a non-default workspace and the # rule's service account is a member of that workspace. # workspaceId: "wrkspc_..." tunnel: # Leave empty to have the setup hook create a tunnel during install. # Set to attach to an existing tunnel from the Console. id: "" # Increment to rotate the tunnel token on the next upgrade. # See the "Rotate the tunnel token" section. tokenVersion: "1" gateway: config: routes: docs: http://docs-mcp.internal:8080 search: http://search-mcp.internal:8080Avec ces routes, Claude atteint les serveurs à
docs.<your-tunnel-domain>etsearch.<your-tunnel-domain>. Certaines distributions Kubernetes gérées allouent le CIDR des Services en dehors des plages privées standard ; si vos routes ciblent des Services internes au cluster, ajoutezgateway.config.upstream.allowed_ipsici comme décrit dans Validation des IP en amont.Examiner les manifestes générés
Générez le rendu du chart et examinez le résultat conformément aux pratiques de vérification de votre organisation :
helm template mcp-tunnel \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.1.0 \ -n mcp-tunnel \ -f values.yaml > rendered.yamlInstaller
helm install mcp-tunnel \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.1.0 \ --namespace mcp-tunnel --create-namespace \ -f values.yamlLe composant de configuration s'exécute en tant que Job de hook pre-install Helm, donc
helm installreste bloqué jusqu'à ce qu'il se termine. En cas de succès, Helm supprime automatiquement le Job. Sihelm installéchoue avec une erreur de hook, consultez Échecs d'authentification du composant de configuration.Lorsque
tunnel.idest vide, le composant de configuration crée le tunnel dans l'espace de travail ciblé par votre règle de fédération (l'espace de travail par défaut de l'organisation, sauf si vous définissezapi.wif.workspaceId) et stocke son ID et son domaine dans le Secretmcp-tunnel. Trouvez le domaine dont vous aurez besoin pour la vérification sur la page de détails du tunnel, que vous ouvrez depuis la page Tunnels MCP (platform.claude.com/settings/workspaces/default/mcp-tunnels) dans la Console. Dans le sélecteur d'espace de travail de la barre latérale, sélectionnez l'espace de travail qui contient le tunnel. Vous pouvez également lire le domaine depuis le Secret :kubectl -n mcp-tunnel get secret mcp-tunnel \ -o jsonpath='{.data.tunnel-domain}' | base64 -dLa réexécution du composant de configuration (lors des mises à niveau ou de la rotation du jeton) réutilise l'ID de tunnel stocké dans ce Secret ; elle ne crée jamais de second tunnel.
Dans ce mode (setup.enabled: false), le chart n'effectue aucun appel d'API ; le composant de configuration ne s'exécute pas et il n'y a pas de CronJob cert-renew. Utilisez cette voie si vous préférez ne pas configurer Workload Identity Federation.
Obtenir le jeton et le domaine du tunnel
Créez le tunnel et obtenez le jeton de tunnel depuis la Console.
Générer une CA et un certificat serveur
Le proxy écoute en WebSocket simple, le TLS interne étant transporté à l'intérieur de ce flux à l'aide du certificat que vous générez ici. Le SAN du certificat serveur doit inclure
*.<tunnel-domain>comme décrit dans les exigences relatives aux certificats.export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE mkdir -p mcp-tunnel/data cd mcp-tunnel # CA auto-signée. Extensions explicites pour satisfaire les exigences du certificat # quelles que soient les valeurs par défaut du openssl.cnf de la distribution. openssl req -x509 -newkey rsa:2048 -nodes \ -keyout data/ca.key -out data/ca.crt \ -days 3650 -subj "/CN=mcp-tunnel-ca" \ -addext "basicConstraints=critical,CA:TRUE" \ -addext "keyUsage=critical,keyCertSign,cRLSign" \ -addext "subjectKeyIdentifier=hash" # Fichier d'extensions pour le certificat serveur. Utiliser -extfile (au lieu de # -copy_extensions, disponible uniquement à partir d'OpenSSL 3.0) assure la compatibilité avec # OpenSSL 1.1.x. cat > data/tls.ext <<EOF subjectAltName = DNS:${TUNNEL_DOMAIN},DNS:*.${TUNNEL_DOMAIN} authorityKeyIdentifier = keyid,issuer extendedKeyUsage = serverAuth EOF # Certificat serveur signé par la CA openssl req -newkey rsa:2048 -nodes \ -keyout data/tls.key -out /tmp/server.csr \ -subj "/CN=${TUNNEL_DOMAIN}" openssl x509 -req -in /tmp/server.csr \ -CA data/ca.crt -CAkey data/ca.key -CAcreateserial \ -out data/tls.crt -days 90 \ -extfile data/tls.extEnregistrez
data/ca.crtdans la Console. Conservezdata/ca.keydans un endroit durable et sécurisé ; vous en aurez besoin pour signer un nouveau certificat serveur lors du renouvellement.Créer les deux Secrets
Le chart lit des clés spécifiques ; les noms des Secrets sont configurables, mais pas les clés. La commande de création de namespace suivante n'a aucun effet si le namespace existe déjà (par exemple, depuis l'étape du serveur MCP d'exemple).
kubectl create namespace mcp-tunnel --dry-run=client -o yaml | kubectl apply -f - kubectl -n mcp-tunnel create secret generic mcp-tunnel-token \ --from-literal=tunnel-token='eyJ...' kubectl -n mcp-tunnel create secret generic mcp-tunnel-cert \ --from-file=tls.crt=data/tls.crt \ --from-file=tls.key=data/tls.keyRécupérer les valeurs par défaut
helm show values \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.1.0 > values.yamlConfigurer les valeurs pour le provisionnement manuel
Modifiez
values.yamlet définissez les clés suivantes :values.yamlsetup: enabled: false external: tunnelTokenSecretName: mcp-tunnel-token # must contain key: tunnel-token serverCertSecretName: mcp-tunnel-cert # must contain keys: tls.crt, tls.key gateway: config: # Required when setup.enabled is false. Replace the placeholder with # the $TUNNEL_DOMAIN value you exported earlier. When setup.enabled # is true the chart injects this from the Secret as a -tunnel-domain # flag instead. tunnel_domain: YOUR_TUNNEL_DOMAIN_HERE routes: docs: http://docs-mcp.internal:8080 search: http://search-mcp.internal:8080Certaines distributions Kubernetes gérées allouent le CIDR des Services en dehors des plages privées standard ; si vos routes ciblent des Services internes au cluster, ajoutez
gateway.config.upstream.allowed_ipsici comme décrit dans Validation des IP en amont.Examiner les manifestes générés
helm template mcp-tunnel \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.1.0 \ -n mcp-tunnel \ -f values.yaml > rendered.yamlInstaller
helm install mcp-tunnel \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.1.0 \ --namespace mcp-tunnel --create-namespace \ -f values.yaml
Vérifier le déploiement
Effectuez une vérification de bout en bout du côté d'Anthropic : utilisez https://<route>.<your-tunnel-domain>/<path> dans une session Managed Agent ou une requête à l'API Messages, où <route> est une clé de gateway.config.routes et <path> est le chemin sur lequel le serveur MCP en amont est servi. Avec le serveur MCP d'exemple, il s'agit de https://echo.<your-tunnel-domain>/mcp. Consultez Utiliser les serveurs MCP tunnelisés pour les formats de requête.
En cas d'échec, vérifiez les journaux du pod (kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c mcp-proxy et -c cloudflared) et consultez Dépannage.
Configuration facultative
Restreindre le trafic sortant avec NetworkPolicy
Le trafic entrant vers le pod du proxy est refusé par défaut (networkPolicy.ingress.enabled: true). Pour restreindre en plus le trafic sortant du pod, définissez networkPolicy.egress.enabled: true et renseignez networkPolicy.egress.mcpServers avec des sélecteurs de labels de pod ou des plages CIDR couvrant vos serveurs MCP en amont. Le trafic sortant de cloudflared vers la périphérie du tunnel est autorisé séparément via networkPolicy.egress.cloudflaredEgressCIDRs.
Ajuster le proxy
Les champs sous gateway.config.* sont transmis au fichier de configuration du proxy. Les ajustements courants incluent upstream.allowed_ips, log_level et upstream.tls. Consultez la référence de configuration du proxy pour la liste complète des champs. Le chart définit toujours listen_addr, tls.cert_file et tls.key_file ; les définir dans gateway.config n'a aucun effet.
Fournir votre propre jeton OIDC
Par défaut, le chart projette un jeton ServiceAccount Kubernetes pour le composant de configuration. Pour utiliser un jeton provenant d'un autre fournisseur d'identité (tel que SPIFFE, Vault ou un sidecar de SDK cloud), montez-le avec setup.extraVolumes et setup.extraVolumeMounts. Faites ensuite pointer api.wif.tokenFile vers le chemin de montage. Le chart définit ANTHROPIC_IDENTITY_TOKEN_FILE sur ce chemin, et le composant de configuration y lit le jeton.
Mises à niveau
Passez toujours --version à helm upgrade afin de ne pas récupérer un chart plus récent de manière inattendue.
Mise à niveau depuis le chart 1.x
Le chart 2.0.0 déplace l'ID du tunnel de api.wif.tunnelId vers tunnel.id. Avant la mise à niveau, modifiez votre values.yaml : déplacez la valeur tnl_... vers tunnel.id et supprimez api.wif.tunnelId. Laisser tunnel.id non défini est sans risque (le composant de configuration réutilise l'ID de tunnel déjà stocké dans le Secret mcp-tunnel lors de la réexécution), mais le déplacement explicite maintient votre values.yaml exact. Mettez également à jour la portée de votre règle de fédération de org:manage_tunnels vers workspace:manage_tunnels dans la Console.
Modifier la configuration
Pour les modifications courantes telles que les routes, le nombre de réplicas ou la NetworkPolicy :
helm upgrade mcp-tunnel \
oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
--version 2.1.0 \
-n mcp-tunnel \
-f values.yamlEffectuer la rotation du jeton de tunnel
Avec l'accès programmatique, incrémentez tunnel.tokenVersion dans values.yaml et effectuez la mise à niveau avec --set setup.force=true. Le composant de configuration ne se réexécute lors des mises à niveau que lorsqu'il est forcé :
helm upgrade mcp-tunnel \
oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
--version 2.1.0 \
-n mcp-tunnel \
-f values.yaml \
--set setup.force=trueLe composant de configuration s'authentifie avec Workload Identity Federation ; il n'y a aucun jeton d'API à révoquer.
Sans accès programmatique, cliquez sur Rotate token sur la page de détails du tunnel dans la Console, puis mettez à jour le Secret mcp-tunnel-token :
kubectl -n mcp-tunnel create secret generic mcp-tunnel-token \
--from-literal=tunnel-token='eyJ...' --dry-run=client -o yaml | kubectl apply -f -
kubectl -n mcp-tunnel rollout restart deploy/mcp-tunnelRenouvellement des certificats
Le chart fournit une automatisation, mais il vous incombe de surveiller l'expiration et de confirmer que le renouvellement se termine.
Avec l'accès programmatique, le renouvellement des certificats est automatique. Le chart déploie un CronJob (nommé d'après le fullname Helm, avec le suffixe -cert-renew) qui exécute setup renew-cert quotidiennement (selon serverCert.cronSchedule, par défaut 0 0 * * * UTC). Le job n'a aucun effet sauf si le certificat se trouve à moins de serverCert.renewBefore de son expiration (30 jours par défaut). Le renouvellement est local : le job signe un nouveau certificat avec la CA déjà stockée dans le Secret, n'effectue aucun appel d'API et n'a besoin que du RBAC Kubernetes accordé par le chart. Le proxy recharge à chaud le certificat depuis le montage du Secret, donc aucun redémarrage du Deployment n'est nécessaire.
Sans accès programmatique, il n'y a pas de CronJob. Depuis le répertoire mcp-tunnel/ que vous avez conservé après l'installation, signez un nouveau certificat serveur avec la CA existante (ne régénérez pas la CA) :
export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE
openssl req -new -key data/tls.key -out /tmp/server.csr \
-subj "/CN=${TUNNEL_DOMAIN}"
openssl x509 -req -in /tmp/server.csr \
-CA data/ca.crt -CAkey data/ca.key -CAcreateserial \
-out data/tls.crt -days 90 -extfile data/tls.ext
kubectl -n mcp-tunnel create secret generic mcp-tunnel-cert \
--from-file=tls.crt=data/tls.crt --from-file=tls.key=data/tls.key \
--dry-run=client -o yaml | kubectl apply -f -Le proxy recharge à chaud le certificat depuis le montage du Secret.
Étapes suivantes
Attachez un serveur MCP en amont à un Managed Agent ou à l'API Messages.
Conseils de renforcement, rotation des identifiants et réponse aux violations.
Diagnostiquez les problèmes de connectivité, de TLS et de routage.
Was this page helpful?