MCP Fusion/Core concepts/Models e Presenters
Models e Presenters
Declare seu domínio uma única vez com defineModel e molde o que o agente percebe com createPresenter: schemas, rules, redação, blocos de UI, affordances e limites.
Models e Presenters são o M e o V de the MVA pattern. O Model declara o seu domínio uma única vez. O Presenter molda a percepção desse domínio para o agente. A separação é proposital: o mesmo Model pode ter vários Presenters, um para cada público ou tarefa.
Models
import { defineModel } from '@mcpfusion/core';
const InvoiceModel = defineModel('Invoice', (m) => {
m.casts({ amount_cents: 'number' });
m.hidden(['internal_notes', 'cost_basis']);
m.guarded(['id', 'created_at']);
m.fillable({
create: ['customer_id', 'amount_cents', 'due_date'],
update: ['status'],
});
});.casts(): coerções de tipo aplicadas na leitura.hidden(): campos que nunca saem do servidor, nem mesmo em saídas de depuração.guarded(): campos que o agente não pode escrever.fillable(): os únicos campos que cada operação pode escrever
Tudo o que você não declarar simplesmente não existe para o agente. O model compila para um schema Zod, então InvoiceModel.schema valida as entradas e typeof InvoiceModel.infer fornece o tipo TypeScript.
As anotações .describe() nos campos são extraídas automaticamente e viram regras de sistema para o agente, assim o significado do campo viaja junto com o campo.
Presenters
import { createPresenter, t, suggest, ui } from '@mcpfusion/core';
const InvoicePresenter = createPresenter('Invoice')
.schema({
id: t.string,
customer: t.string,
amount_cents: t.number.describe('CENTS, divide by 100'),
status: t.enum('draft', 'paid', 'overdue'),
})
.rules([
'CRITICAL: amount_cents is in CENTS. Divide by 100.',
])
.redactPII(['customer.email', 'customer.ssn'])
.ui((inv) => [
ui.table(['Field', 'Value'], [
['Invoice', inv.id],
['Amount', inv.amount_cents / 100],
['Status', inv.status],
]),
])
.suggest((inv) => [
inv.status === 'overdue'
? suggest('billing.remind', 'Send a payment reminder')
: null,
])
.limit(50);Os métodos do builder
| Método | O que faz |
|---|---|
.schema() | Os campos que existem para o agente, com tipos t e descrições |
.rules() | Linhas de prompt de sistema injetadas em toda resposta deste Presenter |
.redactPII() | Caminhos de campos removidos antes da serialização, o firewall de saída |
.ui() | Blocos renderizados como tabelas; seus dados contornam a redação por design |
.suggest() | Affordances: próximas ações que o agente pode considerar |
.limit() | Limite de itens retornados, com uma mensagem de truncamento autorreparável |
.embed() | Presenters aninhados para entidades relacionadas |
Rules são contratos, não comentários
As linhas de .rules() são injetadas no contexto do sistema, então o model não pode ignorá-las como ignora um comentário no código. Elas são o lugar certo para unidades, moedas, status e tudo aquilo em que o model vive errando.
A redação roda tarde, de propósito
O motor de DLP compila os caminhos de .redactPII() em uma função de redação otimizada, armazenada em cache por Presenter. A redação roda depois que os blocos de UI são renderizados e imediatamente antes da serialização, então os dashboards mantêm os valores reais enquanto o contexto do model permanece limpo.
Os dados de .ui() contornam a redação por design: uma tabela destinada a um operador humano pode exibir valores reais. Mantenha segredos fora dos blocos .ui(), a menos que um humano seja o único consumidor.
Uma entidade, vários públicos
const AdminPresenter = createPresenter('Invoice').schema({ /* everything */ });
const SupportPresenter = createPresenter('Invoice')
.schema({ id: t.string, status: t.enum('draft', 'paid', 'overdue') })
.limit(10);A percepção por papel não precisa de módulo extra: escolha o Presenter por tenant ou papel no middleware, e a mesma tool atende os dois públicos com segurança. Veja Credentials e Governance.
