Stdio vs HTTP streamable pour MCP : Qu'est-ce qui change lorsque l'on passe du développement local au déploiement en entreprise

Conçu pour la vitesse : latence d'environ 10 ms, même en cas de charge
Une méthode incroyablement rapide pour créer, suivre et déployer vos modèles !
- Gère plus de 350 RPS sur un seul processeur virtuel, aucun réglage n'est nécessaire
- Prêt pour la production avec un support complet pour les entreprises
Stdio convient pour l'ordinateur portable du développeur. Le HTTP streamable est ce dont les déploiements en entreprise ont réellement besoin. Nous passons en revue les deux types de transport — format filaire, cycle de vie de la connexion, authentification, audit et tests de performance — et montrons ce qui change lorsqu'un environnement MCP dépasse un seul utilisateur.
Un vendredi après-midi chez Northwind. Six mois après le déploiement de Cargo Copilot, le responsable de la sécurité de Northwind pose à l'équipe d'ingénierie une question d'audit de routine : quels développeurs ont appelé l'outil MCP interne de données clients au cours des 30 derniers jours, et pour quels identifiants clients ? L'équipe dispose de chaque message JSON-RPC ayant transité par ces outils — à l'intérieur des journaux stderr de chaque processus Cursor local de développeur. Répartis sur cinquante ordinateurs portables. Sans source d'horodatage partagée, sans schéma et sans moyen de corréler. La question prend une semaine à être résolue, et la réponse est partielle. La cause n'est pas la négligence. C'est le choix de transport qu'ils ont fait il y a six mois.
Northwind a commencé là où la plupart des équipes commencent : des serveurs MCP stdio, un par machine de développeur. C'est le bon choix par défaut pour l'expérimentation locale — et le mauvais choix par défaut pour tout le reste. Cet article explique pourquoi, avec les spécificités des formats filaires, des modèles de déploiement et du chemin de migration.
1. Transport Stdio : Fonctionnement de JSON-RPC 2.0 via stdin/stdout
La spécification de transport MCP définit stdio en un paragraphe : le client lance le serveur en tant que sous-processus ; le serveur lit JSON-RPC 2.0 messages depuis stdin et écrit les réponses vers stdout. Chaque message est une ligne de texte UTF-8 terminée par un saut de ligne. Le serveur peut écrire des journaux dans stderr ; il NE DOIT PAS écrire quoi que ce soit dans stdout qui ne soit pas un message MCP valide.
Un seul appel d'outil de l'agent vers le serveur est une ligne de JSON :
Wire format — newline-delimited JSON-RPC over stdio
# stdin (client → server)
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_issues","arguments":{"query":"is:open label:critical"}}}
# stdout (server → client)
{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"Found 3 issues..."}]}}Les règles de cadrage sont simples mais impitoyables. La spécification MCP exige que les messages soient sur une seule ligne, de sorte que les serveurs conformes échappent tout caractère de nouvelle ligne interne en tant que \n lors de la sérialisation JSON. Ce qui perturbe réellement le cadrage en production est la contamination de stdout par du contenu non-JSON : une instruction print() égarée, une trace d'exception non interceptée, un journal de débogage accidentellement acheminé vers stdout au lieu de stderr, ou un serveur qui oublie de vider stdout après chaque message. Dans tous ces cas, le client voit soit un message malformé, soit attend indéfiniment une réponse qui a techniquement été écrite. Chaque SDK MCP est livré avec une implémentation de transport stdio précisément pour que ces cas limites deviennent le problème de quelqu'un d'autre.
Ce que stdio vous offre en échange de ces contraintes est l'isolation des processus. L'agent gère le cycle de vie du serveur : lorsque l'agent se termine, le système d'exploitation récupère le processus. Il n'y a pas de réseau, pas de poignée de main d'authentification, pas de question de pare-feu. Pour le développement local, c'est exactement ce que vous voulez.
2. Transport HTTP streamable : Modes Requête-Réponse et SSE
Le HTTP streamable, introduit dans la spécification MCP 2025-03-26 et maintenu dans la révision de novembre 2025, remplace l'ancien transport HTTP+SSE par une conception à point d'accès unique. Le serveur expose une seule URL (par exemple /mcp) qui accepte à la fois POST et GET. Les clients POSTent des messages JSON-RPC ; les serveurs répondent soit avec un corps JSON unique, soit en passant à un flux d'événements envoyés par le serveur (Server-Sent Events) pour les appels de longue durée. Il n'y a pas de point de terminaison "événements" séparé.
Le client indique ce qu'il peut accepter ; le serveur choisit le mode de réponse. Voici un appel d'outil sous forme HTTP :
Wire format — Streamable HTTP, both response modes
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
Mcp-Session-Id: 1d3f...e7c2
Authorization: Bearer eyJhbGciOi...
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_issues",...}}
# --- Server response: short call returns plain JSON ---
HTTP/1.1 200 OK
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"result":{"content":[...]}}
# --- Server response: long call upgrades to SSE ---
HTTP/1.1 200 OK
Content-Type: text/event-stream
event: message
data: {"jsonrpc":"2.0","method":"notifications/progress","params":{...}}
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[...]}}Trois détails sont importants sur le plan opérationnel. L'en-tête Mcp-Session-Id lie les requêtes à une session et est attribué par le serveur lors de l'initialisation — il persiste après les redémarrages de pod uniquement si le serveur externalise l'état de la session. L'en-tête Accept est obligatoire : selon la spécification, les clients DOIVENT lister à la fois application/json et text/event-stream, et un serveur conforme peut rejeter un en-tête Accept manquant ou incomplet avec le code HTTP 406 Not Acceptable (selon la sémantique HTTP ; 415 Type de média non pris en charge s'applique à un type incompatible Content-Type, et non Accept). Et selon la section de sécurité de la spécification, les serveurs DOIVENT valider l'en-tête Origin en-tête sur chaque connexion pour prévenir les attaques de réaffectation DNS contre les serveurs liés localement — une exigence normative, non une recommandation, avec le code HTTP 403 Interdit comme réponse prescrite à un en-tête Origin invalide. 3. Cycle de vie de la connexion : Processus par utilisateur vs HTTP sans état
Les deux transports modélisent les connexions de manière complètement différente, et c'est là que le fossé opérationnel se creuse.
Pour un développeur seul travaillant localement, le modèle "processus par connexion" de stdio est une fonctionnalité, pas un bug — l'isolation des processus est gratuite, et le démarrage à froid n'a lieu qu'une seule fois à l'ouverture de l'IDE. Dès qu'il y a plus d'un utilisateur qui a besoin du serveur, ce modèle devient une contrainte.
4. Multi-locataire : Pourquoi Stdio atteint ses limites à grande échelle
La contrainte stdio qui pose problème en entreprise est plus d'ordre arithmétique qu'ingénierique : les déploiements MCP stdio typiques exécutent un processus par tuple (utilisateur, serveur), sans partage intégré entre les utilisateurs. Certaines implémentations multiplexent plusieurs définitions d'outils au sein d'un même sous-processus, et quelques-unes mettent en commun des sous-processus, mais le modèle de déploiement courant dans la pratique — et celui qui est livré dans les SDK officiels — est un processus par utilisateur et par serveur.
.
Chez Northwind, 50 développeurs exécutent chacun un IDE avec huit serveurs MCP attachés. Cela représente 400 processus stdio pendant les heures de pointe, répartis sur 50 ordinateurs portables. Chaque processus occupe de la mémoire (un serveur MCP Python avec quelques dépendances utilise environ 60 à 120 Mo de mémoire résidente ; un serveur Node est similaire), maintient des descripteurs de fichiers ouverts et conserve un environnement d'exécution actif bloqué sur stdin. L'empreinte globale des ressources n'est pas catastrophique — 400 petits processus sont largement dans les limites du matériel moderne — mais le coût réel est opérationnel plutôt que computationnel : le nombre de processus fragmente le plan de contrôle.
Le problème plus complexe concerne les serveurs à état partagé. Imaginez que le serveur MCP de l'API Logistique interne met en cache un graphe client de 200 Mo en mémoire au démarrage. Avec stdio, la machine de chaque développeur charge sa propre copie. Avec Streamable HTTP, deux répliques de pod conservent le graphe pour toute l'entreprise. Mêmes données, deux ordres de grandeur de mémoire en moins au total, et le cache est chaud pour tous les utilisateurs car il est partagé.
Il est important de mentionner l'autre facette de la médaille. Le modèle décentralisé de stdio présente de réels avantages qu'une équipe d'infrastructure senior citera à juste titre : une forte isolation des pannes (le serveur d'un développeur qui plante n'affecte personne d'autre), aucune dépendance d'entrée partagée, aucune panne d'authentification centralisée pour paralyser l'ensemble du système, et une infrastructure minimale à exploiter. Pour les petites équipes, les flux de travail locaux hautement fiables ou les environnements isolés (air-gapped), ces propriétés peuvent réellement l'emporter sur les avantages opérationnels d'une couche HTTP centralisée. L'argument de cet article n'est pas que stdio est mauvais ; c'est que les modes de défaillance qu'il impose à l'organisation — audit fragmenté, identifiants distribués, absence de limitation de débit centrale — apparaissent précisément lorsqu'un système passe de « quelques utilisateurs avancés » à une « infrastructure partagée avec des obligations de conformité ».

