MCP Avancé : Transports, Sampling et Patterns de Production
By Learnia AI Research Team
MCP Avancé : Transports, Sampling et Patterns de Production
Le Model Context Protocol (MCP) va bien au-delà d'une simple interface client-serveur. Ce guide explore les mécanismes de transport, le sampling (interaction modèle initiée par le serveur), les notifications, l'accès au système de fichiers et les patterns de déploiement en production. Si vous découvrez MCP, commencez par notre guide d'introduction au MCP avec Claude Code.
Les 3 Mécanismes de Transport MCP
Le transport est la couche qui détermine comment les messages JSON-RPC circulent entre le client et le serveur MCP. Chaque transport a ses compromis en termes de complexité, performance et cas d'usage.
STDIO — Transport Local
Le transport le plus simple : le client lance le serveur comme un sous-processus et communique via stdin/stdout.
// Serveur MCP avec transport STDIO (TypeScript SDK)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new McpServer({
name: "file-analyzer",
version: "1.0.0",
});
// Déclarer un outil
server.tool(
"analyze_file",
"Analyse le contenu d'un fichier",
{ path: { type: "string", description: "Chemin du fichier" } },
async ({ path }) => {
const content = await fs.readFile(path, "utf-8");
return {
content: [{ type: "text", text: `Fichier: ${content.length} caractères` }],
};
}
);
// Démarrer avec STDIO
const transport = new StdioServerTransport();
await server.connect(transport);
Avantages : Zéro configuration réseau, latence minimale, isolation par processus. Limites : Local uniquement, un client par serveur, pas de reprise de session.
SSE — Server-Sent Events (Legacy)
SSE utilise HTTP pour les requêtes client → serveur et un flux d'événements pour les messages serveur → client.
// Serveur MCP avec transport SSE
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
const app = express();
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/messages", res);
await server.connect(transport);
});
app.post("/messages", async (req, res) => {
await transport.handlePostMessage(req, res);
});
app.listen(3001);
⚠️ SSE est considéré legacy dans la spécification MCP actuelle. Préférez Streamable HTTP pour les nouveaux projets.
Streamable HTTP — Le Standard Production
Le transport recommandé pour la production. Le client envoie des requêtes HTTP POST, et le serveur peut répondre soit avec du JSON classique, soit ouvrir un flux SSE pour le streaming.
// Serveur MCP avec transport Streamable HTTP
import { StreamableHTTPServerTransport } from
"@modelcontextprotocol/sdk/server/streamableHttp.js";
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
onsessioninitialized: (sessionId) => {
sessions.set(sessionId, transport);
},
});
app.post("/mcp", async (req, res) => {
const sessionId = req.headers["mcp-session-id"];
// Routage vers la session existante ou création
await transport.handleRequest(req, res);
});
// Support GET pour le streaming serveur → client
app.get("/mcp", async (req, res) => {
const sessionId = req.headers["mcp-session-id"];
await transport.handleSSEStream(req, res);
});
// Support DELETE pour la terminaison de session
app.delete("/mcp", async (req, res) => {
const sessionId = req.headers["mcp-session-id"];
sessions.delete(sessionId);
res.status(200).end();
});
Avantages : Compatible avec l'infrastructure HTTP existante, reprise de session, streaming optionnel, multi-client.
Le Sampling : Quand le Serveur Demande au Modèle
Le sampling est l'une des fonctionnalités les plus puissantes et les moins comprises de MCP. Il inverse le flux habituel : au lieu que le client appelle le serveur, c'est le serveur qui demande au client d'effectuer un appel LLM.
Pourquoi le Sampling ?
Certaines opérations côté serveur nécessitent du « raisonnement IA » intermédiaire. Par exemple :
- →Analyse de code : Le serveur lit les fichiers, mais a besoin que le LLM identifie les patterns problématiques.
- →Résumé itératif : Le serveur collecte des données, demande un résumé, puis affine.
- →Décision contextuelle : Le serveur a besoin du jugement du modèle pour choisir l'étape suivante.
// Côté serveur : demander un sampling au client
server.tool(
"smart_refactor",
"Refactorise intelligemment un fichier",
{ filePath: { type: "string" } },
async ({ filePath }) => {
const code = await fs.readFile(filePath, "utf-8");
// Demander au client d'appeler le LLM
const analysis = await server.createMessage({
messages: [
{
role: "user",
content: {
type: "text",
text: `Analyse ce code et identifie les améliorations :\n\n${code}`,
},
},
],
maxTokens: 2048,
modelPreferences: {
hints: [{ name: "claude-sonnet-4-20250514" }],
},
});
// Utiliser la réponse du LLM pour effectuer le refactoring
return {
content: [{ type: "text", text: analysis.content.text }],
};
}
);
# Côté serveur en Python : sampling
@server.tool("smart_refactor")
async def smart_refactor(file_path: str) -> str:
code = Path(file_path).read_text()
# Le serveur demande au client d'appeler le LLM
result = await server.create_message(
messages=[
{
"role": "user",
"content": {
"type": "text",
"text": f"Analyse ce code et identifie les améliorations :\n\n{code}",
},
}
],
max_tokens=2048,
model_preferences={
"hints": [{"name": "claude-sonnet-4-20250514"}]
},
)
return result.content.text
Bonnes Pratiques du Sampling
- →Fournissez des
modelPreferencesavec des hints, pas des exigences. Le client choisit le modèle final. - →Limitez
maxTokensau strict nécessaire — le sampling consomme le budget de l'utilisateur. - →Messages clairs : Le prompt envoyé au LLM doit être auto-suffisant (pas de contexte implicite).
- →Gérez le refus : Le client peut refuser un sampling. Prévoyez toujours un fallback.
Notifications et Progress Tokens
MCP est un protocole bidirectionnel qui supporte les notifications — des messages qui n'attendent pas de réponse.
Notifications Serveur → Client
// Le serveur notifie un changement de ressource
server.notification({
method: "notifications/resources/updated",
params: { uri: "file:///project/config.json" },
});
// Le serveur notifie que sa liste d'outils a changé
server.notification({
method: "notifications/tools/list_changed",
});
Progress Tokens — Suivi des Opérations Longues
Pour les opérations longues, le client peut envoyer un progressToken que le serveur utilise pour émettre des mises à jour d'avancement.
// Côté client : envoyer un progress token
const result = await client.callTool({
name: "index_repository",
arguments: { repoPath: "/project" },
_meta: { progressToken: "idx-001" },
});
// Côté serveur : émettre des notifications de progression
server.tool(
"index_repository",
"Indexe un repository entier",
{ repoPath: { type: "string" } },
async ({ repoPath }, { meta }) => {
const files = await glob(`${repoPath}/**/*`);
for (let i = 0; i < files.length; i++) {
// Notifier la progression
await server.notification({
method: "notifications/progress",
params: {
progressToken: meta.progressToken,
progress: i + 1,
total: files.length,
message: `Indexation: ${files[i]}`,
},
});
await indexFile(files[i]);
}
return {
content: [{ type: "text", text: `${files.length} fichiers indexés` }],
};
}
);
Accès au Système de Fichiers
L'un des cas d'usage les plus courants de MCP est l'accès sécurisé au système de fichiers. Le serveur @modelcontextprotocol/server-filesystem officiel illustre les patterns recommandés.
Principe du Moindre Privilège
// Configuration : limiter les répertoires accessibles
const ALLOWED_DIRS = [
"/home/user/project",
"/home/user/docs",
];
function validatePath(requestedPath: string): string {
const resolved = path.resolve(requestedPath);
const isAllowed = ALLOWED_DIRS.some(
(dir) => resolved.startsWith(dir)
);
if (!isAllowed) {
throw new Error(`Accès refusé: ${resolved} hors des répertoires autorisés`);
}
return resolved;
}
// Outil de lecture sécurisé
server.tool(
"read_file",
"Lit le contenu d'un fichier dans les répertoires autorisés",
{ path: { type: "string" } },
async ({ path: filePath }) => {
const safePath = validatePath(filePath);
const content = await fs.readFile(safePath, "utf-8");
return {
content: [{ type: "text", text: content }],
};
}
);
Exposition de Fichiers comme Ressources MCP
Les ressources MCP permettent d'exposer des fichiers au client de manière structurée :
// Exposer des fichiers de configuration comme ressources
server.resource(
"project-config",
"file:///project/config.json",
"Configuration du projet",
async () => {
const config = await fs.readFile("/project/config.json", "utf-8");
return {
contents: [{
uri: "file:///project/config.json",
mimeType: "application/json",
text: config,
}],
};
}
);
Modèle de Sécurité MCP
La sécurité MCP repose sur plusieurs couches. Comprendre ce modèle est essentiel avant tout déploiement en production. Pour des patterns complémentaires sur l'architecture d'agents sécurisés, consultez notre guide des patterns d'architecture d'agents.
Les 6 Principes de Sécurité MCP
- →Validation des entrées : Chaque argument d'outil doit être validé (type, format, limites).
- →Moindre privilège : Un serveur ne devrait exposer que les outils strictement nécessaires.
- →Human-in-the-loop : Les opérations sensibles nécessitent une approbation explicite.
- →Journalisation : Chaque invocation d'outil doit être loggée avec ses paramètres et résultats.
- →Isolation : L'exécution de code doit être sandboxée (containers, VMs, etc.).
- →Authentification : Utiliser OAuth 2.1 pour les transports réseau.
// Middleware de sécurité pour un serveur MCP en production
function securityMiddleware(handler) {
return async (req, res) => {
// 1. Vérifier le token OAuth
const token = req.headers.authorization?.replace("Bearer ", "");
if (!token || !await verifyOAuthToken(token)) {
return res.status(401).json({ error: "Non autorisé" });
}
// 2. Rate limiting
const clientId = extractClientId(token);
if (await isRateLimited(clientId)) {
return res.status(429).json({ error: "Trop de requêtes" });
}
// 3. Journalisation
logger.info("mcp_request", {
clientId,
method: req.body?.method,
params: sanitize(req.body?.params),
timestamp: new Date().toISOString(),
});
return handler(req, res);
};
}
Patterns de Déploiement en Production
Pattern 1 : Gateway MCP
Un reverse proxy centralise l'authentification, le routage et le monitoring pour plusieurs serveurs MCP.
// Gateway MCP avec Express
import express from "express";
const app = express();
const SERVER_REGISTRY = {
"file-server": { url: "http://localhost:3001/mcp", auth: "internal" },
"db-server": { url: "http://localhost:3002/mcp", auth: "oauth" },
"search-server": { url: "http://localhost:3003/mcp", auth: "api-key" },
};
app.post("/mcp/:serverId", securityMiddleware(async (req, res) => {
const { serverId } = req.params;
const server = SERVER_REGISTRY[serverId];
if (!server) {
return res.status(404).json({ error: "Serveur inconnu" });
}
// Proxy vers le serveur MCP cible
const response = await fetch(server.url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"mcp-session-id": req.headers["mcp-session-id"],
},
body: JSON.stringify(req.body),
});
const data = await response.json();
res.json(data);
}));
Pattern 2 : Multi-Serveur avec Discovery
# Client MCP avec découverte dynamique de serveurs (Python)
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
class McpOrchestrator:
def __init__(self):
self.servers: dict[str, ClientSession] = {}
async def discover_and_connect(self, registry_url: str):
"""Découvre et connecte les serveurs MCP disponibles."""
async with httpx.AsyncClient() as http:
registry = await http.get(registry_url)
servers = registry.json()["servers"]
for server_info in servers:
async with streamablehttp_client(server_info["url"]) as (
read, write, _
):
session = ClientSession(read, write)
await session.initialize()
self.servers[server_info["name"]] = session
tools = await session.list_tools()
print(f" {server_info['name']}: {len(tools.tools)} outils")
async def call_tool(self, server_name: str, tool_name: str, args: dict):
"""Appelle un outil sur un serveur spécifique."""
session = self.servers[server_name]
result = await session.call_tool(tool_name, args)
return result
Pattern 3 : Gestion d'Erreurs Robuste
// Gestion d'erreurs avec retry et circuit breaker
class ResilientMcpClient {
private circuitOpen = false;
private failureCount = 0;
private readonly MAX_FAILURES = 5;
private readonly RETRY_DELAY = 1000;
async callTool(name: string, args: Record<string, unknown>) {
if (this.circuitOpen) {
throw new Error("Circuit ouvert — serveur indisponible");
}
for (let attempt = 0; attempt < 3; attempt++) {
try {
const result = await this.session.callTool({ name, arguments: args });
this.failureCount = 0; // Reset on success
return result;
} catch (error) {
this.failureCount++;
if (this.failureCount >= this.MAX_FAILURES) {
this.circuitOpen = true;
setTimeout(() => {
this.circuitOpen = false;
this.failureCount = 0;
}, 30_000); // Réessayer après 30s
}
if (attempt < 2) {
await new Promise((r) =>
setTimeout(r, this.RETRY_DELAY * (attempt + 1))
);
}
}
}
throw new Error(`Échec après 3 tentatives: ${name}`);
}
}
Pour d'autres patterns de production avec Claude, consultez notre guide sur le prompt caching et le protocole MCP.
Checklist de Déploiement Production
Avant de déployer un serveur MCP en production, validez cette checklist :
| Catégorie | Vérification | Priorité |
|---|---|---|
| Transport | Streamable HTTP configuré avec sessions | Critique |
| Auth | OAuth 2.1 implémenté et testé | Critique |
| TLS | HTTPS uniquement, certificats valides | Critique |
| Validation | Toutes les entrées d'outils validées | Haute |
| Rate Limit | Limites par client configurées | Haute |
| Logging | Chaque invocation journalisée | Haute |
| Erreurs | Circuit breaker / retry implémenté | Moyenne |
| Monitoring | Métriques de latence et taux d'erreurs | Moyenne |
| Sandbox | Exécution de code isolée | Selon usage |
| Progress | Progress tokens pour les ops longues | Selon usage |
Pour intégrer ces patterns dans un workflow d'équipe, consultez notre guide sur la collaboration d'équipe avec Claude Code. Pour approfondir l'utilisation des outils avec Claude, voir notre guide complet du tool use.
Conclusion
MCP en production exige une compréhension fine des mécanismes de transport, du modèle de sécurité et des patterns de résilience. Les points clés à retenir :
- →Utilisez Streamable HTTP pour tout déploiement réseau — SSE est legacy.
- →Le sampling ouvre des possibilités uniques mais exige un contrôle strict côté client.
- →Les notifications et progress tokens améliorent l'UX pour les opérations longues.
- →La sécurité est multicouche : transport, authentification, validation, sandboxing.
- →Les patterns Gateway et Circuit Breaker sont essentiels en production.
Weekly AI Insights
Tools, techniques & news — curated for AI practitioners. Free, no spam.
Free, no spam. Unsubscribe anytime.
→Related Articles
FAQ
Quelles sont les différences entre les trois mécanismes de transport MCP ?+
STDIO est le plus simple, idéal pour les processus locaux avec une latence minimale. SSE (Server-Sent Events) permet une communication unidirectionnelle du serveur vers le client via HTTP. Streamable HTTP est le transport recommandé pour la production : il combine requêtes HTTP classiques et streaming SSE optionnel, avec support natif de la reprise de session.
Qu'est-ce que le sampling dans le protocole MCP ?+
Le sampling permet à un serveur MCP de demander au client d'effectuer un appel LLM. Cela inverse le flux habituel : au lieu que le client appelle le serveur, c'est le serveur qui initie une interaction avec le modèle via le client, tout en gardant l'utilisateur dans la boucle de contrôle.
Comment sécuriser un serveur MCP en production ?+
Les bonnes pratiques incluent : valider toutes les entrées côté serveur, implémenter le principe du moindre privilège pour les outils, utiliser OAuth 2.1 pour l'authentification, journaliser toutes les invocations d'outils, limiter le rate des requêtes et sandboxer l'exécution de code si nécessaire.
Quand utiliser les progress tokens dans MCP ?+
Les progress tokens sont utiles pour les opérations longues (indexation de fichiers, téléchargements, analyses complexes). Le client envoie un _meta.progressToken dans sa requête, et le serveur émet des notifications/progress avec l'avancement, permettant au client d'afficher une barre de progression.