Bienvenue sur FlowZap, l'application pour créer des diagrammes avec Rapidité, Clarté et Contrôle.

Patterns d'Architecture MCP pour Agents de Niveau Production

28/02/2025

Tags: MCP, Agents IA, Patterns d'Architecture, Production, Conception Système, Scalabilité, Sécurité

Jules Kovac

Jules Kovac

Business Analyst, Founder

Patterns d'Architecture MCP pour Agents de Niveau Production

 

La Réalité de la Production MCP

Si vous déployez des agents sur MCP en production, les douleurs du jour 2 peuvent vous faire sentir que vous perdez le contrôle : les factures de tokens explosent, les serveurs distants tombent en panne, et la sécurité demande comment verrouiller ce système. Les points de douleur typiques incluent :

  • Votre serveur MCP fonctionne en dev et meurt silencieusement en prod.
  • Votre facture de tokens ressemble à un acompte pour une maison.
  • Votre configuration "simple" d'agent s'est transformée en système distribué avec 14 modes de défaillance.

C'est l'histoire réelle du Model Context Protocol : ce n'est pas une spécification abstraite, c'est la plomberie entre vos agents et les outils et données du monde réel qu'ils doivent toucher.

 

 

Six Patterns pour Agents de Niveau Production

Cet article relie ces douleurs à six patterns d'architecture MCP que vous pouvez réellement utiliser pour déployer et scaler des agents sans perdre le contrôle. Ces six patterns constituent un guide de terrain pratique, pas une spécification officielle — chacun correspond à un pattern d'ingénierie bien documenté avec des implémentations en production pour l'étayer.

 

 

1. Direct Connect — "Livrer Ce Soir"

Slogan : Vous, un agent, un serveur MCP, pas de drame.

Direct Connect est le "monolithe de MCP" — votre application hôte parle directement au serveur MCP via stdio ou HTTP, sans sauts supplémentaires. C'est parfait quand vous voulez juste voir quelque chose fonctionner et que vous ne vous souciez pas (encore) des diapositives de gouvernance.

Idéal quand :

  • Vous construisez un MVP ou une démo de hackathon.
  • Équipe unique, périmètre de confiance unique, tout s'exécute dans "votre" infrastructure.
  • Vous voulez la latence la plus faible possible et le débogage le plus simple.

À éviter quand :

  • Vous exposez des outils entre équipes ou locataires.
  • La sécurité veut des journaux d'audit, des politiques d'accès, et quelqu'un dit "SOX."

Évaluation : Sécurité (⭐☆☆☆), Scalabilité (⭐☆☆☆), Efficacité des coûts (⭐⭐⭐☆), Débogage (⭐⭐⭐⭐)

 

Voyez à quoi cela ressemble dans FlowZap :

Host { # Application Hôte
  n1: circle label="L'utilisateur envoie une invite"
  n2: rectangle label="L'agent construit une requête JSON-RPC"
  n3: rectangle label="Envoyer la requête via stdio"
  n4: rectangle label="Recevoir le résultat JSON-RPC"
  n5: rectangle label="L'agent répond à l'utilisateur"

  n1.handle(right) -> n2.handle(left)
  n2.handle(right) -> n3.handle(left)
  n3.handle(bottom) -> MCPServer.n6.handle(top) [label="Requête JSON-RPC"]
  n4.handle(right) -> n5.handle(left)
}

MCPServer { # Serveur MCP
  n6: rectangle label="Analyser la requête entrante"
  n7: rectangle label="Exécuter l'outil ou la ressource"
  n8: rectangle label="Construire la réponse JSON-RPC"

  n6.handle(right) -> n7.handle(left)
  n7.handle(right) -> n8.handle(left)
  n8.handle(top) -> Host.n4.handle(bottom) [label="Réponse JSON-RPC"]
}

 

 

2. Gateway Proxy — "Rendre la Sécurité Heureuse"

Slogan : Mettre un videur devant vos outils.

