Dans le premier article MCP, on a fabriqué la prise universelle : un serveur en trente lignes, en STDIO, sur votre poste. Et je vous avais promis la suite : « Streamable HTTP quand il faudra partager. »
Il faut partager. Votre serveur stock fonctionne si bien que toute l’équipe le veut — et le commercial aussi, depuis son Copilot. Un serveur MCP distant, partagé, authentifié, déployé : c’est le programme du jour. Vous allez voir : c’est pas sorcier.
STDIO a une limite : c’est un outil de poste, pas un service
Rappel de la mécanique : en STDIO, le client lance votre serveur comme processus enfant. Conséquences : une copie par utilisateur, sur chaque machine, avec les droits de chacun, et la configuration qui va avec. Pour un outil personnel, parfait. Pour un serveur d’équipe — la base de données de stock, le catalogue produit — il faut l’inverse : un serveur, quelque part, et tous les clients qui s’y branchent.
C’est le rôle du transport Streamable HTTP. Au passage : si vous croisez de vieux tutoriels parlant du transport « SSE », passez votre chemin — il est déprécié, Streamable HTTP le remplace.
Le même serveur, en HTTP : dix lignes changent
La beauté de l’affaire : vos outils ne changent pas d’un caractère. Seul le branchement change — on passe d’une console Host à une application ASP.NET :
dotnet new web -n MonServeurMcp
dotnet add package ModelContextProtocol.AspNetCore
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMcpServer()
.WithHttpTransport()
.WithToolsFromAssembly(); // vos StockTools, inchangés
var app = builder.Build();
app.MapMcp("/mcp"); // le endpoint MCP vit sur /mcp
app.Run();
Votre classe StockTools de l’article précédent — attributs, descriptions, logique — se copie telle quelle. Même code d’outils, autre prise murale. Et le bonus immédiat : plus de piège stdout — en HTTP, Console.WriteLine redevient inoffensif (mais gardez les vrais logs, on y revient).
Stateful ou stateless : la décision d’architecture
Le transport HTTP a un choix structurant que STDIO n’avait pas :
- Stateful (défaut) : le serveur garde une session par client. Nécessaire pour les fonctionnalités où le serveur sollicite le client — sampling, elicitation, notifications.
- Stateless (
options.Stateless = true) : chaque requête est indépendante — indispensable pour scaler horizontalement (plusieurs instances derrière un load balancer, scale-to-zero), mais les fonctionnalités serveur-vers-client cessent de fonctionner.
La règle simple : un serveur d’outils classique (des tools qu’on appelle, point) → stateless, et scalez tranquille. Un serveur qui dialogue (demande des validations, pousse des notifications) → stateful, avec de l’affinité de session. Consignez le choix dans un ADR — c’est typiquement une décision qu’on paie plus tard.
L’authentification : c’est de l’ASP.NET, tout simplement
La bonne nouvelle cachée du transport HTTP : votre endpoint MCP est un endpoint ASP.NET comme les autres. Toute votre boîte à outils habituelle s’applique — pas de mécanique exotique à apprendre :
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o => { /* votre autorité : Entra ID, etc. */ });
app.MapMcp("/mcp").RequireAuthorization();
Côté client, les configurations MCP acceptent des en-têtes HTTP (jeton Bearer, clé API) — Copilot CLI propose même un formulaire pour les saisir, et l’OAuth est géré par les clients modernes. Trois réflexes de l’article sécurité à transposer :
- Les secrets restent côté client (variables d’environnement, gestionnaire de secrets) — jamais en dur dans un
mcp.jsoncommité. - Votre serveur revalide tout : le modèle peut envoyer n’importe quoi en paramètre — l’authentification ne remplace pas la validation d’entrées.
- Aucun secret dans les réponses : tout ce que retourne un outil part dans le contexte du modèle — et potentiellement dans des transcriptions.
Le déploiement : un conteneur comme un autre
Un serveur MCP HTTP se déploie comme n’importe quelle API ASP.NET. Le chemin le plus court aujourd’hui : Azure Container Apps —
az containerapp up --name mcp-stock --source . --ingress external --target-port 8080
Pourquoi Container Apps plutôt qu’autre chose : HTTPS fourni, scale-to-zero (un serveur MCP d’équipe dort 90 % du temps — autant ne pas le payer), et l’ingress interne si le serveur ne doit être joignable que depuis votre réseau — pour un serveur d’entreprise, c’est souvent le bon défaut. Les alternatives classiques (App Service, Functions pour les serveurs simples, AKS si vous y êtes déjà) fonctionnent aussi — c’est de l’ASP.NET, encore et toujours.
Dernier kilomètre : la distribution. Une fois l’URL stable, .github/mcp.json dans les dépôts de l’équipe :
{
"servers": {
"stock": { "type": "http", "url": "https://mcp-stock.demo.azurecontainerapps.io/mcp" }
}
}
…et tous les agents de l’équipe héritent du branchement. Écrit une fois, déployé une fois, branché partout.
Le mot d’honnêteté : vous venez de publier une API d’actions
Un serveur MCP distant, c’est une API publique dont les appels sont décidés par des LLM. Traitez-la avec le sérieux correspondant :
- Moindre privilège : le serveur stock lit le stock ; il n’a pas la chaîne de connexion d’écriture si aucun outil n’écrit.
- Journalisez les appels (qui, quel outil, quels paramètres) — c’est votre trace d’audit, et le matériau de l’article observabilité à venir.
- Limitez le débit : un agent en boucle peut marteler un outil — un rate limiter ASP.NET standard suffit.
- Versionnez le catalogue : renommer un outil casse tous les prompts qui s’y réfèrent ; les descriptions font partie du contrat.
En résumé
- STDIO = outil de poste ; Streamable HTTP = service d’équipe — et vos classes d’outils se recopient sans modification (
WithHttpTransport()+MapMcp()). - Stateless pour scaler (serveurs d’outils), stateful pour dialoguer (sampling, elicitation) — un ADR s’impose.
- L’authentification et le déploiement sont de l’ASP.NET standard : JWT/clés en en-têtes, conteneur sur Container Apps, scale-to-zero.
- Distribution par
.github/mcp.json: toute l’équipe branchée en un commit. - Et le sérieux d’une vraie API : moindre privilège, validation, journaux, rate limiting, versionnage.
De la console sur votre poste au service d’équipe sur Azure, sans réécrire un seul outil : la prise universelle tient sa promesse. Et ça, franchement… c’est pas sorcier.