Tu fais tourner ta CI sur le backend. Tu lintes ton frontend. Tes conteneurs Docker ont des healthchecks. Tout dans ta stack a une stratégie de test — sauf les connexions MCP dont ton agent dépend à chaque appel.

Le 19 avril, l'équipe MCP a publié sa roadmap 2026. Quatre priorités : autorisation, registre, primitives UX riches et capacités agentiques. Tests, health checks, validation de contrats — pas dans la liste. Pas mentionnés. Pas prévus. 😾

Donc tu te débrouilles seul. Voici comment tester les serveurs MCP aujourd'hui avec les outils qui existent réellement.

Ce que tu as sous la main

L'écosystème MCP compte environ 17 000 serveurs enregistrés. Les audits communautaires montrent qu'environ la moitié répond de manière fiable à un instant donné. Ton agent se connecte à trois serveurs ? Statistiquement, l'un d'entre eux déconne en ce moment même.

Testomat.io a publié l'étude la plus complète sur les outils de test MCP le 8 avril. Leur conclusion est sans appel : rien ne parle MCP nativement côté tests. Tout repose sur du scotch par-dessus des frameworks HTTP génériques. Aucun test runner ne comprend le transport MCP. Aucune bibliothèque d'assertions ne sait à quoi ressemble une réponse d'outil valide. Tu construis l'intégralité de la stack de test from scratch pour chaque serveur dont tu dépends.

Voici l'inventaire complet de ce qui existe — et comment le faire marcher.

MCP Inspector : le point de départ manuel

MCP Inspector est l'outil de débogage officiel — imagine Postman pour MCP. Tu te connectes à un serveur, tu appelles des outils à la main, tu inspectes les réponses.

Ce qu'il t'apporte :

  • Découverte interactive des outils et invocation
  • Inspection brute des réponses JSON
  • Diagnostics de connexion pour les transports stdio et HTTP+SSE

Ce qu'il ne fait pas :

  • Intégration CI
  • Détection de régressions
  • Suites de tests automatisées
  • Validation des réponses contre un schéma

C'est un tournevis. Utile pour bidouiller en dev, inutile pour prévenir les régressions en production. Il te faut un harnais de test. 😹