Gateway Proxy place une passerelle API entre votre agent et les serveurs MCP pour gérer l'authentification, les limites de débit et l'audit. Votre agent pense toujours appeler des outils normalement ; la passerelle impose silencieusement OAuth 2.0, SAML, SSO, la limitation de débit par outil et l'application des quotas par équipe avant que la requête n'atteigne un serveur MCP. Ce n'est pas théorique — des produits comme MintMCP Gateway, Gravitee MCP Proxy, Kong et Azure APIM implémentent tous ce pattern exact.

Le cas réel pour ceci est frappant : sans contrôles au niveau de la passerelle, un seul agent bloqué dans une boucle de réessai peut épuiser les budgets API en heures. Les passerelles avec quotas basés sur les tokens, allocations d'rafale et granularité par outil sont la prévention standard.

Idéal quand :

  • Authentification cohérente (OAuth/JWT/clés API) nécessaire pour tous les outils.
  • Journaux de requêtes requis pour la conformité (SOC2, RGPD) ou la réponse aux incidents.
  • Plusieurs équipes ou clients partagent le même patrimoine MCP.

À éviter quand :

  • Ultra-sensible à la latence où chaque milliseconde compte — la passerelle ajoute un saut réseau.
  • Pas assez de trafic pour justifier la complexité ajoutée.

Évaluation : Sécurité (⭐⭐⭐⭐), Scalabilité (⭐⭐⭐⭐), Efficacité des coûts (⭐⭐☆☆), Débogage (⭐⭐⭐☆)

 

Code FlowZap

Host { # Application Hôte
  n1: circle label="L'utilisateur envoie une invite"
  n2: rectangle label="L'agent construit un appel d'outil"
  n3: rectangle label="Envoyer la requête à la passerelle"
  n4: rectangle label="Recevoir la réponse de la passerelle"
  n5: rectangle label="L'agent répond à l'utilisateur"

  n1.handle(right) -> n2.handle(left)
  n2.handle(right) -> n3.handle(left)
  n3.handle(bottom) -> Gateway.n6.handle(top) [label="Requête d'outil"]
  n4.handle(right) -> n5.handle(left)
}

Gateway { # Passerelle MCP
  n6: rectangle label="Recevoir et journaliser la requête"
  n7: diamond label="Autorisé ?"
  n8: rectangle label="Transmettre au serveur MCP"
  n9: rectangle label="Recevoir la réponse MCP"
  n10: rectangle label="Journaliser et retourner"

  n6.handle(right) -> n7.handle(left)
  n7.handle(right) -> n8.handle(left) [label="Oui"]
  n7.handle(top) -> Host.n4.handle(left) [label="401 Non Autorisé"]
  n8.handle(bottom) -> MCPServer.n11.handle(top) [label="Requête transmise"]
  n9.handle(right) -> n10.handle(left)
  n10.handle(top) -> Host.n4.handle(bottom) [label="Réponse autorisée"]
}

MCPServer { # Serveur MCP
  n11: rectangle label="Exécuter l'outil"
  n12: rectangle label="Retourner le résultat"

  n11.handle(right) -> n12.handle(left)
  n12.handle(top) -> Gateway.n9.handle(bottom) [label="Résultat de l'outil"]
}

 

 

3. Tool Router — "Arrêter de Donner un Annuaire à l'LLM"

Slogan : 50 outils, 1 agent, utilisation raisonnable des tokens.

Le pattern Tool Router place un cerveau de routage devant vos outils pour que l'LLM ne "voie" que le sous-ensemble dont il a réellement besoin. C'est un problème documenté et sérieux : les définitions complètes de schémas d'outils chargées dans le contexte peuvent consommer 40% des tokens disponibles avant que l'utilisateur n'envoie son premier message. Writer.com a résolu ceci en construisant un "méta-outil de recherche" sémantique qui utilise des embeddings vectoriels et la similarité cosinus pour faire correspondre l'intention de l'utilisateur aux bons outils dynamiquement. Speakeasy a réalisé une réduction de 96% des tokens d'entrée et 90% de la consommation totale de tokens en utilisant des ensembles d'outils dynamiques.

