Problèmes d’intégration d’un serveur MCP : protocole, transport, droits et schema

Un guide de diagnostic par couches pour un serveur MCP : version du protocole, transport, exécution, autorisation, schema des tools et test Inspector sûr.

Si un serveur MCP ne se connecte pas ou qu’une tool call échoue, ne modifiez pas simultanément la configuration du client, le code du serveur et les droits. Identifiez d’abord la couche en cause. Suivez cet ordre : version du protocole → transport → démarrage et environnement → permissions/auth → inputSchema → un appel read-only.

Au 23 août 2026, la version courante et vérifiée de la spécification MCP est 2026-07-28. Elle n’impose plus l’ancien handshake par initialize : le client peut utiliser server/discover, tandis que la version du protocole, les informations du client et ses capabilities accompagnent les requêtes dans _meta. Les implémentations legacy 2025-11-25 et antérieures suivent une autre séquence : initialize, réponse du serveur, puis notifications/initialized. Ne mélangez jamais ces deux époques dans un même échange.

Séparez immédiatement MCP de l’API du modèle. MCP relie un client à des tools et à du contexte ; l’appel du modèle peut emprunter une autre route et utiliser d’autres credentials. Isolez la couche modèle avec le guide BetterToken : votre propre API Key et l’interface OpenAI-compatible ou Anthropic-compatible choisie offrent une route distincte et vérifiable, ce qui évite de chercher une erreur du modèle dans MCP. Cette Key n’est pas un credential du serveur MCP, et BetterToken n’est ni un MCP host ni un MCP transport.

Classer rapidement la panne

Avant de lancer Inspector, notez un symptôme et le dernier point confirmé :

  • le processus ne démarre pas ;
  • le processus tourne, mais le client ne reçoit aucun JSON-RPC ;
  • le transport répond, mais la version ou les capabilities ne concordent pas ;
  • le serveur renvoie 401 ou 403 ;
  • tools/list fonctionne, mais la tool attendue est absente ;
  • la tool est visible, mais tools/call rejette ses arguments ;
  • l’appel aboutit, mais son résultat ne peut pas être vérifié.

Ne consignez ni API Key, ni bearer token, ni cookie, ni prompt complet, ni contenu de fichiers privés. Pour la corrélation, l’heure, le nom du serveur, la méthode, l’id JSON-RPC, le code d’erreur et un message nettoyé suffisent.

1. Identifier l’époque du protocole

Déterminez la version prise en charge par le client, le server et le SDK. Le message Connected confirme le transport et une partie du discovery, mais ne prouve pas à lui seul l’accord sur 2026-07-28.

Pour une implémentation moderne, vérifiez trois éléments :

  1. Le SDK ou ses release notes annoncent explicitement la prise en charge de 2026-07-28.
  2. La trace contient server/discover ou un autre parcours de discovery prévu par le SDK.
  3. Les requêtes contiennent un _meta correct avec la version, les informations du client et les capabilities.

Ne recopiez pas manuellement la forme de _meta depuis un autre SDK : le wire format exact doit être produit par un client compatible ou par le SDK officiel. Si le server attend initialize alors que le client envoie des requêtes autonomes 2026-07-28, il s’agit d’une incompatibilité d’époque, pas d’une erreur de tool schema.

initialize legacy : uniquement pour 2025-11-25 et avant

Voici une requête legacy minimale. Ne l’ajoutez pas à un flow moderne 2026-07-28 « par précaution ».

