MCP Fusion/Core concepts/The MVA pattern
The MVA pattern
Model View Agent replaces MVC for AI backends: the View becomes a Presenter, a perception layer that controls exactly what the AI agent sees, knows and can do next.
MVA stands for Model View Agent. It is an architectural pattern for AI agent backends, and MCP Fusion is its reference implementation for the Model Context Protocol. The idea is small and deep at the same time: when the consumer of your API is an AI agent instead of a browser, the View layer changes meaning. It no longer renders HTML. It shapes what the agent perceives.
f.query() · billing.get_invoice// the agent called the tool
billing.get_invoice({ id: 'inv_2026_0417' })
// the handler returns the raw model row
ctx.db.invoices.findUnique({
where: { id: input.id },
});waiting for the tool result…
Raw SDK would giveA raw SDK server would return this row as-is: every column, every internal field, straight into the model context.
Press Run and follow one tool call through the pattern: raw row, Presenter, redaction, rules, affordances, clean perception package.
The three letters
| Letter | Name | Responsibility |
|---|---|---|
| M | Model | Your domain data: invoices, users, tickets. Declared once with defineModel(). |
| V | View (Presenter) | The perception layer: which fields exist, which values are redacted, which rules the agent gets, which actions it may take next. |
| A | Agent | The LLM. It never touches the Model directly. It only ever sees what a Presenter chose to show. |
Three failures MVA removes
Context starvation. A raw JSON dump wastes context window on fields the task does not need. Presenters trim every response to the fields the task needs and cap the volume with .limit().
Action blindness. The agent finished a step and now guesses what to do next, so it hallucinates a tool name. Presenters close each response with .suggest(), a list of affordances: the server tells the agent what it may do next, the way HATEOAS does for web APIs.
Perception drift. Two tools return the same entity in different shapes, or units change and no one tells the model. A Presenter is defined once per entity, so every tool returning that entity shows the same shaped truth, and .rules() carry the meaning in system prompt lines.
MVC and MVA side by side
| Aspect | MVC | MVA |
|---|---|---|
| Consumer | Browser | AI agent |
| View layer | Renders HTML or JSON | Shapes perception: fields, rules, affordances |
| Trust boundary | The user sees the UI | The model must not see secrets |
| Navigation | Links between pages | .suggest() affordances between actions |
| Failure mode | Broken page | Hallucination, leaked PII, wrong units |
What a Presenter is made of
Each piece is optional and composable:
.schema(): the fields that exist, with descriptions the agent reads.redactPII(): paths removed before serialization, always.rules(): system prompt lines injected with every response.ui(): rendered blocks such as tables whose data bypasses redaction for dashboards.suggest(): next actions the agent is allowed to consider.limit(): a cap on response volume with a self healing message.embed(): nested presenters for related entities
The Presenter is a security boundary, not a formatting helper. Redaction runs after UI blocks render and before serialization, so the wire never sees sensitive data. This is why every tool in MCP Fusion attaches its Presenter with .returns().
Next steps
- Models and Presenters: the full API of both
- Tools: how tools attach Presenters
- Governance: proving the surface stayed safe over time