L'approche Semantic MCP Router offre deux chemins de découverte : un ensemble d'outils par défaut "top 20" curé pré-chargé dans le contexte, et un chemin de recherche sémantique profond pour les outils spécialisés. Ce modèle à double voie garde le chemin rapide rapide et la longue traîne accessible sans gonfler chaque prompt.

Idéal quand :

  • Au-delà de cinq outils et les prompts gonflent comme des fous.
  • Différents cas d'usage nécessitent différentes tranches d'outils (facturation vs. analytics vs. ops).
  • Réduction de la taille du contexte nécessaire sans simplifier l'agent.

À éviter quand :

  • Une petite application avec quelques outils.
  • L'équipe ne peut pas encore supporter la logique de routage, les métriques et les fallbacks.

Évaluation : Sécurité (⭐⭐⭐☆), Scalabilité (⭐⭐⭐⭐), Efficacité des coûts (⭐⭐⭐⭐), Débogage (⭐⭐☆☆)

 

Code FlowZap

Host { # Application Hôte
  n1: circle label="L'utilisateur envoie une invite"
  n2: rectangle label="L'agent extrait l'intention"
  n3: rectangle label="Envoyer l'intention au routeur"
  n4: rectangle label="Recevoir le résultat routé"
  n5: rectangle label="L'agent répond à l'utilisateur"

  n1.handle(right) -> n2.handle(left)
  n2.handle(right) -> n3.handle(left)
  n3.handle(bottom) -> Router.n6.handle(top) [label="Intention + requête d'outil"]
  n4.handle(right) -> n5.handle(left)
}

Router { # Routeur d'Outils
  n6: rectangle label="Recevoir l'intention"
  n7: rectangle label="Correspondance sémantique via embeddings"
  n8: diamond label="Quel serveur MCP ?"
  n9: rectangle label="Transmettre au Serveur A"
  n10: rectangle label="Transmettre au Serveur B"
  n11: rectangle label="Normaliser et retourner le résultat"

  n6.handle(right) -> n7.handle(left)
  n7.handle(right) -> n8.handle(left)
  n8.handle(bottom) -> n9.handle(top) [label="Route A"]
  n8.handle(right) -> n10.handle(left) [label="Route B"]
  n9.handle(bottom) -> ServerA.n12.handle(top) [label="Appeler Serveur A"]
  n10.handle(bottom) -> ServerB.n14.handle(top) [label="Appeler Serveur B"]
  n11.handle(top) -> Host.n4.handle(bottom) [label="Résultat final"]
}

ServerA { # Serveur MCP A
  n12: rectangle label="Exécuter l'outil A"
  n13: rectangle label="Retourner le résultat A"

  n12.handle(right) -> n13.handle(left)
  n13.handle(top) -> Router.n11.handle(bottom) [label="Résultat A"]
}

ServerB { # Serveur MCP B
  n14: rectangle label="Exécuter l'outil B"
  n15: rectangle label="Retourner le résultat B"

  n14.handle(right) -> n15.handle(left)
  n15.handle(top) -> Router.n11.handle(left) [label="Résultat B"]
}

 

 

4. Agent Mesh — "Escouade d'Agents, Un Cerveau"

Slogan : Beaucoup de spécialistes, contexte partagé, chaos contrôlé.

Agent Mesh est ce qui arrive quand vous arrêtez de prétendre qu'un agent peut tout faire. Plusieurs agents communiquent via un courtier de contexte partagé soutenu par MCP, permettant l'accès coordonné aux outils et la synchronisation d'état. L'implémentation Azure de Microsoft utilise l'état de session persistant via Cosmos DB (avec fallback en mémoire), supportant l'échange dynamique de patterns et les interactions multi-agents traçables.

Le choix architectural clé ici est la chorégraphie vs. l'orchestration. Dans les configurations orchestrées, un agent Manager coordonne toutes les interactions, maintient un registre des tâches, et peut re-planifier dynamiquement basé sur les découvertes intermédiaires. En chorégraphie, les agents communiquent pair-à-pair via des échanges JSON-RPC structurés via MCP, où n'importe quel agent peut demander de l'aide à n'importe quel autre. Les deux approches s'appuient sur une mémoire partagée pour que tous les agents accèdent au même magasin d'état pour un contexte cohérent.