{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": { "name": "mcp-diagnostic-client", "version": "1.0.0" } } }

Après une réponse réussie, le legacy client envoie notifications/initialized. Si cet échange s’interrompt, comparez d’abord les versions et la liste des capabilities. Il est encore trop tôt pour passer à tools/list.

2. Tester le transport séparément de la sémantique MCP

MCP définit les méthodes et les données ; le transport gère le démarrage, le framing, la livraison et l’annulation des requêtes. Passer de stdio à HTTP ne réparera pas un inputSchema incorrect.

stdio

Avec stdio, le client lance le server comme processus enfant. Les messages transitent par stdin et stdout sous forme de documents JSON-RPC UTF-8 séparés par un saut de ligne.

Vérifiez que :

  1. command existe et s’exécute sous le même utilisateur.
  2. Les arguments sont transmis comme éléments distincts, sans dépendre d’aliases shell.
  3. Le répertoire de travail contient les fichiers requis, ou les chemins sont absolus.
  4. Les variables nécessaires sont réellement accessibles au processus enfant.
  5. stdout ne contient ni bannière, ni ligne de debug, ni stack trace ; les logs vont dans stderr.

Un seul console.log() accidentel dans stdout peut casser le framing avant que le client ne voie une réponse JSON-RPC.

Streamable HTTP

Avec Streamable HTTP, le client envoie des messages POST vers un endpoint MCP unique. La réponse peut être du JSON ordinaire ou du SSE limité à la requête. Vérifiez l’URL exacte, la méthode HTTP, Content-Type, TLS, les redirects, le proxy et le mode d’authentification.

Effectuez le test de transport sur loopback ou dans un environnement isolé. Ne scannez pas un endpoint public de production sans autorisation. Si POST renvoie une page HTML de connexion, un 301/302 vers un autre host ou une réponse de reverse proxy, vous n’avez pas encore atteint MCP.

3. Reproduire le démarrage dans le même environnement

Pour stdio, exécutez d’abord la commande du server directement depuis le même répertoire et sous le même utilisateur que le MCP client. Un lancement depuis l’IDE n’est pas équivalent : PATH, cwd, runtime et permissions peuvent différer.

Vérifiez :

node --version pwd node ./dist/server.js

pwd ne révèle pas un secret à lui seul, mais ne publiez pas le chemin s’il contient un nom d’utilisateur ou un projet privé. La commande du server doit soit attendre du JSON-RPC sur stdin, soit quitter avec une erreur claire dans stderr. Une sortie immédiate sans message indique généralement un mauvais entrypoint, une dépendance absente ou une erreur de démarrage interceptée sans log.

La documentation officielle d’Inspector CLI, vérifiée le 23 août 2026, exige Node.js 22.19.0 ou une version ultérieure. Si votre version est inférieure, arrêtez-vous et changez de runtime avant de poursuivre.

4. Séparer permissions et authentication

Le code de réponse guide la vérification suivante :

  • 401 Unauthorized : le credential est absent, expiré ou refusé ;
  • 403 Forbidden : l’identity est reconnue, mais ne possède pas le permission ou le scope requis ;
  • 404 : il s’agit souvent d’un mauvais endpoint ou d’une mauvaise route, et non d’un manque de droits ;
  • timeout : le server, le proxy ou la tool n’a pas terminé à temps ; cela ne prouve pas une erreur d’auth.

Ne désactivez pas les permissions pour un smoke test. Créez une identity de test distincte avec le scope minimal et choisissez une tool read-only sans effet externe. Le client doit permettre à un humain de refuser l’appel ; les annotations de la tool sont des données non fiables et ne remplacent pas la policy.

Dans les logs, conservez la décision d’auth (allowed/denied), le nom du scope et le correlation ID. Supprimez ou masquez le credential lui-même, le header Authorization et les cookies.

5. Valider la capability et inputSchema

Le server doit déclarer la capability tools avant de traiter tools/list. Chaque tool doit avoir un nom unique et un objet JSON Schema valide dans inputSchema. Les arguments de tools/call doivent respecter cette schema.

Déclaration minimale d’une tool read-only :

{ "name": "echo", "description": "Renvoie le texte fourni sans modification", "inputSchema": { "type": "object", "properties": { "text": { "type": "string" } }, "required": ["text"], "additionalProperties": false } }

Les erreurs fréquentes sont simples : absence du type: object racine, champ obligatoire absent de properties, envoi d’un nombre au lieu d’une chaîne par le client, casse différente dans le nom d’un argument, ou deux tools annoncées avec le même nom.

Une fois la version et le transport alignés, testez la méthode avec ce payload JSON-RPC :

{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }

Appelez ensuite une seule tool :

{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "echo", "arguments": { "text": "MCP_OK_2026" } } }

Ces extraits montrent les payloads des méthodes, pas le bootstrap complet de la connexion. Dans un flow 2026-07-28, un client compatible ajoute les métadonnées de requête requises dans _meta ; dans un flow legacy, initialize vient d’abord. N’envoyez pas manuellement ces JSON vers un endpoint de production sans autorisation.

6. Exécuter un test sûr avec MCP Inspector CLI

Installez d’abord Inspector comme dépendance du projet verrouillée par un lockfile fiable. L’option --no-install ci-dessous évite de télécharger une version courante arbitraire pendant le diagnostic.

Lister les tools d’un server stdio local :

npx --no-install @modelcontextprotocol/inspector --cli node ./dist/server.js --method tools/list

Appeler echo une fois :

npx --no-install @modelcontextprotocol/inspector --cli node ./dist/server.js \ --method tools/call \ --tool-name echo \ --tool-arg text=MCP_OK_2026

