MCP Fusion/Protocol and runtime/Formato do wire

Formato do wire

Pergunte à IA sobre a Vinkius

Os bytes exatos que um Presenter envia: os blocos de texto ordenados, o XML de ui_passthrough, as regras de domínio, as sugestões de ação, a guilhotina _select, os embeds e o TOON.

Uma chamada de tool com um Presenter não retorna JSON.stringify. Ela retorna uma sequência ordenada de blocos de texto, cada um com um formato estável que o agente (e qualquer UI do cliente) consegue interpretar. Esta página é a especificação desse formato.

A sequência de blocos

ResponseBuilder.build() emite até seis blocos, sempre nesta ordem:

#BlocoFormato
1datao payload mascarado e serializado, como texto JSON
2UI blocksum <ui_passthrough> por bloco
3embedsblocos brutos de Presenters filhos
4directiveslista de <llm_directives>
5ruleslista de <domain_rules>
6suggestionslista de <action_suggestions>

Um bloco de UI fica assim no 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>

Os atributos type, title, width (full, half, third) e priority dizem a um renderer de cliente o que é o conteúdo cercado. ui.table e ui.list são markdown; ui.json é json cercado; ui.codeBlock mantém sua tag de linguagem. Não existe um tipo de bloco de tabela nativo: o cercado é o contrato, e fence() é o único lugar que muda se o MCP padronizar os blocos de UI.

Os blocos de regras e sugestões são listas de bullets simples:

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

structuredContent é um sistema à parte

O campo structuredContent do MCP 2.0 não é produzido por Presenters. Para emiti-lo, use successStructured(data), que envia o bloco de texto JSON para clientes MCP 1.0 mais o campo legível por máquina structuredContent, e declare o formato no wire com .withOutputSchema(schema). Os Presenters cuidam do texto para humanos e LLMs; successStructured cuida do canal de objeto parseado. Não confunda os dois.

_select: a guilhotina de campos

.enableSelect() adiciona um parâmetro de array _select a toda ação da tool, cujo enum é a união das chaves raiz do schema de todas as ações. Quando o agente passa _select, o bloco data é filtrado para essas chaves. Todo o resto (blocos de UI, regras, sugestões) continua rodando sobre o objeto completo, então um cliente que renderiza a resposta para um humano vê o panorama inteiro enquanto o contexto do modelo carrega só o que ele pediu. Essa é a economia de tokens mais barata em modelos largos, e é opt-in por builder.

embed: composição relacional

.embed('lines', InvoiceLinesPresenter)data.lines por meio de um Presenter filho e mescla os blocos e as regras do filho na resposta do pai. O pai possui uma entidade; um grafo de entidades é uma árvore de Presenters, cada um com seu próprio schema, mascaramento e limites.

TOON: descrições com metade dos tokens

.toonDescription() e toonSuccess(data) usam o formato TOON delimitado por pipes para coleções uniformes: chaves repetidas colapsam em uma linha de cabeçalho e o array vira uma tabela de valores. Para uma lista de dez mil itens com o mesmo formato, a contagem de tokens cai para cerca da metade. O formato é gerado a partir do schema da ação, então o agente vê action|desc|required como camada um e os dados como camada dois.

A porta lateral dos testes

Toda resposta de um Presenter carrega um symbol não-enumerável com { data, systemRules, uiBlocks }. @mcpfusion/testing lê a visão estruturada por meio dele em vez de reanalisar o XML, e é assim que result.data, result.systemRules e result.uiBlocks em Testing são exatos. JSON.stringify nunca vê o symbol.

Regras implícitas de empacotamento

O valor de retorno de um handler é interpretado com uma única regra: objetos ToolResponse com a marca passam intocados, todo o resto é embrulhado como success(data). Um retorno null ou undefined vira o texto OK. É por isso que você nunca monta à mão formatos { content: [...] }, e é por isso que adicionar um Presenter nunca exige mexer no handler.

Próximos passos