Pedro Sousa
‹ ← Artigos

Artigo · Blog

MCP não é integração. É fronteira. E você provavelmente está desenhando ela no lugar errado.

A maioria dos projetos que usa MCP trata o protocolo como um adaptador glorificado. O problema real não é técnico: é onde você decide que o agente termina e o sistema começa. Errar essa fronteira é mais caro do que errar a implementação.

MCP não é integração. É fronteira. E você provavelmente está desenhando ela no lugar errado.

Num projeto com agentes de IA que conduzi recentemente, o MCP server estava funcionando. Tools respondendo. O modelo chamando as funções certas. Build verde, demo impecável.

Duas semanas depois, o agente começou a tomar decisões que ninguém conseguia rastrear.

Não era bug. Era consequência de uma fronteira desenhada no lugar errado desde o início.


O que a maioria entende por MCP

Model Context Protocol virou assunto rápido. E como todo assunto que cresce rápido, a maioria absorveu a camada de cima: é um jeito padronizado de o modelo chamar ferramentas externas. Você define tools, expõe um servidor, o modelo decide quando usar cada uma.

Até aí, correto.

O problema é que parar nessa definição é o mesmo que descrever Clean Architecture como "separar em pastas".

MCP não é só o mecanismo. É onde você formaliza o que o agente pode fazer. E o que ele não pode. E, mais importante, o que ele não precisa saber.


A decisão que parece técnica mas não é

Quando você começa a mapear tools para um MCP server, a primeira tentação é expor operações que espelham sua API interna. getUserById, updateOrder, fetchTransactions.

Parece natural. Você já tem esses endpoints. É só adaptar.

O que acontece na prática: o modelo vira um orquestrador de chamadas de API sem nenhuma semântica de negócio. Ele não sabe o que uma transação significa no seu domínio. Ele só sabe que pode buscar, atualizar e deletar.

Num app financeiro onde trabalhei, fizemos exatamente isso num protótipo. O agente funcionava. Mas ele resolvia tarefas montando sequências de chamadas que faziam sentido sintaticamente e eram incorretas semanticamente. Ele fazia fetchTransactions seguido de updateOrder em situações onde a regra de negócio exigia uma verificação intermediária que só existia implicitamente no código de produção.

A verificação estava no service layer da aplicação. Não estava exposta como tool. E o modelo não tinha como saber que ela deveria existir naquele fluxo.


O problema real: quem conhece o domínio?

Quando você expõe operações CRUD como tools, você está delegando conhecimento de domínio para o modelo.

Só que o modelo não tem esse conhecimento. Ele tem linguagem. Ele infere padrão. Ele generaliza a partir do que você descreveu nas definições das tools.

Se a descrição da tool diz "atualiza o status de um pedido", o modelo vai chamar essa tool quando precisar atualizar o status de um pedido. Simples. Mas a regra que diz "só atualiza se o pedido estiver em estado X e o usuário tiver permissão Y" não está na tool. Está no seu sistema.

Se você não encapsula essa regra dentro da tool, você tem dois caminhos ruins: ou duplica a regra na descrição (e ela fica desatualizada quando o produto muda), ou confia que o modelo vai inferir corretamente (e ele não vai, não sempre).

A tool que expõe operação sem semântica de domínio não protege o sistema. Ela abre o sistema.


Onde a fronteira deveria estar

A analogia que funciona melhor pra mim é a de caso de uso.

Uma tool de MCP bem desenhada age como um caso de uso, não como um repositório. Ela recebe uma intenção, executa uma sequência de operações com regras de negócio aplicadas, e retorna um resultado com significado.

Em vez de fetchTransactions + updateOrder, você expõe approveRefundRequest. O modelo só precisa saber que pode pedir um reembolso para um usuário em um pedido. O que acontece por baixo, qual a sequência de validações, quais estados são permitidos: isso é responsabilidade do sistema, não do modelo.

Isso muda o que o agente pode fazer de forma significativa. E muda o que ele pode fazer de errado também.

Um agente que só tem acesso a casos de uso bem definidos comete erros dentro de limites. Um agente com acesso a operações atômicas do banco de dados pode escalar um erro de raciocínio para um estrago que atravessa múltiplas entidades.


O papel do RAG nessa equação

RAG aparece nessa conversa porque é a outra metade do contexto que o modelo recebe.

A maioria implementa RAG como busca semântica em documentação ou histórico. Funciona para perguntas. Falha para decisão.

O que percebi depois de errar em alguns protótipos: RAG para agentes de decisão precisa trazer contexto de estado, não só contexto de conhecimento.

Não adianta recuperar "como funciona o fluxo de reembolso" se o modelo não sabe que aquele usuário específico já teve três reembolsos aprovados esse mês e que existe uma política de limite. Esse contexto de estado precisa ser recuperado e injetado de forma estruturada, não como texto livre.

Quando você mistura conhecimento de domínio com estado operacional num único pipeline de RAG, o modelo recebe tudo junto e decide com base numa mistura que você não controla bem.

Separar as fontes, e ser explícito sobre o que é conhecimento estático e o que é estado dinâmico, muda a qualidade das decisões do agente de forma mensurável.


O que eu faria diferente hoje

No projeto onde a fronteira ficou errada, o sinal apareceu tarde porque o problema era intermitente. O agente acertava na maioria dos casos. Errava nos edge cases de domínio. E edge case de domínio financeiro não é apenas um bug, é um incidente.

Se fosse começar de novo:

Antes de implementar qualquer tool, eu escreveria os casos de uso do sistema em linguagem natural. Depois mapearia cada tool para exatamente um caso de uso. Se uma tool mapeasse para mais de um caso de uso, ou para metade de um, eu redesenharia a granularidade.

A descrição da tool precisaria incluir o que ela valida, não só o que ela faz. O modelo usa a descrição para raciocinar sobre quando chamar a tool. Se a descrição não menciona as guardas de negócio, o modelo não sabe que elas existem.

E o RAG viria em duas camadas distintas: uma para contexto de domínio estático, outra para contexto de estado do usuário e da sessão. Injetados separadamente, com estrutura diferente, para que o modelo possa distinguir o que é regra do que é situação.


O que isso tem a ver com arquitetura mobile

Mais do que parece.

Nos últimos anos acompanhei times tentando integrar IA em apps mobile como se fosse uma feature nova. Chamada de API diferente, resultado exibido na tela.

O problema começa quando o agente precisa agir dentro do contexto do app. Chamar APIs do sistema. Navegar fluxos. Reagir a estado local.

Nesse momento, a mesma questão de fronteira aparece. O que o agente pode fazer dentro do app? O que é intenção legítima do usuário? O que é operação que precisa de confirmação explícita?

Apps que integraram agentes sem definir essa fronteira viraram um campo de decisões não rastreáveis. O usuário não sabia o que o agente tinha feito. O time não conseguia reproduzir o estado que o agente tinha gerado.

Rastreabilidade de estado não é problema de IA. É problema de arquitetura. A IA só torna o problema mais urgente.


Existe uma diferença entre um sistema que usa IA e um sistema que pensa em termos de IA.

O primeiro trata o modelo como um componente. O segundo reorganiza fronteiras de responsabilidade com o modelo em mente desde o início.

MCP é uma ferramenta boa. Mas como toda ferramenta boa, ela amplifica o que você já tem. Se o que você tem são operações sem semântica de domínio, o agente vai usá-las sem semântica de domínio.

A pergunta que vale fazer antes de expor qualquer tool não é "o modelo consegue chamar isso?". É "o modelo consegue errar isso sem você perceber?"