Pour un endpoint Streamable HTTP de test sur loopback :

npx --no-install @modelcontextprotocol/inspector --cli \ http://127.0.0.1:3000/mcp \ --transport http \ --method tools/list

Ne placez aucun token dans l’historique shell, l’URL ou l’article. Si l’endpoint exige une auth, configurez le credential avec le mécanisme normal d’Inspector dans l’environnement local, ou arrêtez le test et demandez une identity de test au propriétaire du server. Les commandes ci-dessus ne contiennent volontairement aucune Key réelle.

Symptôme → vérification → correction

SymptômeÀ vérifier d’abordCorrection minimale
spawn ENOENT ou processus introuvableChemin absolu de command, runtime et PATH du processus clientPointer vers un executable existant ou corriger l’environnement de lancement
Le processus quitte immédiatementcwd, entrypoint, dépendances et erreur dans stderrLancer depuis le bon répertoire et renvoyer un non-zero exit clair
Le client signale une JSON parse errorSortie parasite dans stdout, UTF-8 et saut de ligneRéserver stdout au JSON-RPC et envoyer les logs dans stderr
HTTP renvoie du HTML ou un redirectURL MCP, proxy, TLS et route POSTUtiliser un endpoint MCP correct et corriger la règle proxy
Erreur de version avant tools/listÉpoque du protocole et support du SDKMettre à jour le côté incompatible ou conserver explicitement le chemin legacy ; ne pas mélanger les handshakes
401 UnauthorizedPrésence et expiration du credentialObtenir un credential de test séparé par le processus approuvé
403 ForbiddenScope, resource policy et identityAccorder uniquement le scope requis à l’identity de test
tools/list → erreur de méthode/capabilityDéclaration de la capability toolsCorriger la déclaration avant d’enregistrer les tools
Tool absente de la listeNom unique et enregistrement effectifEnregistrer une tool puis redémarrer le server
tools/call rejette les argumentsinputSchema, types, required et casse des nomsAligner les arguments sur la schema sans l’assouplir en objet arbitraire
L’appel reste bloquéTimeout, cancellation et dépendance externe de la toolRemplacer le test par un echo local read-only, puis tester la dépendance séparément

Critères d’acceptation

L’intégration réussit l’acceptation minimale lorsque les cinq conditions sont réunies :

  1. Les logs ou la telemetry montrent la version de protocole négociée attendue.
  2. Le transport préserve le framing : stdio ne contient aucun stdout parasite et HTTP répond depuis l’endpoint MCP.
  3. tools/list renvoie une tool attendue avec un inputSchema valide.
  4. tools/call est réellement invoqué avec text=MCP_OK_2026 et renvoie MCP_OK_2026 sans modification.
  5. Le test n’a pas désactivé les permissions, exposé de credentials ni provoqué d’effet externe.

Voir MCP_OK_2026 dans une réponse du modèle ne suffit pas. Il faut une tool call enregistrée avec l’id JSON-RPC correspondant, ou un relevé Inspector accompagné d’un log server nettoyé.

Conditions d’arrêt

Arrêtez le diagnostic et ne passez pas à la couche suivante si :

  • l’époque du protocole prise en charge par l’une des parties est inconnue ;
  • Inspector propose d’installer une version non verrouillée du package sans vérification ;
  • le test exige un credential de production, la désactivation de l’auth ou un scope plus large ;
  • la seule tool disponible écrit dans une base, envoie un message, modifie un fichier ou exécute une commande ;
  • l’endpoint HTTP appartient à un tiers et l’autorisation de le tester n’est pas confirmée ;
  • une Key, un token, un cookie, des données personnelles ou le contenu d’une ressource privée apparaît dans les logs ;
  • tools/list est instable ou renvoie des schemas différents entre plusieurs exécutions ;
  • le server tombe avant de produire une réponse JSON-RPC valide.

Dans ces cas, conservez le symptôme nettoyé, les versions client/server/SDK, le transport, le correlation ID et un fragment minimal de l’erreur. Cela suffit pour transmettre le problème au responsable de la bonne couche sans diffuser des droits ou des secrets inutiles.

Sources

La version de la spécification, les détails du transport et la version minimale de Node.js ont été vérifiés le 23 août 2026. Consultez de nouveau la documentation officielle avant de répéter le diagnostic après une mise à jour du client, du server, du SDK ou d’Inspector.

Prêt à optimiser votre workflow LLM ?

Connectez vos modèles via une API unique, gérez les clés et maîtrisez vos dépenses d’IA.