ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Tests et débogage des serveurs MCP

Tester les serveurs MCP avec l'Inspector, le piège de journalisation stderr qui affecte tout le monde, et un workflow de test pratique en trois étapes.

Planté
mcpdata engineeringtesting

Tester des serveurs MCP nécessite des outils qui parlent le protocole. Vous ne pouvez pas simplement faire curl vers un serveur MCP comme vous testeriez une API REST — la communication est du JSON-RPC sur stdio ou HTTP, avec une négociation de protocole et une découverte de capacités. Le MCP Inspector est l’outil essentiel ici.

MCP Inspector

L’Inspector est une interface de test interactif qui se connecte à votre serveur et vous permet d’exercer chaque capacité :

Terminal window
# Tester un serveur Python
npx @modelcontextprotocol/inspector uv run server.py
# Tester un serveur Node.js
npx @modelcontextprotocol/inspector node build/index.js
# Se connecter à un serveur HTTP distant
npx @modelcontextprotocol/inspector --connect https://mcp.yourcompany.com

L’Inspector lance une interface web (généralement sur http://localhost:5173) où vous pouvez :

  • Voir tous les outils, ressources et prompts enregistrés
  • Appeler des outils avec des arguments personnalisés et voir la réponse
  • Inspecter les messages JSON-RPC bruts qui circulent
  • Déboguer le formatage des réponses et la gestion des erreurs
  • Vérifier que les schémas d’entrée correspondent à ce que vous aviez prévu

Utilisez l’Inspector comme outil de développement principal. Chaque fois que vous ajoutez ou modifiez un outil, vérifiez-le dans l’Inspector avant de tester avec un vrai client IA. L’Inspector vous montre exactement ce que l’IA verra — descriptions, schémas, formats de réponse — sans la surcharge d’une conversation IA complète.

Le piège de journalisation stderr

L’erreur la plus courante lors de la construction de serveurs MCP est d’écrire sur stdout. Le transport stdio utilise stdout pour les messages JSON-RPC. Tout autre output sur stdout corrompt le protocole et casse la communication. Le serveur démarre, le client se connecte, mais rien ne fonctionne.

# MAUVAIS - casse la communication JSON-RPC
print("Debug: processing query") # Va sur stdout !
print(f"Error: {e}") # Aussi sur stdout !

La solution est d’envoyer toute la journalisation sur stderr :

# BON - utilise la journalisation vers stderr
import logging
import sys
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
stream=sys.stderr # Envoi explicite vers stderr
)
logger = logging.getLogger(__name__)
logger.info("Debug: processing query") # Va sur stderr
logger.error(f"Error: {e}") # Aussi stderr

Ou utilisez l’objet Context à l’intérieur des outils pour une journalisation au niveau MCP appropriée :

from mcp.server.fastmcp import Context
@mcp.tool()
async def my_tool(query: str, ctx: Context) -> str:
await ctx.info("Processing query") # Journalisation MCP appropriée
# ...

La journalisation Context est préférable car elle passe par le protocole — le client peut l’afficher dans son interface, filtrer par sévérité et l’inclure dans le contexte de conversation. Le module logging de Python vers stderr est le recours pour le code en dehors des fonctions d’outils (démarrage du serveur, initialisation de module, etc.).

Le pattern de diagnostic : si votre serveur fonctionne quand vous testez la logique métier en isolation mais échoue lorsqu’il est connecté à un client, vérifiez d’abord les instructions print() parasites. Vérifiez également les bibliothèques qui écrivent sur stdout — certains drivers de base de données et clients HTTP ont des modes verbeux qui utilisent stdout par défaut.

Workflow de test en trois étapes

Étape 1 : Tests unitaires de la logique métier

Testez votre logique métier réelle — les requêtes aux bases de données, les appels d’API, le traitement des données — séparément de MCP. C’est du test Python standard :

test_logic.py
def test_query_execution():
result = execute_query("SELECT 1", "test_db")
assert "1" in result
def test_catalog_search():
results = search_tables("orders", tags=["financial"])
assert len(results) > 0
assert results[0]["name"] == "sales.orders"

À cette étape, vous testez que votre code fonctionne, pas qu’il fonctionne via MCP. Moquez les dépendances externes (bases de données, API) comme vous le feriez normalement.

Étape 2 : Tests via MCP avec l’Inspector

Lancez l’Inspector et exercez chaque outil :

Terminal window
npx @modelcontextprotocol/inspector uv run server.py

Vérifiez :

  • Tous les outils apparaissent dans la liste avec les bons noms
  • Les descriptions sont claires et utiles
  • Les schémas d’entrée affichent les bons types, valeurs par défaut et contraintes
  • Les outils s’exécutent sans erreur sur des entrées valides
  • Les outils retournent des messages d’erreur utiles sur des entrées invalides
  • Le formatage des réponses est propre et analysable

Cela détecte les problèmes spécifiques à MCP : sérialisation cassée, annotations de type manquantes, docstrings qui ne se parsent pas correctement, et problèmes de transport.

Étape 3 : Test d’intégration avec un vrai client

Connectez-vous à Claude Desktop ou Claude Code et testez en langage naturel :

Terminal window
# Ajouter à Claude Code
claude mcp add test-server -- uv run server.py
# Démarrer une conversation
claude
> What tools does test-server provide?
> [Test each tool with realistic inputs]

Cette étape révèle des problèmes que ni les tests unitaires ni l’Inspector ne détectent : des descriptions ambiguës qui amènent l’IA à choisir le mauvais outil, des formats de paramètres que l’IA génère incorrectement, des formats de réponse que l’IA interprète mal, et des cas limites dans la façon dont l’IA compose plusieurs appels d’outils.

L’écart entre « fonctionne dans l’Inspector » et « fonctionne avec une IA » est souvent dans les descriptions et la conception du schéma. Si l’IA utilise systématiquement mal un outil, la correction est généralement une meilleure description ou des noms de paramètres plus clairs — pas un changement de code. La note MCP Tool Design Patterns couvre cela en profondeur.

Conseils de débogage

Le serveur ne répond pas : Vérifiez que le serveur démarre sans erreur. Exécutez-le directement (uv run server.py) et regardez la sortie stderr. Si rien n’apparaît, le serveur attend probablement correctement des entrées.

Les outils n’apparaissent pas : Vérifiez que le décorateur @mcp.tool() est présent et que la fonction a une docstring. FastMCP ignore les fonctions sans docstrings.

Incompatibilité de schéma : Si l’Inspector affiche un schéma différent de ce que vous attendez, vérifiez vos annotations de type. FastMCP infère le schéma à partir des types Python — str, int, list[str], Optional[int], modèles Pydantic.

Timeouts : MCP a son propre timeout séparé de celui de votre back-end. Si un outil appelle une API lente, le client MCP peut expirer avant que l’API réponde. Envisagez d’ajouter un rapport de progression via l’objet Context pour les opérations longues.