Dans la spécification MCP, un outil peut renvoyer à la fois structuredContent et un bloc content textuel pour préserver la compatibilité avec les clients qui ne comprennent pas encore les résultats structurés (spécification officielle des outils MCP). Cette contrainte résume le vrai sujet : un AI Agent ne dépend pas du JSON pour raisonner, mais du JSON et du Schema pour faire circuler des données vérifiables entre plusieurs responsabilités.
Symptôme : le JSON se parse, mais l’appel échoue, le résultat revient au mauvais tour ou le client MCP ne sait pas quoi consommer.
Solution la plus rapide : vérifiez séparément la syntaxe, le Schema, le choix de l’outil, les droits, l’identifiant d’appel et la validation du résultat. Ne corrigez pas toutes les erreurs en ajoutant des contraintes de type.
Cette méthode est plus fiable que l’examen du seul message final, car elle permet de déterminer si l’erreur vient du modèle, de l’orchestrateur, de l’exécuteur, du protocole ou de votre logique métier.
Le public concerné par cette analyse
Cet article s’adresse d’abord aux développeurs d’Agents qui doivent comprendre le rôle exact du JSON dans une chaîne de Tool Calling ou de Function Calling.
Il concerne également les ingénieurs chargés de diagnostiquer des erreurs de paramètres, d’état, d’autorisation ou de résultats MCP, ainsi que les architectes qui veulent normaliser les événements, les versions de Schema et les traces d’exécution.
Le cas d’usage est particulièrement concret pour les Agents qui contrôlent des outils macOS : génération audio, rendu vidéo, traitement d’images, export depuis un logiciel de design ou exécution de scripts sur un Mac distant. Dans ces scénarios, une erreur de transport peut ressembler à une panne applicative alors que l’outil n’a jamais été exécuté.
Le JSON comme enveloppe de données
Non. Le JSON est une enveloppe de données et de contrôle.
Le modèle interprète la demande et peut décider qu’un outil est nécessaire. Il génère alors une structure contenant, selon l’API utilisée, un nom d’outil, des arguments et un identifiant. L’orchestrateur extrait cette intention, vérifie sa conformité, applique les règles de sécurité puis appelle réellement le service concerné. Enfin, le résultat est reformaté et réinjecté dans le contexte du modèle.
La chaîne ressemble donc à ceci :
Demande utilisateur
↓
Décision du modèle
↓
Nom de l’outil + arguments JSON + identifiant d’appel
↓
Validation Schema et politique
↓
Exécution API ou outil local
↓
Résultat structuré + texte de compatibilité
↓
Association avec l’appel initial
↓
Validation métier
↓
Réponse finale
Le guide officiel du Function Calling décrit la même séparation : le modèle détermine l’action et les paramètres, mais le code de l’application reste responsable de l’exécution. La documentation de l’API OpenAI précise également que les arguments générés doivent être validés avant l’appel, car ils peuvent être invalides ou contenir des paramètres non prévus (référence officielle des outils et appels de fonction).
| Couche | Donnée JSON typique | Responsable principal | Échec caractéristique |
|---|---|---|---|
| Modèle | Nom, arguments, choix d’outil | Modèle et prompt | Outil inadapté ou argument mal interprété |
| Orchestrateur | Schema, historique, appel ID | Votre application | Mauvaise validation ou état perdu |
| Exécution | Requête API, identité, ressource | Exécuteur | Refus, délai, ressource indisponible |
| Protocole | content, structuredContent, isError |
Client et serveur MCP | Résultat incompatible ou mal associé |
| Métier | Statut, règle, résultat attendu | Votre domaine applicatif | Réponse techniquement valide mais inutilisable |
Le point important est la responsabilité. Le modèle peut produire une proposition d’appel. Il ne doit pas décider seul si une opération destructive est autorisée, ni conclure qu’un résultat vide signifie « succès ».
Premier incident : un JSON analysable mais un Schema non conforme
Considérez cet appel :
{
"name": "export_video",
"arguments": {
"format": "mp4",
"resolution": "4K",
"include_audio": "yes"
}
}
Le document peut être syntaxiquement valide. Pourtant, il peut violer le Schema si include_audio doit être un booléen, si resolution doit appartenir à une liste fermée ou si un champ obligatoire comme project_id manque.
Il faut distinguer trois niveaux :
- Syntaxe JSON : les guillemets, accolades, virgules et types primitifs sont acceptables.
- Conformité au Schema : les propriétés requises, les types, les valeurs autorisées et les contraintes sont respectés.
- Validité métier : le projet existe, le format est disponible, l’utilisateur peut exporter et la ressource est dans un état exploitable.
Un système robuste journalise ces trois verdicts séparément. Sinon, un message comme invalid_request masque la cause réelle et pousse l’Agent à modifier le mauvais champ.
Les implémentations ne supportent d’ailleurs pas nécessairement le même sous-ensemble de JSON Schema. Le mode strict des outils OpenAI utilise un sous-ensemble documenté, tandis que les déclarations de fonction de Gemini et les input_schema d’Anthropic suivent leurs propres contraintes d’API. La documentation Anthropic demande notamment un Schema d’entrée pour chaque outil et un identifiant associé à chaque bloc tool_use (documentation officielle d’implémentation du Tool Use).
Attention : rendre un Schema plus strict ne corrigera pas un mauvais choix d’outil. Si le modèle appelle
delete_assetau lieu deinspect_asset, le type du champasset_idn’est pas le problème principal.
Pour éviter les faux diagnostics, conservez dans chaque événement :
{
"schema_name": "export-video-input",
"schema_version": "2026-08-01",
"syntax_valid": true,
"schema_valid": false,
"business_valid": false
}
La version du Schema doit être explicite. Modifier silencieusement un champ peut rendre illisibles les erreurs qui apparaissent seulement avec certaines conversations ou certains clients.
Deuxième incident : les paramètres sont corrects, mais le mauvais outil est choisi
Un appel peut respecter parfaitement son Schema et rester incorrect. Le problème se situe alors dans la sélection.
Imaginez deux outils :
preview_audio: génère un aperçu sans modifier le projet ;publish_audio: remplace le fichier diffusé dans un environnement de production.
Si leurs descriptions sont vagues, si leurs noms sont proches ou si les deux outils sont toujours exposés au modèle, celui-ci peut sélectionner publish_audio pour répondre à une demande de test.
La correction doit porter sur la décision, pas seulement sur les types.
Améliorations à appliquer
- Donnez à chaque outil un nom distinct et orienté action.
- Décrivez ce que l’outil fait, mais aussi ce qu’il ne fait pas.
- Indiquez clairement les effets de bord : lecture seule, modification, suppression ou publication.
- Réduisez la liste des outils visibles selon l’étape de la tâche.
- Ajoutez au contexte le but opérationnel : aperçu, validation, production ou restauration.
- Exigez une confirmation humaine avant les opérations irréversibles.
- Utilisez un mode de sélection forcé lorsque la branche du workflow est déjà connue.
Les annotations MCP comme readOnlyHint ou destructiveHint peuvent aider à présenter l’intention d’un outil, mais elles ne remplacent pas une politique de sécurité. La spécification rappelle que ces annotations doivent être considérées comme non fiables lorsqu’elles ne proviennent pas d’un serveur de confiance (définition officielle des outils MCP).
Pour les workflows audio et vidéo, séparez notamment l’outil qui analyse un fichier de celui qui l’encode. Pour le design, ne mélangez pas la lecture des calques avec l’outil qui écrase le document. Cette séparation réduit les erreurs de sélection et facilite l’audit.
Troisième incident : l’appel est conforme, mais l’API refuse l’exécution
À ce stade, le JSON peut être irréprochable. L’API peut néanmoins refuser la demande pour plusieurs raisons :
- le jeton est expiré ou absent ;
- l’identité authentifiée n’a pas le rôle requis ;
- le projet ou le fichier n’existe plus ;
- la ressource est verrouillée par une autre tâche ;
- le quota ou la limite de débit est atteint ;
- le réseau est indisponible ;
- une règle métier interdit l’opération dans cet état.
Le défaut fréquent consiste à renvoyer au modèle un simple texte comme « request failed ». L’Agent doit alors deviner si le problème vient d’un droit, d’un paramètre, d’un délai ou d’une ressource absente. Il peut modifier inutilement la requête, répéter une action ou entrer dans une boucle.
Retournez plutôt une erreur structurée :
{
"ok": false,
"error": {
"class": "authorization",
"code": "PROJECT_EXPORT_FORBIDDEN",
"retryable": false,
"user_action": "request_export_permission"
}
}
L’exécuteur doit produire le diagnostic, car lui seul connaît le résultat réel de l’authentification et de l’appel API. Le modèle peut ensuite expliquer l’erreur ou choisir une alternative, mais il ne doit pas inventer la cause.
Dans vos journaux, distinguez au minimum :
- la décision d’autoriser ou de refuser ;
- la ressource ciblée ;
- l’identité utilisée ;
- le code de réponse du service ;
- le caractère réessayable ;
- l’effet de bord déjà produit ou non.
Cette séparation est indispensable pour les Agents qui lancent une commande sur un Mac distant. Un timeout réseau ne prouve pas que le script n’a pas démarré. Sans identifiant d’exécution et état de la machine, un second appel peut déclencher deux rendus ou deux exports.
Pourquoi un appel ID perdu casse-t-il toute la boucle ?
L’identifiant d’appel relie une intention à son résultat. Dans une exécution séquentielle, sa disparition provoque déjà une ambiguïté. Dans une exécution parallèle, elle peut rendre l’association impossible.
Exemple :
[
{
"id": "call_audio_17",
"name": "inspect_audio",
"arguments": { "file": "voix.wav" }
},
{
"id": "call_video_18",
"name": "inspect_video",
"arguments": { "file": "plan.mp4" }
}
]
Si les résultats reviennent sans leurs identifiants, l’orchestrateur peut attribuer les caractéristiques audio au fichier vidéo. Le JSON reste valide, mais l’état logique est faux.
La documentation de Tool Use d’Anthropic impose que tool_result référence le tool_use_id correspondant et que le résultat soit placé immédiatement après le bloc d’utilisation de l’outil dans l’historique. Cette règle montre que l’ordre des messages et la relation entre les identifiants font partie du protocole, pas d’un simple détail d’implémentation.
Conservez donc dans chaque événement :
{
"trace_id": "trace-...",
"interaction_id": "turn-...",
"tool_call_id": "call_audio_17",
"parent_call_id": null,
"parallel_group": "group-04",
"sequence": 3
}
Vous n’avez pas besoin d’utiliser exactement ces noms dans toutes les plateformes. Vous devez en revanche conserver la même sémantique dans votre modèle interne, puis effectuer une conversion explicite vers les champs attendus par chaque API. Ne mélangez pas tool_call_id, tool_use_id, identifiant JSON-RPC et identifiant métier.
Comment MCP transporte-t-il un résultat exploitable ?
Dans MCP, un appel d’outil utilise tools/call et transmet notamment un nom ainsi que des arguments. Le résultat peut contenir content, structuredContent et isError. Lorsqu’un outputSchema est fourni, le serveur doit produire une structure conforme et le client devrait la valider (référence officielle du Schema MCP).
Le traitement recommandé est le suivant :
- Vérifier que la réponse respecte le format de transport attendu.
- Vérifier la présence du résultat associé à l’appel demandé.
- Examiner
isErroravant de lire les données métier. - Valider
structuredContentcontreoutputSchema, s’il existe. - Conserver le bloc texte pour les clients qui ne gèrent pas les données structurées.
- Convertir le résultat vers un contrat interne stable.
- Refuser explicitement un résultat incomplet au lieu de le transmettre comme un succès.
Un piège apparaît lorsque le client suppose que structuredContent est toujours un objet avec des propriétés fixes. Les évolutions récentes de la spécification MCP permettent d’envisager des sorties plus variées, tandis que la compatibilité avec d’anciens clients repose encore sur le contenu textuel sérialisé (proposition officielle sur JSON Schema 2020-12 et les résultats structurés).
Expérience d’architecture : gardez deux représentations du résultat : une forme structurée pour votre programme et une forme textuelle compréhensible par le modèle et les clients moins complets. Elles doivent provenir de la même donnée validée, et non de deux transformations indépendantes.
Un résultat MCP peut donc échouer à plusieurs endroits : champ absent, Schema non respecté, isError ignoré, identifiant incorrect ou texte de compatibilité non fourni. Lire uniquement content[0].text n’est pas une stratégie de validation.
La checklist de diagnostic à utiliser avant de modifier le prompt
Utilisez cette liste dans l’ordre. Elle permet de corriger la couche responsable avant d’ajouter des instructions supplémentaires au modèle.
- [ ] Enregistrer le message brut reçu du modèle avant toute transformation.
- [ ] Vérifier que le JSON est syntaxiquement analysable.
- [ ] Valider les arguments contre la version exacte du Schema utilisée.
- [ ] Confirmer que le nom de l’outil existe dans la liste exposée à cette étape.
- [ ] Comparer la description de l’outil avec l’objectif réel de la demande.
- [ ] Vérifier l’identité, le rôle et les permissions de l’exécuteur.
- [ ] Contrôler l’état de la ressource avant l’appel API.
- [ ] Enregistrer le code d’erreur et préciser s’il est réessayable.
- [ ] Conserver l’identifiant d’appel dans la requête, le résultat et l’historique.
- [ ] Tester les appels parallèles avec des résultats volontairement inversés.
- [ ] Vérifier que le résultat MCP porte le bon identifiant.
- [ ] Valider
structuredContentcontreoutputSchema. - [ ] Préserver un contenu textuel de compatibilité.
- [ ] Effectuer la validation métier après la validation du protocole.
- [ ] Relier l’appel à l’état de l’environnement distant : version, processus, fichier et statut réseau.
Questions fréquentes sur le flux JSON des AI Agent
Les cinq cas ci-dessous résument les recherches opérationnelles les plus courantes : pourquoi le JSON est utilisé, qui exécute un Function Calling, comment MCP renvoie ses résultats, pourquoi un JSON légal échoue et quelles conséquences entraîne la perte d’un identifiant.
Pour aller plus loin, vous pouvez consulter notre présentation de Kvmzen afin de comprendre l’environnement proposé pour les tests distants, puis vérifier les conditions d’utilisation avant d’intégrer un poste de test dans votre processus de diagnostic.
Construire un journal d’événements qui permet vraiment de trancher
Un journal utile ne se contente pas d’écrire « Agent failed ». Il doit permettre de reconstruire la chaîne sans exposer les secrets.
Enregistrez au minimum :
- la demande ou son empreinte anonymisée ;
- la version du modèle et de l’orchestrateur ;
- la liste des outils visibles ;
- le nom et la version du Schema ;
- les arguments après masquage des données sensibles ;
- le résultat de la validation syntaxique et structurelle ;
- l’identifiant de trace et l’identifiant d’appel ;
- la décision d’autorisation ;
- le début, la fin et le statut de l’exécution ;
- la réponse API ou MCP normalisée ;
- le résultat de la validation métier ;
- l’état de l’environnement utilisé.
Pour les tâches longues, ajoutez les transitions d’état : queued, running, completed, failed, cancelled et unknown. L’état unknown est important après un timeout : il empêche votre Agent de supposer qu’une action n’a pas eu lieu.
Vous pouvez également créer un contrat interne unique, puis écrire des adaptateurs pour chaque fournisseur. Cette approche évite de propager directement dans toute votre application les différences entre les champs de Tool Calling, de Function Calling et de MCP. Les noms externes restent spécifiques à chaque protocole ; votre observabilité, elle, conserve une structure homogène.
Le choix d’un environnement de reproduction
Oui, surtout lorsque l’Agent dépend d’outils macOS, de fichiers locaux, de logiciels audio ou vidéo et de scripts qui ne sont pas disponibles sur un serveur Linux standard.
Une machine de développement personnelle rend souvent la reproduction incomplète : dépendances différentes, session graphique déjà ouverte, fichiers présents uniquement en local, permissions héritées et état impossible à partager avec l’équipe. Un environnement distant isolé permet de conserver une version précise des outils, de répéter le même appel et de rattacher les journaux JSON à l’état réel de la machine.
Cela ne signifie pas que la location convient à tous les cas. Pour une charge lourde permanente, une machine achetée peut être plus cohérente. Si votre Agent exige un périphérique physique spécifique, un accès matériel direct ou une présence locale permanente, un Mac distant ne supprimera pas cette contrainte. En revanche, pour une campagne de reproduction, un test d’intégration ou une validation croisée entre API, l’environnement temporaire est souvent plus simple à remettre à zéro.
Vous pouvez examiner les options de location de Mac mini pour les tests distants lorsque votre chaîne d’appel doit être reproduite sur macOS plutôt que simulée dans un environnement différent.
Un environnement local partagé présente généralement trois défauts : il conserve des dépendances invisibles, il mélange les données de plusieurs essais et il rend difficile l’attribution exacte d’un échec à une version de l’outil. Une infrastructure cloud générique peut, de son côté, ne pas fournir la même interface graphique, les mêmes frameworks macOS ou les mêmes logiciels de création. Pour un diagnostic centré sur les appels JSON, les droits et l’état des outils, la possibilité de repartir d’un Mac isolé et documenté peut donc apporter une preuve plus solide qu’une simple nouvelle tentative dans votre poste habituel.
La bonne décision est de louer lorsque vous avez besoin d’un environnement temporaire, reproductible et accessible à plusieurs intervenants. Achetez plutôt lorsque la charge est continue, que l’amortissement est prévisible et que vous contrôlez déjà l’ensemble des dépendances. Dans les deux cas, la règle reste la même : sans identifiant de trace, version de Schema et état d’exécution, vous ne pourrez pas expliquer précisément pourquoi l’Agent a échoué.
