Déployer un serveur MCP sur AWS Lambda, Azure et Cloud Run côte à côte
Un serveur MCP TypeScript sans état, trois hôtes. Découvrez la configuration exacte de Lambda Web Adapter, le host.json d’Azure Functions pour les serveurs auto-hébergés et la commande de déploiement Cloud Run, ainsi que les options d’authentification, les délais d’expiration et les compromis liés aux démarrages à froid qui décident de la plateforme adaptée à votre projet.
Votre serveur MCP fonctionne très bien sur votre ordinateur en stdio. Puis un collègue vous demande une URL qu’il peut coller dans un client, et le vrai travail commence. Un serveur distant a besoin de HTTPS, d’une authentification, d’un transport qui survit aux équilibreurs de charge, et d’un hôte qui ne vous facture rien quand personne ne l’appelle. Cet article prend un petit serveur TypeScript et le déploie sur trois plateformes : AWS Lambda, Azure Functions et Google Cloud Run. Vous obtenez la configuration qui compte sur chacune, le contrôle d’accès qui tient les inconnus à l’écart, et une comparaison claire pour choisir un hôte en dix minutes plutôt qu’en une semaine.
💡 Périmètre : chaque extrait ci-dessous suppose le transport Streamable HTTP. Stdio sert aux processus enfants locaux ; un serveur qui ne parle que stdio a donc besoin d’un front HTTP avant que l’un de ces hôtes puisse le faire tourner.
Choisir le transport avant le cloud
Pourquoi le sans état l’emporte en serverless
Les plateformes serverless démarrent et arrêtent les instances à leur guise. La première requête atterrit sur l’instance A, la deuxième sur l’instance B, et la troisième déclenche un démarrage à froid sur l’instance C. Si votre serveur garde une session en mémoire, cette séquence la casse.
La solution est un serveur Streamable HTTP sans état : un seul point de terminaison /mcp qui accepte un POST, répond et oublie. Les trois plateformes sont conçues autour de cette forme. La préversion auto-hébergée d’Azure n’accepte que des serveurs sans état sur le transport streamable-http. Cloud Run documente SSE et Streamable HTTP comme ses deux options distantes, avec le streaming de réponse HTTP intégré. Lambda se comporte de la même façon dès qu’on place un adaptateur web devant.
Ce qu’a changé la spécification de juillet 2026
La révision 2026-07-28 de la spécification MCP allait dans le même sens :
Plus de sessions au niveau du protocole. L’en-tête Mcp-Session-Id disparaît de Streamable HTTP.
Plus de poignée de main. L’échange initialize a été supprimé, et chaque requête porte désormais sa version de protocole et les capacités du client dans _meta.
État via des handles. Un serveur qui a besoin de mémoire entre les appels crée un handle explicite et le transmet comme un argument d’outil ordinaire.
Pas de reprise de flux. Un flux de réponse rompu fait perdre la requête en cours, et le client doit la renvoyer avec un nouvel identifiant de requête.
HTTP+SSE est déprécié. Les nouveaux développements doivent utiliser Streamable HTTP.
Concrètement, vous pouvez écrire le serveur comme du simple code de requête et de réponse, et laisser la plateforme lancer autant de copies qu’elle veut. Une mise en garde : les versions du SDK arrivent après les révisions de la spécification. Épinglez donc votre version du SDK et testez avec les clients qui comptent pour vous avant de faire confiance à un déploiement.
Un serveur, trois cibles
Le handler que partagent tous les hôtes
La démo expose deux outils qui font office de façade pour l’API développeur de PicassoIA : l’un lance une tâche de génération d’image, l’autre en vérifie l’état. L’API est de style Replicate et asynchrone, avec une URL de base https://api.picassoia.com/v1, une authentification Bearer, POST /models/{owner}/{name}/predictions pour lancer une tâche et GET /predictions/{id} pour la lire. Séparer le travail en lancement et vérification garde chaque requête courte, ce qui convient aux plateformes qui facturent à la milliseconde. La tâche ci-dessous cible PicassoIA Image via son slug picassoia/picassoia-image.
Un serveur et un transport neufs par requête constituent le schéma sans état des exemples du SDK, et ce schéma ne coûte presque rien, car l’enregistrement de deux outils est peu coûteux. Les noms de méthodes changent d’une version du SDK à l’autre : adaptez donc l’extrait à la version que vous installez, et vérifiez les champs de requête et de réponse dans la documentation de l’API PicassoIA.
Les secrets restent hors de l’image
N’intégrez rien de sensible dans le conteneur. Lisez PICASSOIA_API_TOKEN dans le coffre-fort de secrets de la plateforme : AWS Secrets Manager ou SSM Parameter Store sur Lambda, un paramètre d’application qui pointe vers un coffre géré sur Azure, et Google Secret Manager sur Cloud Run.
💡 Les comptes PicassoIA autorisent 5 prédictions simultanées, partagées entre les tokens et les connexions MCP. Plafonnez la diffusion de votre hôte avec la concurrence réservée de Lambda, --max-instances et --concurrency sur Cloud Run, ou le nombre maximal d’instances d’Azure, plutôt que de découvrir la limite en production.
Déployer sur AWS Lambda
Configuration de Lambda Web Adapter
L’approche la moins intrusive fait tourner votre application Express sans modification via Lambda Web Adapter. Pour une image de conteneur, cela tient en une ligne supplémentaire :
FROM public.ecr.aws/docker/library/node:22-slim
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt/extensions/lambda-adapter
ENV PORT=8080 AWS_LWA_INVOKE_MODE=response_stream AWS_LWA_READINESS_CHECK_PATH=/health
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY dist ./dist
CMD ["node", "dist/server.js"]
Vous préférez les paquets zip ? Ajoutez la couche de l’adaptateur, réglez AWS_LAMBDA_EXEC_WRAPPER sur /opt/bootstrap, et pointez le handler vers un script de démarrage. L’adaptateur lit le port dans AWS_LWA_PORT (il se rabat sur PORT, par défaut 8080) et interroge le chemin de disponibilité avant de transmettre le trafic.
Function URL et streaming de réponse
Placez une Function URL devant et réglez son mode d’invocation sur RESPONSE_STREAM, en cohérence avec la variable de l’adaptateur ci-dessus. Le mode tamponné par défaut conserve la réponse entière jusqu’à la fin de l’outil, ce qui annule le streaming. Lambda offre jusqu’à 15 minutes par invocation et jusqu’à 10 Go de mémoire, bien plus que ce qu’exige un outil de type démarrage-et-vérification.
Deux chemins d’accès existent :
AWS_IAM Function URL. Les appelants signent leurs requêtes avec SigV4. Bien adapté au trafic de service à service, peu pratique pour les clients MCP de bureau.
NONE plus votre propre contrôle. Exécutez OAuth dans le serveur ou placez un autorisateur devant. Un autorisateur Cognito ou Lambda via API Gateway est le choix habituel, mais son délai d’intégration par défaut tourne autour de 30 secondes. Les longs appels d’outils favorisent donc la Function URL.
Le Serverless Framework v4 peut câbler tout cela en quelques lignes de YAML :
mcp:
servers:
images:
server: index.ts
💡 Ce billet signale deux pièges : la connexion OAuth interactive exige un domaine personnalisé à la racine plutôt que l’URL par défaut execute-api, et Cognito ne prend pas en charge l’enregistrement dynamique de clients.
Déployer sur Azure Functions
Le host.json qui compte
Azure exécute les serveurs construits avec le SDK comme des gestionnaires personnalisés : l’hôte Functions reçoit la requête et la transmet à votre processus. La documentation Microsoft sur MCP auto-hébergé donne ce fichier minimal pour un serveur TypeScript, et le démarrage rapide Node le montre dans un projet fonctionnel :
Le profil mcp-custom-handler active le proxy HTTP, route tous les chemins ({*route}) vers votre serveur et supprime le préfixe de route, de sorte que /mcp arrive intact. Faites correspondre la valeur port au port sur lequel écoute votre serveur. Testez en local avec func start, car le débogueur F5 n’est pas encore pris en charge, puis publiez avec func azure functionapp publish <APP_NAME>.
Limites de la préversion et connexion Entra
Lisez les petites lignes avant de vous engager : cette fonctionnalité est en préversion publique. Elle ne prend en charge que les serveurs streamable-http sans état, écrits avec les SDK Python, TypeScript, C# ou Java, et l’application doit tourner sur le plan Flex Consumption. Si vous avez besoin d’un état, Microsoft vous oriente vers l’extension MCP de Functions. Flex Consumption peut garder des instances toujours prêtes pour réduire les démarrages à froid, au prix de la facturation de la capacité inactive.
L’authentification est le point fort d’Azure. L’authentification serveur intégrée à la plateforme implémente pour vous les exigences d’autorisation MCP : elle émet le défi 401, publie le document Protected Resource Metadata et envoie les clients vers Microsoft Entra ID pour se connecter. L’exemple développé host.json de la documentation définit defaultAuthorizationLevel sur anonymous et laisse la connexion à cette couche de la plateforme. Activez-la donc avant que l’URL ne soit rendue publique.
Déployer sur Cloud Run
Une seule commande depuis les sources
Cloud Run est le plus simple à mettre en place. Avec un Dockerfile ou un projet Node dans le dossier :
Vous avez déjà une image ? gcloud run deploy --image IMAGE_URL --port PORT suffit. Cloud Run injecte PORT, et le serveur doit se lier à 0.0.0.0, ce que fait déjà le handler partagé. La ligne d’adaptateur du Dockerfile de la section Lambda n’est ici qu’un fichier inerte, de sorte qu’une seule image peut servir aux deux plateformes.
Privé par défaut
Une nouvelle URL Cloud Run exige le rôle IAM Cloud Run Invoker (roles/run.invoker) sur chaque requête. Pour un client local, la documentation de Google recommande un proxy qui injecte votre identité :
gcloud run services proxy mcp-images --region us-central1 --port=3000
Pointez ensuite le client vers http://localhost:3000/mcp. Les appelants automatisés peuvent envoyer un jeton d’identité OIDC sous la forme Authorization: Bearer <token>, avec l’audience définie sur l’URL run.app du service. Les appelants qui tournent sur Cloud Run ont davantage d’options, dont un sidecar, l’authentification standard de service à service ou Cloud Service Mesh. Un serveur public destiné aux consommateurs a besoin de --allow-unauthenticated plus OAuth dans votre application, et c’est une décision à prendre délibérément, pas par défaut.
Instances préchauffées et délais d’attente
Cloud Run passe à zéro instance par défaut. Ajoutez --min-instances 1 si les démarrages à froid vous gênent, et prévoyez le coût de l’instance inactive. Les requêtes peuvent durer jusqu’à 60 minutes avec --timeout (la valeur par défaut est de 5 minutes), le plafond le plus élevé des trois, et le streaming de réponse HTTP ne demande aucune option supplémentaire.
Comparaison côte à côte
Question
AWS Lambda
Azure Functions
Cloud Run
Format de déploiement
Image de conteneur avec le Web Adapter, ou zip plus couche
Gestionnaire personnalisé plus host.json
Image de conteneur ou déploiement depuis les sources
Serveurs avec état
À éviter
Pas dans la préversion auto-hébergée
À éviter
Requête la plus longue
15 minutes
Défini par le plan Flex Consumption
60 minutes
Options de connexion
Function URL avec IAM, Cognito ou autorisateur Lambda
Authentification intégrée avec Entra ID
Rôle Invoker ou jeton d’identité OIDC
Instances préchauffées
Concurrence provisionnée
Instances toujours prêtes
--min-instances
Statut pour le MCP auto-hébergé
Fonctionne via l’adaptateur
Préversion publique
Chemin d’hébergement documenté
Quel hôte pour quelle équipe ?
Déjà sur AWS avec un trafic irrégulier : Lambda. Vous payez à la requête et rien pendant les périodes d’inactivité.
Environnement Microsoft avec Entra ID : Azure Functions. L’authentification intégrée vous évite d’écrire une couche OAuth, à condition qu’une fonctionnalité en préversion soit acceptable.
Petite équipe avec de longs appels d’outils : Cloud Run. La mise en place la plus simple et le délai le plus long.
Si vous ne parvenez pas à trancher, construisez d’abord une image de conteneur. Elle tourne telle quelle sur Cloud Run, sur Lambda via l’adaptateur, et le même code fonctionne derrière le gestionnaire personnalisé Azure.
Tester le point de terminaison avant les clients
Lancez le MCP Inspector avec npx @modelcontextprotocol/inspector, choisissez Streamable HTTP, collez votre URL /mcp et listez les outils. Puis faites le test que les gens sautent : appelez l’URL sans identifiants.
curl -i -X POST "$URL/mcp" -H "Content-Type: application/json" -d '{}'
Un 401 ou un 403 signifie que la porte d’entrée tient. Toute autre réponse signifie que la requête a franchi votre authentification, et votre compte en amont paie pour ce que cet appelant fait ensuite.
3 erreurs courantes
Se lier à localhost.127.0.0.1 fonctionne sur un ordinateur portable mais échoue derrière chacune de ces plateformes. Liez-vous à 0.0.0.0.
Garder l’état en mémoire. Un compteur ou un cache qui vit dans le processus disparaît au démarrage à froid suivant. Utilisez des handles explicites ou un stockage externe.
Mettre le flux en tampon. Le mode d’invocation par défaut de Lambda est tamponné, et un proxy intermédiaire peut faire de même. Si les messages de progression arrivent en bloc, cherchez un tampon.
Rédiger et illustrer avec PicassoIA
La même plateforme qui fournit à votre serveur quelque chose à appeler peut aussi écrire le code qui l’entoure et créer les images de sa documentation.
Utiliser Claude Sonnet 5 sur PicassoIA
Un modèle de code vous mène des extraits ci-dessus à un serveur qui correspond à vos propres outils. Claude Sonnet 5 gère le codage en plusieurs étapes et les tâches d’usage d’outils, et lit les images : une capture d’écran d’un déploiement raté peut donc entrer directement dans la requête.
Collez un prompt qui nomme le transport, les outils et l’hôte, par exemple : « Write a stateless Streamable HTTP MCP server in TypeScript with two tools, start_job and get_job, ready for Cloud Run. »
Remplissez une fois le system prompt pour que chaque réponse suive vos règles : sans état, liaison à 0.0.0.0, lecture de PORT, pas de sessions en mémoire.
Choisissez un niveau d’effort adapté à la tâche, en vous aidant du tableau ci-dessous.
Laissez max_tokens à la valeur par défaut de 8 192 pour les réponses en un seul fichier, et demandez un fichier à la fois si une réponse est coupée.
Joignez une image lorsque vous avez une capture de journal. Le réglage max_image_resolution est par défaut à 0,5 mégapixel et la réduit avant l’envoi.
Paramètre
Réglage suggéré
À utiliser pour
effort
low (par défaut)
Ajustements de configuration et corrections d’une ligne
effort
high ou max
Flux d’authentification et bogues touchant plusieurs fichiers
max_tokens
8 192 (par défaut)
Un fichier par réponse
system_prompt
Les règles de votre hébergement
Sortie cohérente sur l’ensemble d’un projet
image
Capture d’écran d’erreur
Débogage des journaux de déploiement
Pour un second avis sur un bogue d’authentification délicat, passez le même prompt par GPT 5.6 Sol et comparez les deux réponses.
Générer vos propres images
Une fois le serveur en ligne, il lui faut un en-tête de README, un fond de schéma et une carte pour les réseaux sociaux. PicassoIA Image transforme un prompt simple en image finie en quelques secondes, avec sept formats, de 1:1 à 16:9, une seed verrouillable pour des résultats reproductibles, une sortie en JPG, PNG ou WebP et jusqu’à deux variations par exécution. Il est présenté comme illimité, sans plafond par image, ce qui vous permet d’itérer librement. Quand une image fixe mérite du mouvement, PicassoIA Video l’anime en un court clip.
Essayez ce prompt : a quiet loft office at dusk, laptop open on an oak desk, soft window light, 35mm photograph, film grain. Modifiez un seul détail, verrouillez la seed, générez à nouveau et comparez les deux. Ouvrez Picasso IA, lancez votre premier prompt et découvrez à quoi ressemblera votre prochaine image. Chaque modèle se trouve sur picassoia.com/en/all-models, il y a donc de quoi expérimenter.