Le risque est réel : les agents peuvent se renvoyer des tâches indéfiniment sans conditions de terminaison et observabilité appropriées.

Idéal quand :

  • Des rôles distincts existent : planificateur, codeur, réviseur, opérateur.
  • Un état partagé (tâches, ressources, workflows) est nécessaire au lieu de silos isolés.
  • Les charges de travail se décomposent naturellement en sous-tâches parallélisables.

À éviter quand :

  • Un seul agent bien outillé suffit.
  • L'observabilité et le traçage ne sont pas encore en place (le débogage sera douloureux).

Évaluation : Sécurité (⭐⭐⭐☆), Scalabilité (⭐⭐⭐⭐), Efficacité des coûts (⭐⭐☆☆), Débogage (⭐⭐☆☆)

 

Code FlowZap

Orchestrator { # Agent Orchestrateur
  n1: circle label="Tâche complexe reçue"
  n2: rectangle label="Décomposer en sous-tâches"
  n3: rectangle label="Assigner sous-tâche à l'Agent B"
  n4: rectangle label="Recevoir le résultat de sous-tâche"
  n5: rectangle label="Demander le contexte partagé"
  n6: rectangle label="Compiler la réponse finale"

  n1.handle(right) -> n2.handle(left)
  n2.handle(right) -> n3.handle(left)
  n3.handle(bottom) -> Worker.n7.handle(top) [label="Assignation de sous-tâche"]
  n4.handle(right) -> n5.handle(left)
  n5.handle(bottom) -> Broker.n11.handle(top) [label="Demande de contexte"]
  n6.handle(left) -> n2.handle(bottom) [label="Itération suivante"]
}

Worker { # Agent Travailleur
  n7: rectangle label="Recevoir la sous-tâche"
  n8: rectangle label="Récupérer le contexte partagé"
  n9: rectangle label="Appeler l'outil MCP"
  n10: rectangle label="Retourner le résultat à l'orchestrateur"

  n7.handle(right) -> n8.handle(left)
  n8.handle(bottom) -> Broker.n11.handle(left) [label="Demande de contexte"]
  n8.handle(right) -> n9.handle(left)
  n9.handle(bottom) -> MCPServer.n13.handle(top) [label="Appel d'outil MCP"]
  n10.handle(top) -> Orchestrator.n4.handle(bottom) [label="Résultat de sous-tâche"]
}

Broker { # Courtier de Contexte
  n11: rectangle label="Résoudre la demande de contexte"
  n12: rectangle label="Retourner l'état partagé"

  n11.handle(right) -> n12.handle(left)
  n12.handle(top) -> Orchestrator.n6.handle(bottom) [label="Contexte à l'orchestrateur"]
  n12.handle(left) -> Worker.n9.handle(top) [label="Contexte au travailleur"]
}

MCPServer { # Serveur MCP
  n13: rectangle label="Exécuter l'outil"
  n14: rectangle label="Retourner la sortie de l'outil"

  n13.handle(right) -> n14.handle(left)
  n14.handle(top) -> Worker.n10.handle(bottom) [label="Sortie de l'outil"]
}

 

 

5. Circuit Breaker — "Fin des Appels Zombies"

Slogan : Si un outil meurt, arrêtez de le marteler.

Circuit Breaker enveloppe les appels MCP avec des portes conscientes de la santé utilisant trois états : Fermé (opération normale, les requêtes passent), Ouvert (défaillances détectées, les requêtes échouent rapidement), et Semi-Ouvert (tester si le service a récupéré). C'est une hygiène classique des systèmes distribués appliquée directement aux appels d'outils MCP.

Sans disjoncteurs, la cascade de défaillance est prévisible : Outil A échoue → les réessais s'accumulent → ressources épuisées → autres outils ralentissent → surcharge du système → tout échoue. Avec les disjoncteurs : Outil A échoue → circuit ouvert → échec rapide → autres outils non affectés → système stable → récupération quand prêt.

