MCP Fusion/Protocol and runtime/Sincronização de estado

Sincronização de estado

Pergunte à IA sobre a Vinkius

Diga ao agente o que pode ser reaproveitado e o que acabou de ficar obsoleto: hints de cache-control nas descrições, invalidação com escopo glob nas mutations, notificações de recursos e cache de listas do MCP 2.0.

O fracasso clássico de um agente depois de uma mutation: ele relê dados obsoletos, "corrige" o problema errado ou reaplica a própria mudança porque a leitura veio do contexto dele, não do seu servidor. A camada de sincronização de estado do MCP Fusion responde a uma única pergunta: quando o agente pode confiar em dados em cache, e o que avisa que um domínio acabou de ficar obsoleto?

Isto não é um cache de respostas. O MCP Fusion nunca armazena resultados de tools. É um sistema de sinalização temporal, usando o vocabulário de cache HTTP que o modelo já entende.

Declarando a política

Três métodos em qualquer tool:

typescript
f.query('billing.get_invoice')
  .cached()                          // "[Cache-Control: immutable]" on the description
  .handle(...)

f.mutation('billing.refund')
  .invalidates('billing.get_*', 'dashboard.invoices')
  .handle(...)
  • .cached() carimba immutable; .stale() carimba no-store; não existe max-age, porque modelos de linguagem não têm relógio
  • .invalidates(patterns) declara quais domínios uma chamada bem-sucedida torna stale, com padrões dot-glob (* um segmento, ** qualquer profundidade)
  • as hints são compiladas em políticas num PolicyEngine first-match-wins com resolução memoizada

O que vai para o fio

Duas interceptações na camada de protocolo, não middlewares:

Em tools/list, as descrições decoradas carregam a diretiva: Retrieve an invoice by ID [Cache-Control: immutable]. O agente vê a regra de reuso no momento de planejar.

Em tools/call, apenas chamadas bem-sucedidas disparam invalidação (uma mutation isError falha não emite nada). A resposta ganha uma tag legível por máquina como primeiro item de conteúdo, posicionada no índice 0 para que o truncamento não a corte:

xml
<cache_invalidation cause="billing.refund" domains="billing.get_*" />

e cada URI stale dispara notifications/resources/updated com um mcpfusion://stale/{pattern} sintético, para que clientes com assinaturas saibam exatamente o que descartar.

Recursos e assinaturas

Recursos reais passam por f.resource() (ou defineResource): um template de URI como invoices://{id}, um handler que retorna { text } ou { blob }, um flag subscribable e anotações de audiência. URIs de template são resolvidas com matching estilo RFC-6570.

No MCP 2.0 o stream subscriptions/listen é de primeira classe: um cliente registra um filtro (toolsListChanged, promptsListChanged, resourcesListChanged, URIs específicas em resourceSubscriptions), o servidor confirma com o filtro atendido e transmite os eventos correspondentes. A entrega é best-effort e nunca bloqueia o pipeline: uma notificação é descartada, não gera backpressure.

Cache de listas (SEP-2549)

Separado do staleness de tools, o MCP 2.0 permite que um servidor anexe metadados de cache aos resultados de listagem: ttlMs e scope (private por cliente, public compartilhado) em tools/list, prompts/list e nas listas de recursos, configurados com attachToServer({ listCacheTtlMs, listCacheScope }). Padrão: 5 minutos, private. Um proxy na frente do seu conector pode então servir chamadas de listagem repetidas do próprio cache, que é a resposta do protocolo para clientes verbosos no cold start. Defina listCacheTtlMs: 0 e os metadados desaparecem com overhead zero.

O manifesto dinâmico e a introspecção

attachToServer({ introspection: { enabled: true } }) registra o recurso mcpfusion://manifest.json: toda tool, ação, presenter, chave de schema, flag destrutivo e flag de regra contextual, filtrados por RBAC a cada leitura com um contexto fresco de contextFactory, para que duas sessões leiam o manifesto que têm direito de ver. A mesma máquina de materializar e digerir é o que Governance tranca.

Por que não max-age

Porque não existe timer dentro do modelo: ele não pode checar um header de frescor, só pode ser avisado. immutable significa "reaproveite nesta conversa a menos que eu diga o contrário"; os eventos de invalidação são o "contrário". O vocabulário HTTP é emprestado justamente porque os modelos foram treinados nele, e o sufixo [Cache-Control] viaja dentro da descrição da tool, que é o único texto que o agente lê de forma confiável antes de planejar.

Próximos passos