Écrire des tests wrapper (l'approche scotch-et-ficelle)

La plupart des équipes qui testent MCP aujourd'hui écrivent des tests wrapper — des suites pytest ou Jest classiques qui appellent les outils directement via le SDK client MCP et vérifient ce qui revient.

# Exemple pytest — tester un outil de serveur MCP
import json
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def test_search_tool_returns_results():
    server = StdioServerParameters(
        command="npx",
        args=["-y", "@example/mcp-search-server"]
    )
    async with stdio_client(server) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            
            result = await session.call_tool(
                "search",
                arguments={"query": "test query", "limit": 5}
            )
            
            assert result.content is not None
            assert len(result.content) > 0
            assert result.content[0].type == "text"
            
            data = json.loads(result.content[0].text)
            assert "results" in data
            assert len(data["results"]) <= 5

Ça marche jusqu'au jour où le serveur upstream change son format de réponse. Ce qui arrive en silence, sans versioning, sans changelog — la spec MCP n'a aucune convention semver, pas d'équivalent de lockfile, aucun mécanisme pour annoncer les breaking changes. Ton assertion vérifie data["results"] — le serveur renomme ça en data["items"] un mardi à 2h du mat'. Dans le meilleur cas : ton test passe au rouge. Dans le pire : le champ existe toujours mais la structure interne a changé, ton test reste vert, ton agent hallucine sur des données malformées, et tu payes au token halluciné.

Tests de contrat sans contrat

Le problème fondamental : les serveurs MCP ne publient pas de schémas de réponse. La spec décrit ce qu'un outil devrait faire en langage naturel. Elle ne propose aucun contrat lisible par une machine pour valider quoi que ce soit.

La solution de contournement : génère le tien.

# Étape 1 : Enregistrer les réponses réelles au fil du temps
from genson import SchemaBuilder

builder = SchemaBuilder()
for response in recorded_responses:  # collectées depuis staging/dev
    builder.add_object(json.loads(response))

inferred_schema = builder.to_schema()
# Sauvegarde dans ton repo comme "contrat"

# Étape 2 : Valider en CI
from jsonschema import validate, ValidationError

def test_tool_response_matches_contract():
    response = call_mcp_tool("search", {"query": "test"})
    try:
        validate(instance=response, schema=inferred_schema)
    except ValidationError as e:
        pytest.fail(f"Violation de contrat : {e.message}")

Le processus : enregistre les réponses réelles du serveur pendant une semaine. Infère un JSON Schema à partir de ces réponses avec un générateur de schéma. Commite ce schéma dans ton repo. Valide les réponses futures en CI.

C'est du test de contrat rétro-ingéniéré. Pas élégant. Mais ça attrape les changements upstream silencieux qui atteindraient sinon la production sans être détectés. Quand le schéma casse, ton pipeline casse — bruyamment, en CI, pas silencieusement dans l'output de ton agent. 😸

Monitoring de santé : construis-le ou prie

Ton orchestrateur ping les conteneurs Docker. Ton load balancer vérifie /health. Les serveurs MCP n'offrent aucun endpoint de santé — la spec n'en définit pas. Un serveur répond ou ne répond pas, et tu le découvres quand l'appel d'outil de ton agent reste en suspens.

Construis ton propre health check :

import asyncio
from datetime import datetime

async def check_mcp_health(server_params, timeout=10):
    try:
        async with asyncio.timeout(timeout):
            async with stdio_client(server_params) as (read, write):
                async with ClientSession(read, write) as session:
                    await session.initialize()
                    tools = await session.list_tools()
                    return {
                        "status": "healthy",
                        "tools_available": len(tools.tools),
                        "checked_at": datetime.utcnow().isoformat()
                    }
    except (asyncio.TimeoutError, Exception) as e:
        return {
            "status": "unhealthy",
            "error": str(e),
            "checked_at": datetime.utcnow().isoformat()
        }

Fais tourner ça en cron. Alerte sur les échecs consécutifs. Vérifie pas seulement la connectivité mais aussi la liste des outils — les serveurs ajoutent et suppriment des outils sans prévenir, et ton agent qui s'attend à trouver search_v2 après que le serveur l'a discrètement supprimé produit le genre de panne qui ressemble à un bug d'agent mais n'en est pas un.

Injection de pannes : la partie que tout le monde zappe

Ton agent appelle un outil. L'outil timeout. Qu'est-ce qui se passe ensuite ?

Si tu n'as pas testé ça, la réponse est : le modèle improvise. Il peut réessayer en boucle à l'infini. Il peut halluciner la réponse attendue. Il peut s'excuser auprès de l'utilisateur et ne rien faire. Tu ne le sauras qu'en production, et la production facture au token la leçon. 🙀

Wrappe ton client MCP pour simuler des pannes :

import random

class ChaosProxy:
    """Enveloppe une vraie session MCP pour injecter des pannes pendant les tests."""
    def __init__(self, real_session, failure_rate=0.1, corruption_rate=0.05):
        self.session = real_session
        self.failure_rate = failure_rate
        self.corruption_rate = corruption_rate
    
    async def call_tool(self, name, arguments):
        # Simuler un timeout
        if random.random() < self.failure_rate:
            raise TimeoutError(f"Timeout MCP simulé sur {name}")
        
        result = await self.session.call_tool(name, arguments)
        
        # Simuler une réponse corrompue
        if random.random() < self.corruption_rate:
            return self._corrupt_response(result)
        
        return result
    
    def _corrupt_response(self, result):
        # Retourne une enveloppe MCP valide avec du contenu pourri
        # Teste si ton agent gère gracieusement les données malformées
        ...

Fais passer ton agent à travers ce proxy avec un taux d'échec de 10 %. Observe comment il gère les timeouts, les données pourries et les outils manquants. Corrige les plantages. Augmente le taux. Recommence jusqu'à ce que ton agent se dégrade gracieusement au lieu d'halluciner avec aplomb.

La stack de test complète

Voici à quoi ressemble un déploiement MCP testé aujourd'hui — tout fait main, rien de standardisé :

Couche Outil Ce que ça attrape
Exploration manuelle MCP Inspector "Cet outil existe et répond ?"
Tests unitaires Wrappers pytest/Jest Forme de la réponse, comportement de base
Tests de contrat JSON Schema inféré Changements silencieux de format upstream
Monitoring de santé Cron custom + alerting Pannes serveur, dérive de la liste d'outils
Injection de pannes Wrapper chaos proxy Comportement de l'agent en conditions dégradées
Tests d'intégration Exécutions agent end-to-end Régressions du pipeline complet

Total d'outillage standardisé que la spec MCP fournit pour tout ça : zéro. Chaque couche que tu construis, tu la maintiens aussi, tu la débogues, et tu la reconstruis quand les changements de transport cassent ton infra de test. 😾

Les pièges qui vont te mordre

Pollution d'état. Les outils MCP peuvent avoir des effets de bord — écrire des données, supprimer des enregistrements, facturer de l'argent. La spec ne définit aucun mode mock. Tu construis un faux serveur pour les tests, tu tapes sur la prod (dangereux), ou tu maintiens un environnement de staging par dépendance MCP (coûteux). La plupart des équipes testent contre la prod et espèrent. L'espoir n'est pas une stratégie de test.

Différence de transport. Tes tests tournent en stdio. La prod tourne en HTTP+SSE. Ils se comportent différemment sous charge, timeout différemment, plantent différemment. Teste les deux transports ou accepte que ton environnement de test ne correspond pas à la production.

Expiration d'auth. Les tokens OAuth expirent. Ta CI tourne à 3h du mat'. Le token a expiré à 2h. Ton test échoue, non pas parce que le serveur a cassé, mais parce que l'auth a lâché. Gère le refresh de token dans le setup de tes tests ou tu vas chasser des pannes fantômes pendant des heures.

Dérive de la liste d'outils. Le serveur ajoute un outil, en supprime un, renomme un paramètre — aucune notification, aucun version bump. Teste la découverte d'outils dans tes health checks. Compare la liste d'outils à un snapshot de référence. Alerte sur les changements.

Maintenant tu es dangereux

Tu sais tester les serveurs MCP. Pas parce que le protocole t'aide — la roadmap du 19 avril confirme qu'il ne priorisera pas ça de sitôt — mais parce que la validation JSON Schema, le chaos engineering et le monitoring de santé sont tous des problèmes résolus. Tu peux les boulonner sur la surface non testée de MCP avec du Python standard et un cron job.

Le setup est moche. La maintenance est manuelle. Toute la stack devra être reconstruite quand la spec ajoutera enfin des primitives de test — si elle le fait un jour.

Mais ton agent a maintenant des dépendances testées au lieu de prières. C'est la différence entre "ça marchait dans la démo" et "ça marche en production". L'un des deux te paye ton salaire. L'autre te vaut un message Slack à 2h du mat' de quelqu'un qui a fait confiance à ton agent pour quelque chose d'important. 😼

Roadmap MCP 2026 (19 avril 2026)Testomat.io — Outils de test de serveurs MCP