C'est la contrainte que la documentation de la passerelle MCP de TrueFoundry explicite :
Une passerelle a besoin d'un point d'accès HTTP qu'elle peut intercepter. En pratique, les déploiements de passerelles centralisées nécessitent une couche de transport orientée HTTP, c'est pourquoi les serveurs stdio sont généralement encapsulés à l'aide de mcp-proxy (plus de détails au §8). La décision architecturale « quel transport livrons-nous » est donc aussi la décision « pouvons-nous placer une passerelle devant cela ».
5. Injection d'authentification : les lacunes de chaque transport
Stdio n'a pas d'authentification au niveau de la couche de transport. La spécification MCP est explicite : les implémentations stdio doivent récupérer les identifiants de l'environnement, et non d'un flux OAuth. En pratique, cela signifie que la machine de chaque développeur contient des clés API dans des variables d'environnement shell, dans les paramètres de l'éditeur, ou dans un fichier de configuration partagé sur Slack lorsqu'une personne rejoint l'équipe. Les identifiants résident là où le processus s'exécute.
Streamable HTTP dispose de l'en-tête Authorization. Une passerelle peut valider l'identifiant entrant avant que la requête n'atteigne le serveur, l'échanger contre un identifiant en aval selon le modèle d'authentification sortant configuré, et rejeter les appels qui ne respectent pas la politique — tout cela sans toucher au code de l'application. Le modèle basé sur les en-têtes est ce qui rend possible l'identité centralisée, le RBAC et le courtage de jetons OAuth.
L'impact pratique est le plus visible lors d'une rotation d'identifiants. Avec stdio, un jeton GitHub divulgué nécessite de retrouver chaque machine de développeur qui l'a mis en cache — dans les paramètres de l'éditeur, les dotfiles, les gestionnaires de mots de passe, et les copies inévitables que les développeurs ont faites pour s'entraider. Avec HTTP, la même rotation est une seule mise à jour au niveau de la passerelle, et chaque requête ultérieure utilise le nouvel identifiant. Le choix du transport n'est pas la seule raison pour laquelle l'authentification centralisée fonctionne, mais c'est celle qui la rend possible.
6. Pistes d'audit : ce que la passerelle peut et ne peut pas voir
Stdio ne dispose pas de point d'interception centralisé naturel. Il n'y a pas de socket à intercepter, pas de proxy à insérer, pas d'en-tête à journaliser — l'audit structuré doit donc être reconstruit hors bande via des forwarders locaux, des collecteurs stderr, le traçage eBPF ou des superviseurs de processus déployés sur chaque hôte où un serveur s'exécute. C'est réalisable, mais c'est un programme opérationnel en soi, distinct de l'application. Le seul enregistrement de premier ordre de ce qui s'est passé au sein d'une session MCP stdio est ce que le serveur a choisi d'écrire dans stderr — non structuré, par processus, horodatages non fiables, pas d'ID de corrélation, pas d'identité de l'appelant. Pour un seul développeur déboguant localement, c'est suffisant. Pour une équipe de sécurité reconstituant un incident sur cinquante ordinateurs portables, ce n'est pas le cas.
Streamable HTTP expose chaque appel d'outil au niveau de la couche HTTP, où une passerelle peut intercepter de manière structurée. Un enregistrement d'audit minimal de la passerelle TrueFoundry ressemble à ceci :
Audit log entry — illustrative gateway record for a single tool call
{
"timestamp": "2026-05-14T16:23:11.482Z",
"request_id": "req_8f3a...e91",
"session_id": "1d3f...e7c2",
"caller": {
"subject": "user:alice@northwind.com",
"auth_method": "TrueFoundry API Key (PAT)",
"team": "platform-engineering"
},
"server": "backend-group/github",
"tool": "search_issues",
"arguments": {"query": "repo:northwind/logistics-core is:open"},
"outcome": "ok",
"latency_ms": 187,
"outbound_auth": "OAuth2 (Authorization Code)"
}L'enregistrement d'audit n'est pas une ligne de journal que le serveur a choisi d'émettre. C'est une métadonnée que la passerelle produit par sa structure. Le même enregistrement existe pour chaque appel d'outil sur chaque serveur MCP du système, avec un schéma cohérent, des horodatages monotones et une identité liée à l'identifiant entrant. C'est cette propriété qui permet à l'équipe de sécurité de Northwind de répondre à la question du vendredi après-midi en une seule requête au lieu d'une semaine.
7. Benchmarks de latence : appels d'outils séquentiels vs parallèles sur les deux transports
Sur l'ordinateur portable du développeur, stdio est plus rapide par appel. Sur un réseau avec une charge réaliste, Streamable HTTP l'emporte sur les métriques qui évoluent. Voici la décomposition.
En bref : stdio l'emporte sur la latence d'un seul appel dans son meilleur scénario (processus chaud, même machine) et maintient le domaine de panne restreint. Ce que HTTP perd en latence brute d'un seul appel, il le regagne en contrôles opérationnels dont les entreprises ont généralement besoin à grande échelle — réplication, parallélisme via des gestionnaires concurrents, limitation de débit centralisée et observabilité au niveau de la passerelle. Et comme l'appel LLM encapsulant chaque invocation d'outil est généralement de plusieurs centaines de millisecondes, une surcharge HTTP de 5 à 10 ms est rarement le coût dominant par rapport au travail effectué.
8. Guide de migration : Conversion d'un serveur Stdio en HTTP en flux continu
La migration est un remplacement de la couche de transport. Les outils eux-mêmes — leurs schémas d'entrée, leurs gestionnaires, leurs dépendances — restent inchangés. Il existe deux approches, selon que vous contrôlez ou non le code source du serveur.
Approche A : modifier le transport dans votre propre serveur
Si vous avez développé le serveur MCP, la modification représente environ cinq lignes. Avec fastmcp, la différence entre les modes de transport est un seul run() argument :
Python — stdio → Streamable HTTP, single argument change
from fastmcp import FastMCP
mcp = FastMCP("logistics-api")
@mcp.tool
def lookup_shipment(shipment_id: str) -> dict:
return {"id": shipment_id, "status": "in_transit"}
# --- BEFORE: stdio for local development ---
# mcp.run() # default transport is stdio
# --- AFTER: streamable HTTP for the gateway ---
mcp.run(
transport="http",
host="0.0.0.0",
port=8000,
path="/mcp",
)Approche B : encapsuler un serveur stdio avec mcp-proxy
De nombreux serveurs MCP open source ne prennent en charge que le stdio — les serveurs officiels GitHub, Slack et de système de fichiers, ainsi que la plupart des offres communautaires. Pour ceux-ci, TrueFoundry recommande de les encapsuler avec mcp-proxy et de les déployer comme un service régulier. L'encapsuleur termine la connexion HTTP, lance le processus enfant stdio et achemine le JSON-RPC entre eux. Du point de vue de la passerelle, le serveur encapsulé est indiscernable d'un serveur HTTP natif.
Shell — wrap a stdio server with mcp-proxy (verbatim from TrueFoundry docs)
# Wrap a stdio Python server with mcp-proxy and expose Streamable HTTP
mcp-proxy --port 8000 --host 0.0.0.0 --server stream python my_server.py
# Then register with the gateway as a regular HTTP MCP server:
# url: http://my-server.northwind.internal:8000/mcp
# transport: streamable-httpLes drapeaux exacts dépendent de l'implémentation de mcp-proxy utilisée — la variante TypeScript (punkpeye/mcp-proxy) utilise --server stream comme indiqué ici ; la variante Python (sparfenyuk/mcp-proxy) utilise --transport streamablehttp avec la commande encapsulée comme argument positionnel. Dans tous les cas, vérifiez par rapport au README amont actuel avant de livrer un guide d'exploitation, car les drapeaux de CLI peuvent varier entre les versions.
Une fois encapsulé, le serveur est enregistré auprès de la passerelle de la même manière que tout serveur MCP HTTP — consultez notre précédent article sur OAuth au niveau de la couche MCP pour le modèle de configuration. La migration est rarement un projet de code ; c'est un projet de déploiement et d'enregistrement.
9. FAQ
Le stdio est-il obsolète ?
Non. Stdio est le bon transport pour le développement local et pour toute configuration où le client et le serveur partagent une machine et un seul utilisateur. La spécification MCP définit les deux transports comme de première classe. Ce qui est obsolète est l'ancien transport HTTP+SSE (points de terminaison séparés pour POST et GET-SSE), que Streamable HTTP a remplacé.
Puis-je exécuter les deux transports sur le même serveur ?
Oui. La plupart des SDK MCP permettent à un seul serveur de se lier à plusieurs transports. Un modèle courant est stdio pour le développement local et Streamable HTTP pour la production, contrôlé par une variable d'environnement ou un drapeau de ligne de commande. La logique de l'outil est partagée ; seule l'initialisation du transport diffère.
Qu'en est-il des Server-Sent Events (SSE) en tant que transport autonome ?
L'ancien transport HTTP+SSE de la spécification 2024-11-05 utilisait deux points de terminaison — un pour les messages POST, un pour le flux SSE. Il est officiellement obsolète depuis la spécification 2025-03-26, bien que les serveurs puissent le maintenir en fonctionnement pour la compatibilité ascendante avec les clients plus anciens. Les nouvelles implémentations devraient cibler Streamable HTTP.
La passerelle ajoute-t-elle de la latence à chaque appel d'outil ?
Oui, mais le coût est faible. Une passerelle saine ajoute quelques millisecondes sur le chemin de l'accès au cache (recherche de jeton + RBAC + vérification JWT). Comparé à l'appel LLM environnant (généralement des centaines de millisecondes à des secondes) et au serveur MCP en aval, la surcharge est rarement le coût dominant. Consultez notre précédent article sur OAuth au niveau de la couche MCP pour la décomposition complète de la latence.
Qu'en est-il de WebSocket ?
Ne fait pas partie de la spécification actuelle du transport MCP. Streamable HTTP avec SSE couvre le cas d'utilisation du streaming sans nécessiter d'infrastructure WebSocket, qui est plus difficile à équilibrer la charge et plus difficile à sécuriser que le simple HTTP. Les auteurs du MCP ont choisi délibérément la sémantique HTTP.
Où se situe TrueFoundry ?
Le Passerelle MCP TrueFoundry est une entrée Streamable HTTP uniquement pour les serveurs MCP de votre entreprise. Les serveurs Stdio l'atteignent via mcp-proxy wrappers (voir §8). Une fois enregistré, chaque serveur bénéficie d'une identité uniforme, d'un contrôle d'accès basé sur les rôles (RBAC), d'un audit et d'une couche de courtier OAuth au niveau de la passerelle, quelle que soit la manière dont le serveur en amont a été initialement implémenté.
Passez à l'étape suivante
Si vous utilisez un MCP à une échelle non négligeable, l'exercice le plus efficace consiste à lister chaque serveur MCP en utilisation active, à marquer chacun comme étant uniquement stdio ou compatible HTTP, et à décider lesquels migrer en premier. Les serveurs uniquement stdio derrière mcp-proxy sont un modèle courant ; la migration tient généralement en un seul sprint.
Commencez ici : Documentation d'utilisation du SDK de la passerelle MCP de TrueFoundry. Ou réservez un audit d'architecture d'entreprise avec notre équipe.
Lectures complémentaires
Les citations sont liées en ligne tout au long du texte. La liste ci-dessous regroupe toutes les URL pour l'impression et pour se prémunir contre les liens morts.
- Spécification des transports MCP (25-11-2025). https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
- Spécification des transports MCP (26-03-2025, introduction de l'HTTP en flux continu). https://modelcontextprotocol.io/specification/2025-03-26/basic/transports
- Présentation de la passerelle MCP de TrueFoundry. https://www.truefoundry.com/docs/ai-gateway/mcp/mcp-overview
- Authentification et sécurité de la passerelle MCP de TrueFoundry. https://www.truefoundry.com/docs/ai-gateway/mcp/mcp-gateway-auth-security
- Utilisation du SDK de la passerelle MCP de TrueFoundry (exigence HTTP en flux continu). https://docs.truefoundry.com/gateway/mcp-gateway-sdk-usage
- TrueFoundry : Architecture interne du MCP (guide pour les wrappers mcp-proxy). https://www.truefoundry.com/blog/inside-the-model-context-protocol-mcp-architecture-motivation-internal-usage
- Spécification JSON-RPC 2.0. https://www.jsonrpc.org/specification
- Benchmarks de démarrage à froid AWS Lambda (Python/Node 200–400 ms). https://edgedelta.com/company/knowledge-center/aws-lambda-cold-start-cost
- mcp-proxy : pont stdio ↔ HTTP (open source). https://github.com/sparfenyuk/mcp-proxy
Remarque : Northwind Logistics est une entreprise fictive utilisée pour ancrer la conception dans un déploiement concret. Les chiffres de latence au §7 sont des estimations techniques basées sur des benchmarks de composants publiés, et non des données de télémétrie de production mesurées par TrueFoundry.
TrueFoundry AI Gateway offre une latence d'environ 3 à 4 ms, gère plus de 350 RPS sur 1 processeur virtuel, évolue horizontalement facilement et est prête pour la production, tandis que LiteLM souffre d'une latence élevée, peine à dépasser un RPS modéré, ne dispose pas d'une mise à l'échelle intégrée et convient parfaitement aux charges de travail légères ou aux prototypes.













.png)



.png)
.png)
.png)

.png)
.png)
.png)
.png)
.png)
.png)
.png)





