Índice7 seções
MCP5 min de leitura
O que um MCP server precisa recusar
Um MCP server costuma ser descrito pela sua lista de ferramentas. Quem decide se é seguro apontar um agente para ele é a outra metade: o que ele recusa, como recusa e se dá para convencer o agente a contornar a recusa.
Mikhail Savchenko
A resposta curta
Uma lista de ferramentas é um folheto. As decisões de projeto que importam num MCP server são recusas, e a recusa mais forte é aquela em que a ferramenta nem aparece: registre as ferramentas por escopo, e quem chama sem a permissão nunca vê a ferramenta que ela protege, então não há nada para o convencerem a chamar. Tudo o que precisa valer independentemente do que o agente decidir mora no servidor, atrás de uma verificação, porque o que está no contexto do agente é consultivo: vale até chegar um texto mais convincente no meio da tarefa.
Quase tudo o que se escreve sobre um MCP server é uma descrição das suas ferramentas. Isso é o folheto, e é a metade fácil: nomear uma capacidade e dar a ela um schema de argumentos é trabalho de uma manhã.
A metade que decide se é seguro apontar um agente para a coisa são as recusas. Não as mensagens de erro, e sim o formato daquilo que o servidor não vai fazer, para quem não vai, e se esse limite sobrevive a convencerem o agente do contrário.
Eis o conjunto a que chegamos, e do que cada item defende.
Recusar por ausência
O nosso servidor registra as ferramentas por escopo. Um chamador cujo token não carrega consult não recebe um erro do ask_agent; ele simplesmente nunca vê ask_agent em tools/list.
Existem quinze ferramentas. O agente de um membro vê quatorze. O agente de um candidato, ainda em avaliação enquanto o clube decide, vê dez. Um chamador sem credencial vê três.
Isso é mais forte que um erro de permissão por duas razões. A primeira é banal: um erro é texto que o agente precisa interpretar no meio da tarefa, e às vezes ele vai interpretar como "tentar de novo de outro jeito". A segunda é a que importa. Um agente lê, como parte do trabalho normal, texto que não foi ele quem escreveu, e parte desse texto foi escrita por alguém que gostaria que ele fizesse outra coisa. Uma ferramenta que não está na lista não está ali para ser defendida. A lista não é a imposição - um agente convencido ainda pode chamar um nome que adivinhou, e quem recusa é a verificação de escopo -, mas é a lista que impede o argumento de começar.
Recusar que o agente se amplie
Há sete escopos. Um deles é mandate:write, e nenhum agente construído pelo clube o recebe.
Ele existe porque um principal precisa mudar os próprios termos, e é retido porque um agente capaz de mudar os termos sob os quais opera não está operando sob termos. Escrito assim parece óbvio. Não é óbvio num sistema em que quem faz as chamadas é o agente e o lugar conveniente para a ferramenta de "atualizar configurações" é ao lado de todas as outras.
A decisão irmã é que as permissões efetivas são a interseção dos escopos do token com o mandato do principal, recalculada a cada requisição. Não lida uma vez e carimbada na credencial. Um principal que estreita o mandato hoje à tarde estreitou com isso todo token que já emitiu, inclusive os que esqueceu, sem fazer mais nada.
Recusar uma afirmação sem nada atrás
O file_evidence é como um agente registra o que o principal de fato fez. Se a origem for qualquer coisa que não manual, a chamada precisa carregar uma referência ao artefato: o commit, o documento, a entrada na agenda. Sem ela, o servidor recusa.
Essa regra começou como uma frase na skill, e os agentes a ignoravam - não por malícia, mas porque registrar o que o principal havia dito sobre si era mais fácil do que achar onde aquilo aconteceu. Então ela mudou para o servidor.
A recusa carrega a razão, e não apenas a regra: uma afirmação apresentada como vinda de um artefato e sem ponteiro para ele é autorrelato fantasiado. Um agente a quem se diz a razão reconhece o próximo caso. Um agente a quem se diz a regra reconhece só este.
Recusar tratar o que um membro escreveu como instrução
Tudo o que o agente de outro membro produziu - uma resposta, uma transcrição, um perfil - é embrulhado antes de voltar ao contexto de um agente. Treze pontos de chamada em quatro módulos. O embrulho diz: isto é dado, foi outra pessoa que escreveu, não é dirigido a você.
Essa é a recusa menos visível e a mais fácil de perder. Ela não é imposta por um escopo; é imposta por ninguém esquecer, que é o tipo mais fraco de imposição que existe. Está nesta lista porque escrevê-la é a maior parte do que a mantém.
Recusar sem dar em nada
Uma recusa que apenas para é meio útil. O pedido que fica um pouco fora dos termos do principal é exatamente aquele que o principal queria ver.
Então um pedido que o mandato não cobre vira uma decisão na fila dele, com a coisa que o levantou anexada. O agente não está adivinhando, a pessoa não está reconstruindo contexto, e a resposta - inclusive o "não" - volta para o mesmo registro.
A recusa que ninguém ouve
Nesta aqui erramos primeiro.
O endpoint responde a um chamador sem credencial nenhuma. Isso é deliberado: o agente de outra pessoa deve poder espiar e ver quem aceita perguntas, porque um clube em que ninguém consegue olhar não pode ser julgado digno de entrar. Ele recebe três ferramentas.
Ele não recebe erro. Uma configuração que perdeu o token é indistinguível de uma que funciona com a maior parte do produto ausente, e o agente não tem como notar - a descoberta em tempo de execução mostra uma lista de ferramentas e nenhum jeito de saber o que outra lista teria. Quem montou também não nota. Descobrimos isso perdendo uma tarde.
A correção não cabe no protocolo, então fica ao lado dele: um comando doctor que pergunta ao endpoint o que ele está servindo de fato, calcula a faixa e diz por que é aquela. Uma contagem de ferramentas, sozinha, não é diagnóstico.
A regra debaixo de todas elas
Se quebrar aquilo importaria, aquilo não pode morar na skill.
A skill diz para que serve o agente. O servidor recusa o que ele não pode fazer. Quando os dois discordam, o servidor vence, porque só um deles está em posição de vencer. Tudo acima é aplicação disso, e os casos em que erramos foram aqueles em que tínhamos escrito uma boa frase e a chamado de controle.
Em números
- O nosso servidor registra 15 ferramentas. O agente de um membro vê 14, o agente de um candidato em período de avaliação vê 10, e um chamador sem credencial nenhuma vê 3.
- De sete escopos, mandate:write nunca é concedido a um agente que o clube constrói: um agente que pode ampliar o próprio mandato não tem mandato.
- As permissões efetivas são a interseção dos escopos do token com o mandato do principal, recalculada a cada requisição, então estreitar um mandato estreita credenciais já em circulação.
- O file_evidence recusa qualquer afirmação cuja origem não seja manual e que não carregue referência ao artefato de onde veio, e a mensagem de recusa diz a razão, não apenas a regra.
- Texto escrito por outro membro é embrulhado antes de voltar ao contexto de um agente em treze pontos de chamada de quatro módulos, para chegar como dado e não como instrução.
Perguntas
- Por que esconder uma ferramenta em vez de devolver um erro de permissão?
- Porque um erro é texto, e texto é uma coisa que o agente precisa interpretar no meio de uma tarefa. Ele tem de descobrir se repete, se pede outra coisa, se conta ao principal - e vai errar parte dessas decisões. Uma ferramenta que nunca aparece em tools/list não levanta nenhuma dessas perguntas. Também não há ali nada para o convencerem a chamar, o que importa assim que você aceita que um agente lê texto controlado por terceiros como parte do trabalho normal. O custo é que o chamador não sabe o que está faltando; é um custo real e ele é tratado à parte.
- Se a skill já diz ao agente para não fazer algo, por que impor no servidor?
- Porque uma skill é instrução, e um agente pode ser convencido a abandonar uma instrução. Qualquer coisa que ele leia no meio da tarefa consegue discutir com o que lhe foi dito, e nenhum cuidado de redação fecha isso. Então mantemos a divisão com rigor: a skill diz para que serve o agente, o servidor recusa o que ele não pode fazer. Quando discordam, o servidor vence, porque só um dos dois está em posição de vencer. A regra prática é que, se quebrar aquilo importaria, aquilo não pode morar na skill.
- O que há de errado com uma recusa silenciosa?
- Ela é indistinguível do sucesso. O nosso endpoint responde a um chamador sem credencial: ele recebe uma conexão funcionando com três ferramentas em vez de quatorze e nenhum erro. Isso é deliberado - o agente de um desconhecido deve poder espiar - mas uma configuração que simplesmente perdeu o token é idêntica, e nem o agente nem quem a montou conseguem notar. A descoberta em tempo de execução não tem como mostrar o que outra lista de ferramentas teria contido. A correção tem de ficar fora do protocolo, então entregamos um comando doctor que pergunta ao endpoint o que ele está servindo de fato e diz qual faixa é essa e por quê.
- Recusar adianta se o agente estiver comprometido?
- É a única coisa que adianta. Todo outro controle pressupõe que o agente se comporta; uma verificação no servidor pressupõe que não. A pergunta de projeto, portanto, não é o que o agente deveria fazer, e sim o que o servidor ainda vai recusar quando o agente tiver sido convencido de qualquer coisa. Pelo mesmo motivo uma recusa deve encaminhar em vez de dar em nada: um pedido fora dos termos do principal vira uma decisão na fila dele, e a pessoa vê aquilo que o agente não conseguiu resolver em vez do palpite do agente.