Ce pattern a des implémentations MCP réelles. Le mcp-context-forge d'IBM a une demande de fonctionnalité complète pour les disjoncteurs avec récupération d'état semi-ouvert, seuils de défaillance, et protection contre les échecs rapides. Le SDK Go MCP inclut un exemple de récupération d'erreur prêt pour la production implémentant des disjoncteurs aux côtés de réessai avec backoff exponentiel et isolation de cloison. Octopus.com a documenté une implémentation complète Langchain + Python utilisant la bibliothèque pybreaker pour les appels d'outils MCP.

Idéal quand :

  • Dépendance d'API tierces instables ou de bases de données legacy.
  • Les agents ont été observés gelés parce qu'un seul serveur MCP est devenu non réactif.
  • La dégradation gracieuse est préférée au comportement tout-ou-rien.

À éviter quand :

  • Tout est local, rapide et solide comme un roc (par exemple, stdio vers un processus local).
  • Aucun plan n'existe pour quoi faire en "échec rapide" (outils de fallback, messages utilisateur, etc.).

Évaluation : Sécurité (⭐⭐⭐☆), Scalabilité (⭐⭐⭐⭐), Efficacité des coûts (⭐⭐⭐☆), Débogage (⭐⭐⭐⭐)

 

Code FlowZap

Host { # Application Hôte
  n1: circle label="L'utilisateur envoie une invite"
  n2: rectangle label="L'agent prépare l'appel MCP"
  n3: rectangle label="Passer l'appel au disjoncteur"
  n4: rectangle label="Recevoir le résultat ou l'erreur"
  n5: rectangle label="L'agent répond à l'utilisateur"

  n1.handle(right) -> n2.handle(left)
  n2.handle(right) -> n3.handle(left)
  n3.handle(bottom) -> CB.n6.handle(top) [label="Appel d'outil MCP"]
  n4.handle(right) -> n5.handle(left)
}

CB { # Disjoncteur
  n6: rectangle label="Vérifier l'état du circuit"
  n7: diamond label="Circuit ouvert ?"
  n8: rectangle label="Transmettre au serveur MCP"
  n9: rectangle label="Échec rapide avec erreur"
  n10: diamond label="Appel réussi ?"
  n11: rectangle label="Enregistrer le succès"
  n12: rectangle label="Enregistrer l'échec et vérifier le seuil"

  n6.handle(right) -> n7.handle(left)
  n7.handle(right) -> n8.handle(left) [label="Fermé"]
  n7.handle(bottom) -> n9.handle(top) [label="Ouvert"]
  n8.handle(bottom) -> MCPServer.n13.handle(top) [label="Requête transmise"]
  n9.handle(top) -> Host.n4.handle(bottom) [label="ErreurCircuitOuvert"]
  n10.handle(right) -> n11.handle(left) [label="Oui"]
  n10.handle(bottom) -> n12.handle(top) [label="Non"]
  n11.handle(top) -> Host.n4.handle(left) [label="Retourner le résultat"]
  n12.handle(top) -> Host.n4.handle(right) [label="Retourner l'erreur"]
}

MCPServer { # Serveur MCP
  n13: rectangle label="Tenter l'exécution de l'outil"
  n14: rectangle label="Retourner le résultat ou l'erreur"

  n13.handle(right) -> n14.handle(left)
  n14.handle(top) -> CB.n10.handle(bottom) [label="Résultat de l'exécution"]
}

 

 

6. Context Proxy — "Réduire Votre Facture LLM de Moitié"

Slogan : Cachez les trucs ennuyeux, payez pour les trucs intelligents.

Context Proxy est une couche de mise en cache et de compression qui se place entre l'agent et les serveurs MCP, interceptant les requêtes de contexte redondantes avant qu'elles n'atteignent le réseau. Cela traite le contexte comme une ressource réelle gérée avec des TTL, des crochets d'invalidation, et un monitoring du taux de succès — pas comme un flux magique infini de tokens.

