Au 05/11/2026, la version de protocole MCP 2026-07-28 et la proposition SEP-2106 assouplissent outputSchema pour que les retours d'outil puissent être validés comme tableaux, chaînes, nombres, booléens ou null — pas seulement des objets. Cela n'enlève pas le travail du harness Agent : après tools/call, la valeur dans structuredContent reste du JSON serveur que l'on SHOULD vérifier contre le outputSchema publié dans tools/list. Ce n'est pas le même contrat que les sorties structurées LLM. La racine de inputSchema reste type: object ; unions d'arguments avec oneOf / anyOf. Lien avec events et sécurité à la fin — voir payloadSchema MCP Events, Agents API vs MCP et outils MCP malveillants.
Les agents valident déjà les arguments choisis par le modèle contre inputSchema avant tools/call. Le chemin retour se saute plus facilement : les serveurs envoient souvent une phrase amicale dans content et mettent la charge machine dans structuredContent. Sans outputSchema, champs non déclarés et chaînes type instruction peuvent entrer au tour suivant — même classe de risque que dans la note fake-skills. SEP-2106 légitime les retours non-objet ; il ne rend pas la validation optionnelle.
Ce que SEP-2106 a changé
- Pin protocole : MCP 2.x version
2026-07-28(plugins ChatGPT, connecteurs hébergés et serveurs autonomes s'alignent de plus en plus sur cette révision). - SEP-2106 : Assouplit les contraintes sur
outputSchemapour que la racine JSON Schema puisse décrire tableaux et valeurs JSON primitives, pas seulementtype: object. - Pourquoi ça comptait : les outils liste/recherche renvoient naturellement
[{ ... }, { ... }]; les métriques un nombre ; les flags un booléen. Forcer une enveloppe objet dupliquait des champs et embrouillait les clients qui parseaientstructuredContentdeux fois. - Ce qui n'a pas changé :
inputSchemapour les arguments reste une racine objet. Les clients SHOULD toujours validerstructuredContentquandoutputSchemaest présent. Le texte danscontentreste orienté humain et ne remplace pas du JSON vérifié par schema.
En une ligne. SEP-2106 élargit ce que vous pouvez déclarer pour la sortie d'outil ; le harness valide toujours structuredContent contre cette déclaration avant que le modèle ne le voie.
outputSchema vs texte content
Une définition d'outil dans tools/list porte les deux sens :
inputSchema— arguments que le client envoie danstools/callarguments.outputSchema(optionnel, recommandé) — la valeur JSON que le serveur place dansstructuredContenten cas de succès.
Le résultat tools/call a aussi content : un tableau de parties (souvent type: text) pour affichage ou logs. La spec ne traite pas cette prose comme contrat. Un serveur peut écrire « 3 lignes trouvées » en texte tandis que structuredContent contient les trois lignes. Le harness devrait préférer structuredContent validé pour construire tool_result pour le modèle, et traiter les écarts comme erreurs — ne pas fusionner silencieusement texte et JSON.
# tools/list excerpt — array root outputSchema (SEP-2106)
{
"name": "search_tickets",
"description": "Search tickets by query string.",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string", "minLength": 1 }
},
"required": ["query"],
"additionalProperties": false
},
"outputSchema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"title": { "type": "string" },
"status": { "type": "string", "enum": ["open", "closed"] }
},
"required": ["id", "title", "status"],
"additionalProperties": false
}
}
}
Fixez additionalProperties: false sur les objets item quand vous voulez bloquer des clés extra qui pourraient orienter le hop suivant. Un enum sur status limite le vocabulaire que le modèle pourrait sinon trop faire confiance depuis le texte libre dans content.
structuredContent
En succès, le serveur renvoie un objet résultat. Le champ à valider est structuredContent : une valeur JSON (objet, tableau, chaîne, nombre, booléen ou null) qui doit respecter outputSchema quand l'outil en a annoncé un.
# tools/call result — good structuredContent
{
"content": [
{ "type": "text", "text": "3 tickets matched." }
],
"structuredContent": [
{ "id": "T-1", "title": "Login bug", "status": "open" },
{ "id": "T-2", "title": "Billing", "status": "closed" },
{ "id": "T-9", "title": "API timeout", "status": "open" }
]
}
Comparez avec la sortie structurée côté LLM : les API exposent des schemas sous des noms comme response_format ou output_config.format.schema. Ceux-ci contraignent la génération du modèle. structuredContent est la sortie du serveur d'outil. Un Agent peut utiliser les deux dans une session ; gardez trois validateurs en tête — arguments, JSON outil, JSON modèle — et ne copiez pas un schema d'une couche sur une autre.
Bons vs mauvais payloads contre le schema tableau ci-dessus :
{
"structured_ok": [
{ "id": "T-1", "title": "Login bug", "status": "open" }
],
"structured_bad_extra_key": [
{ "id": "T-1", "title": "Login bug", "status": "open", "run_shell": "rm -rf /" }
],
"structured_bad_wrong_root": {
"items": [{ "id": "T-1", "title": "Login bug", "status": "open" }]
},
"structured_bad_enum": [
{ "id": "T-1", "title": "Login bug", "status": "done" }
]
}
structured_ok correspond à la racine tableau. structured_bad_extra_key échoue à additionalProperties: false. structured_bad_wrong_root est une enveloppe objet — la racine schema est array, pas object. structured_bad_enum viole enum sur status. Aucun de ceux-ci ne devrait atteindre le modèle comme résultat d'outil réussi.
inputSchema et oneOf
SEP-2106 n'assouplit pas inputSchema. Les arguments d'outil restent un objet JSON au fil. Quand le modèle peut appeler le même nom d'outil avec des formes différentes, composez des unions à l'intérieur de la racine objet :
{
"type": "object",
"oneOf": [
{
"properties": {
"mode": { "const": "by_id" },
"ticket_id": { "type": "string" }
},
"required": ["mode", "ticket_id"],
"additionalProperties": false
},
{
"properties": {
"mode": { "const": "search" },
"query": { "type": "string", "minLength": 1 }
},
"required": ["mode", "query"],
"additionalProperties": false
}
]
}
Validez arguments contre ce schema avant le RPC. Validez structuredContent contre outputSchema après le RPC. Inverser les deux schemas est un bug fréquent en portant les enveloppes OpenAI parameters vers MCP — utilisez MCP Tool pour garder les noms de champs droits.
Validation harness
Une boucle Agent minimale devrait traiter les contrôles schema comme des portes à la frontière de confiance entre serveur et modèle :
- Discover : mettre en cache
inputSchemaetoutputSchemade chaque outil depuistools/list(rafraîchir à la reconnexion ou changement de capacités). - Pre-call : valider les arguments produits par le modèle contre
inputSchema; rejeter ou réparer avanttools/call. - Post-call : si
outputSchemaexiste et l'appel n'est pas marqué erreur, validerstructuredContent. En échec, définirisError(ou équivalent) et ne pas sérialiser du JSON invalide dans le transcript comme succès. - Texte : optionnellement faire passer le texte
contentpar les mêmes règles de quoting que pour les chaînes non fiables ; ne jamais préférer le texte au JSON validé quand les deux sont présents. - Sortie modèle : si vous utilisez des réponses structurées du LLM, validez séparément — ne supposez pas qu'elles correspondent à la sortie MCP.
Les harness hébergés (Codex Agents API, Claude avec MCP, plugins ChatGPT) peuvent valider pour vous sur certains chemins ; votre client MCP ou pont local possède encore le contrôle quand vous appelez les serveurs directement. « SHOULD validate » dans la spec est le plancher pour les Agents de production que vous contrôlez.
Essayer dans JSON Toolbox
{
"tool_ref": "search_tickets.outputSchema",
"validate_these": {
"structured_ok": "pass",
"structured_bad_extra_key": "fail",
"structured_bad_wrong_root": "fail",
"structured_bad_enum": "fail"
}
}
- Coller le
outputSchemade l'exemple tableau dans MCP Tool et confirmer que la racine estarray. - Copier chaque échantillon
structured_*dans Validation Schema contre ce schema. - Rédiger ou resserrer des schemas avec Sortie structurée pour la couche LLM — garder séparé du
outputSchemaMCP. - Envelopper les
parametersstyle OpenAI à côté des champs MCP avec Function Calling. - Normaliser les payloads avant diff dans Formatage JSON.
FAQ
Un outputSchema assoupli signifie-t-il que je peux sauter structuredContent ?
Non — SEP-2106 élargit l'expressivité ; les clients SHOULD toujours valider quand outputSchema est déclaré.
La racine outputSchema peut-elle être un tableau après MCP 2026-07-28 ?
Oui — structuredContent peut être un tableau nu correspondant à cette racine.
structuredContent est-il le même schema que la sortie structurée du modèle ?
Non — JSON serveur d'outil vs génération modèle sont des couches différentes.
inputSchema a-t-il changé quand outputSchema a été assoupli ?
Non — la racine arguments reste object ; oneOf / anyOf dedans.
Que faire quand structuredContent échoue outputSchema ?
Faire échouer le résultat d'outil avant le contexte modèle ; ne pas traiter les erreurs schema comme succès.
En résumé
Au 05/11/2026, MCP 2026-07-28 et SEP-2106 permettent à outputSchema de décrire tableaux et primitives — pas seulement objets — pour que structuredContent reflète les vraies données d'outil sans enveloppes maladroites. C'est distinct des sorties structurées LLM et de la prose dans content. inputSchema reste ancré à type: object avec oneOf / anyOf pour les unions. Les harness Agent SHOULD valider les arguments avant tools/call et structuredContent après, en traitant les échecs comme erreurs d'outil. Prochaine étape : prendre un outil MCP avec outputSchema tableau, faire passer les échantillons bons/mauvais par Validation Schema, et câbler le même contrôle dans votre client avant d'append tool_result.