MCP Fusion/Protocol and runtime/Format du wire

Format du wire

Demandez à l’IA à propos de Vinkius

Les octets exacts qu’un Presenter envoie : les blocs de texte ordonnés, le XML de ui_passthrough, les règles de domaine, les suggestions d’action, la guillotine _select, les embeds et TOON.

Un appel de tool avec un Presenter ne renvoie pas JSON.stringify. Il renvoie une séquence ordonnée de blocs de texte, chacun avec une forme stable que l’agent (et toute UI cliente) peut analyser. Cette page est la spécification de ce format.

La séquence de blocs

ResponseBuilder.build() émet jusqu’à six blocs, toujours dans cet ordre :

#BlocForme
1datale payload masqué et sérialisé, en texte JSON
2UI blocksun <ui_passthrough> par bloc
3embedsdes blocs bruts de Presenters enfants
4directivesliste de <llm_directives>
5rulesliste de <domain_rules>
6suggestionsliste de <action_suggestions>

Voici à quoi ressemble un bloc UI sur le wire :

xml
<ui_passthrough type="echarts" title="Revenue by month" width="full" priority="1">
  echarts-fenced-content: the chart config as formatted JSON
</ui_passthrough>

Les attributs type, title, width (full, half, third) et priority indiquent au renderer client ce qu’est le contenu encadré. ui.table et ui.list sont du markdown ; ui.json est du json encadré ; ui.codeBlock garde son language tag. Il n’y a pas de type de bloc table natif : l’encadrement est le contrat, et fence() est le seul endroit qui change si MCP standardise les blocs UI.

Les blocs de règles et de suggestions sont de simples listes à puces :

xml
<domain_rules>
- amount_cents is in CENTS. Divide by 100.
</domain_rules>
<action_suggestions>
- billing.remind: Send a payment reminder
</action_suggestions>

structuredContent est un système à part

Le champ structuredContent de MCP 2.0 n’est pas produit par les Presenters. Pour l’émettre, utilisez successStructured(data), qui envoie le bloc de texte JSON pour les clients MCP 1.0 plus le champ lisible par machine structuredContent, et déclarez la forme sur le wire avec .withOutputSchema(schema). Les Presenters possèdent le texte pour humains et LLMs ; successStructured possède le canal objet parsé. Ne confondez pas les deux.

_select : la guillotine de champs

.enableSelect() ajoute un paramètre de type tableau _select à chaque action du tool, dont l’enum est l’union des clés racine du schéma de toutes les actions. Quand l’agent passe _select, le bloc data est filtré sur ces clés. Tout le reste (blocs UI, règles, suggestions) tourne toujours sur l’objet complet, si bien qu’un client qui affiche la réponse à un humain voit l’ensemble pendant que le contexte du modèle ne transporte que ce qui a été demandé. C’est le gain de tokens le moins cher sur les modèles larges, et c’est opt-in par builder.

embed : composition relationnelle

.embed('lines', InvoiceLinesPresenter) lit data.lines via un Presenter enfant et fusionne les blocs et les règles de l’enfant dans la réponse du parent. Le parent possède une entité ; un graphe d’entités est un arbre de Presenters, chacun avec son propre schéma, sa propre rédaction et ses propres limites.

TOON : des descriptions avec moitié moins de tokens

.toonDescription() et toonSuccess(data) utilisent le format TOON délimité par des pipes pour les collections uniformes : les clés répétées fusionnent en une seule ligne d’en-tête et le tableau devient une table de valeurs. Pour une liste de dix mille éléments de forme identique, le nombre de tokens est à peu près divisé par deux. Le format est généré à partir du schéma de l’action, donc l’agent voit action|desc|required comme la couche une et les données comme la couche deux.

La porte dérobée des tests

Chaque réponse d’un Presenter porte un symbol non énumérable contenant { data, systemRules, uiBlocks }. @mcpfusion/testing y lit la vue structurée au lieu de re-analyser le XML, et c’est ainsi que result.data, result.systemRules et result.uiBlocks dans Testing sont exacts. JSON.stringify ne voit jamais le symbol.

Règles d’enveloppement implicites

La valeur de retour d’un handler est interprétée avec une seule règle : les objets ToolResponse marqués passent tels quels, tout le reste est enveloppé comme success(data). Un retour null ou undefined devient le texte OK. C’est pourquoi vous ne construisez jamais à la main des formes { content: [...] }, et pourquoi ajouter un Presenter ne demande jamais de toucher au handler.

Prochaines étapes