L'évidence pour ce pattern est forte. Le serveur MCP Token Optimizer combine la compression Brotli avec une mise en cache persistante basée sur SQLite pour atteindre jusqu'à 95%+ de réduction de tokens. Le projet mcp-context-proxy sur GitHub agit comme un proxy MCP transparent qui compresse les grandes réponses d'outils en utilisant un LLM externe avant de les passer aux modèles locaux à ressources limitées. Les stratégies efficaces incluent la mise en cache au niveau des prompts (réutiliser les paires complètes prompt-réponse), la mise en cache de contexte partielle (réutiliser les prompts système statiques), et la mise en cache sémantique (faire correspondre les requêtes quasi-dupliquées via des embeddings).

L'invalidation de cache est la partie difficile. L'expiration basée sur le temps fonctionne pour les données changeant lentement, l'invalidation basée sur les événements gère les mises à jour de données, et les approches hybrides équilibrent fraîcheur et efficacité. Définissez la tolérance à l'obsolescence basée sur les exigences réelles de l'application.

Idéal quand :

  • Les agents continuent de demander les mêmes docs, schémas, ou tranches de repo.
  • La récupération opère sur des données relativement statiques (bases de connaissances, spécifications).
  • La facture crie "gonflement de contexte" plus que "taille de modèle".

À éviter quand :

  • Les données sont en temps réel et l'obsolescence est dangereuse (trading, opérations critiques).
  • Aucune stratégie claire pour l'invalidation et la fraîcheur.

Évaluation : Sécurité (⭐⭐⭐☆), Scalabilité (⭐⭐⭐⭐), Efficacité des coûts (⭐⭐⭐⭐), Débogage (⭐⭐⭐☆)

 

Code FlowZap

Host { # Application Hôte
  n1: circle label="L'utilisateur envoie une invite"
  n2: rectangle label="L'agent demande le contexte"
  n3: rectangle label="Recevoir le contexte"
  n4: rectangle label="L'agent répond à l'utilisateur"

  n1.handle(right) -> n2.handle(left)
  n2.handle(bottom) -> Proxy.n5.handle(top) [label="Demande de contexte"]
  n3.handle(right) -> n4.handle(left)
}

Proxy { # Proxy de Contexte
  n5: rectangle label="Recevoir la demande de contexte"
  n6: rectangle label="Vérifier le cache avec TTL"
  n7: diamond label="Succès du cache ?"
  n8: rectangle label="Retourner le contexte en cache"
  n9: rectangle label="Récupérer frais du serveur MCP"
  n10: rectangle label="Compresser et mettre en cache la réponse"

  n5.handle(right) -> n6.handle(left)
  n6.handle(right) -> n7.handle(left)
  n7.handle(right) -> n8.handle(left) [label="Succès"]
  n7.handle(bottom) -> n9.handle(top) [label="Échec"]
  n8.handle(top) -> Host.n3.handle(bottom) [label="Contexte en cache"]
  n9.handle(bottom) -> MCPServer.n11.handle(top) [label="Requête de récupération"]
  n10.handle(top) -> Host.n3.handle(left) [label="Contexte frais"]
}

MCPServer { # Serveur MCP
  n11: rectangle label="Récupérer le contexte complet"
  n12: rectangle label="Retourner les données fraîches"

  n11.handle(right) -> n12.handle(left)
  n12.handle(top) -> Proxy.n10.handle(bottom) [label="Données fraîches"]
}

 

 

Comment Utiliser Réellement Ces Patterns

Si vous vous demandez "lequel je choisis ?", utilisez cette échelle :

  1. Commencez par Direct Connect pour faire fonctionner quelque chose.
  2. Ajoutez Gateway Proxy une fois que de vrais utilisateurs et la sécurité arrivent.
  3. Introduisez Tool Router quand vous dépassez 5 outils et que la douleur des tokens arrive.
  4. Intégrez Circuit Breaker dès que quelque chose de distant peut échouer (ce sera le cas).
  5. Portez Context Proxy la première fois que la finance vous envoie un message sur les coûts LLM.
  6. Ne considérez Agent Mesh que si un seul agent ne peut vraiment pas suivre.

 

 

Inspirations:

Retour à tous les articles du blogue