Anotações de ferramentas MCP e limites de aprovação
As anotações de ferramentas MCP podem descrever o comportamento esperado, mas somente testes dos efeitos colaterais reais devem determinar quais ações do agente precisam de aprovação.

As anotações de ferramentas MCP são uma documentação útil. Também são um lugar fácil para fazer um sistema de aprovação inseguro parecer organizado. Se um cliente trata readOnlyHint, destructiveHint ou idempotentHint como autorização, o autor do servidor acaba escrevendo a política de aprovação do usuário sem provar que a implementação merece essa confiança.
Isso está invertido. Uma anotação pode ajudar a explicar um prompt, organizar uma lista de ferramentas ou sugerir um padrão razoável para uma pessoa que analisa uma ação. A aprovação deve seguir a solicitação que o servidor executará, a credencial que usará, o alvo que alcançará e os efeitos colaterais que poderá disparar. Já vi integrações demais chamarem um endpoint chamado get, retornarem um objeto JSON educado e ainda assim criarem trabalho em outro lugar.
A distinção importa especialmente com agentes, porque eles repetem chamadas, combinam ferramentas e agem em um ritmo que torna caro até um pequeno erro de classificação. Uma ferramenta segura uma vez pode ser insegura em um loop. Uma ferramenta somente leitura em uma API pode virar um mecanismo de exportação em outra. Uma ferramenta que parece idempotente pode criar trabalho duplicado quando um timeout esconde o primeiro sucesso.
As anotações descrevem o comportamento, mas não concedem autoridade
A especificação de ferramentas do Model Context Protocol descreve as anotações como indicações sobre o comportamento de uma ferramenta. Essa escolha de palavras é intencional: um cliente pode usá-las para melhorar sua interface, mas não pode usar com segurança uma declaração não verificada como decisão de segurança.
Os três campos em questão descrevem afirmações diferentes:
readOnlyHint: trueafirma que a ferramenta não modifica o ambiente.destructiveHint: trueafirma que a ferramenta pode executar atualizações destrutivas.idempotentHint: trueafirma que chamadas repetidas com os mesmos argumentos não têm efeito adicional sobre o ambiente.
Essas afirmações não cobrem todo o risco de uma chamada. Uma ferramenta pode ler um banco de dados inteiro de clientes, enviar o resultado ao agente e, ainda assim, marcar-se honestamente como somente leitura. Outra pode gravar apenas um horário de acesso, algo que parece pequeno até alterar retenção, cobrança ou um registro de incidente. A idempotência não diz nada sobre a aceitabilidade do primeiro efeito.
A especificação também define padrões conservadores para esses campos. readOnlyHint assume false. idempotentHint assume false. destructiveHint assume true, e só tem significado útil quando a ferramenta não é somente leitura. Não substitua esses padrões por uma regra caseira como «metadados ausentes significam seguro o bastante». A ausência de metadados muitas vezes indica que o autor do servidor não refletiu sobre a classificação.
Há outro ponto desconfortável: um servidor benigno pode estar errado. Um desenvolvedor adiciona readOnlyHint: true porque o handler executa um SELECT, mas uma camada de biblioteca atualiza um token, grava uma entrada de cache ou chama um hook de solicitação. A anotação continua verdadeira muito depois de o comportamento ter mudado. Ninguém tentou enganar o cliente, mas o cliente ainda tomou uma decisão ruim se aprovou a ação automaticamente.
Uma leitura não significa um resultado inofensivo
Uma operação somente leitura pode expor dados, consumir um recurso escasso ou ativar um comportamento em um serviço remoto. Tratar «não grava» como «não precisa de aprovação» é um erro de categoria.
Considere uma ferramenta chamada get_build_log que aceita o identificador de uma tarefa. O servidor faz uma leitura no sistema de compilação e retorna a saída. Ele pode declarar corretamente readOnlyHint: true. Ainda assim, o log pode conter código-fonte, detalhes do ambiente, URLs de download assinadas ou credenciais que outro sistema imprimiu por acidente. Enviar essa resposta a um agente autônomo muda quem pode usar essas informações, mesmo que o banco de dados do sistema de compilação permaneça intacto.
O mesmo problema aparece em APIs administrativas. get_user pode retornar códigos de recuperação. list_invoices pode expor dados bancários. search_documents pode virar extração em massa quando um agente aumenta o tamanho da página ou percorre todos os prefixos. O efeito colateral é a divulgação, e a anotação não tem um campo para a sensibilidade dessa divulgação.
Operações de leitura também podem alterar o serviço remoto. Algumas APIs atualizam last_accessed_at, consomem um token de download de uso único, registram uma visualização ou emitem uma consulta tarifada. Uma falha de cache pode aquecer um serviço posterior dispendioso. Esses efeitos não tornam toda leitura perigosa, mas tornam indefensável uma regra de aprovação sem ressalvas para operações somente leitura.
Classifique a chamada em dois eixos separados: ela modifica um sistema, e o que pode revelar ou causar fora desse sistema? Uma consulta de status de baixo risco e uma exportação em massa podem não modificar nada. Elas não devem receber o mesmo tratamento de aprovação.
Um registro prático de revisão deve indicar o limite dos dados em linguagem simples. «Lê o status da implantação do projeto A» pode ser analisado. «Chama get_status» não pode. A segunda descrição esconde o alvo, o escopo, a conta e o fato de que um método com nome semelhante pode significar algo diferente em outro servidor.
Teste o handler contra um alvo descartável
Não é possível estabelecer a segurança de uma anotação lendo o nome de uma ferramenta ou seu esquema de entrada. Execute o servidor em um ambiente em que seja possível observar a solicitação, a resposta e o estado antes e depois da chamada.
Comece com uma conta fixture que contenha registros que possam ser perdidos. Dê a ela uma credencial de API separada e direcione os webhooks de notificação para um endpoint de captura. Registre as solicitações de saída do servidor, o estado do banco de dados se você tiver controle sobre ele, eventos de auditoria, e-mails, tarefas enfileiradas e contadores de cobrança ou uso. O corpo da resposta é uma evidência, mas não é o registro completo.
Use uma pequena matriz de testes para toda ferramenta que possa influenciar aprovações:
- Chame-a uma vez com uma entrada válida comum e salve o estado completo antes e depois.
- Chame-a novamente com uma entrada idêntica byte a byte e compare todos os efeitos observáveis.
- Chame-a com um recurso ausente, uma operação já concluída e um campo inválido.
- Interrompa o cliente depois que o servidor receber a solicitação e repita a mesma chamada.
- Execute duas chamadas idênticas simultaneamente se os agentes puderem fazer isso em paralelo.
O caso de timeout captura uma falha frequente. Suponha que create_ticket envie a solicitação de criação do chamado e a conexão seja interrompida antes que o servidor responda. O agente vê um erro e tenta novamente. Se o sistema de chamados não tiver um token de idempotência, a ferramenta criará dois chamados. Marcar o handler como idempotente porque o código aceita a mesma entrada duas vezes não muda o resultado remoto.
Registre o resultado em um formato que force as pessoas a examinar os efeitos, em vez de confiar em uma resposta verde:
case: retry after response timeout
request: {"title":"rotate staging certificate","request_id":"test-104"}
first call: transport timeout after request received
second call: 201 {"ticket":"842"}
remote records: ["841", "842"]
result: not idempotent without a remote idempotency mechanism
O request_id desse exemplo só ajuda se a API remota o armazenar e fizer cumprir seu uso. Um identificador criado pelo cliente que o servidor ignora é apenas decoração. Teste essa aplicação repetindo o identificador exato e verificando se o sistema remoto retorna a operação original, em vez de criar outra.
Mantenha os testes junto do servidor. A divergência das anotações costuma chegar com uma mudança de código, uma atualização de dependência ou um novo endpoint. Um teste aprovado que compara a indicação declarada com o comportamento observável vale mais do que um comentário ao lado da definição da ferramenta.
As declarações de somente leitura falham nos limites
A falsa readOnlyHint mais fácil de criar surge quando se observa apenas a consulta principal ao banco de dados. O limite relevante inclui todos os serviços chamados pelo handler e todas as ações causadas por sua resposta.
Considere uma ferramenta de servidor que busca um documento. A solicitação principal é GET /documents/42, mas o handler pode primeiro trocar um token de atualização, emitir uma URL temporária de download, atualizar um cache local e gravar um evento de acesso. Cada operação pode falhar de uma forma diferente. Cada uma pode estar sujeita a credenciais e requisitos de auditoria diferentes.
Não aceite o argumento de que uma gravação é pequena demais para contar. Gravações pequenas criam seus próprios modos de falha. Um marcador de última visualização pode influenciar a retenção. Um cache pode preservar conteúdo depois que o acesso deveria ter terminado. Um evento de acesso pode notificar um proprietário. Um contador de uso pode colocar uma conta em um plano pago. Pergunte se a gravação altera um fato que outra pessoa, processo ou cobrança observará. Se alterar, documente.
O comportamento disparado pela resposta merece o mesmo cuidado. Uma ferramenta que retorna um link assinado pode levar o agente a buscá-lo depois. Uma ferramenta que retorna um comando executável pode levar o agente a executá-lo em outro canal. A primeira ferramenta continua sendo somente leitura em sentido estrito, mas uma tela de aprovação que diz «leitura segura» dá à pessoa uma imagem falsa da próxima ação que o agente pode realizar.
Um bom servidor separa as operações quando os riscos são diferentes. get_document_metadata pode continuar sendo uma chamada de inspeção limitada. create_download_link deve ser uma ferramenta própria porque cria uma capacidade de portador, mesmo que os bytes do documento subjacente não mudem. Essa divisão ajuda os agentes a escolher corretamente e oferece aos revisores uma descrição que eles podem de fato aprovar.
Destrutivo diz respeito à possibilidade de reversão, não a uma lista de verbos
destructiveHint deve refletir se uma chamada pode causar atualizações prejudiciais difíceis de reverter, não se o nome da ferramenta contém delete. As equipes erram nos dois sentidos.
Alguns verbos óbvios são reversíveis em um sistema e permanentes em outro. archive pode apenas ocultar um registro ou iniciar um temporizador de exclusão definitiva. disable_user pode manter intactos todos os acessos e arquivos ou revogar o acesso de uma forma que deixe um processo automatizado sem saída. replace_config pode atualizar um rascunho ou causar uma implantação imediata em produção. O handler precisa de informações sobre o alvo que um único booleano não consegue expressar.
Algumas ferramentas com nomes inocentes são claramente destrutivas. sync_members pode remover contas ausentes da lista enviada. apply_labels pode substituir uma taxonomia cuidadosamente mantida. reconcile pode corrigir um registro contábil externo com lançamentos que ninguém deveria criar casualmente. Um autor de servidor que marca essas ferramentas como não destrutivas porque a API pode tecnicamente desfazer as alterações está escondendo o custo operacional da correção.
Trate a possibilidade de reversão como uma sequência, não como uma caixa de seleção. Pergunte quem pode desfazer o resultado, de quais evidências essa pessoa precisa, por quanto tempo a reversão continua disponível e se um processo posterior consome a alteração antes que alguém possa revertê-la. Se uma pessoa precisar reconstruir a intenção a partir dos logs depois de uma atualização em massa, a ação merece uma classificação destrutiva mesmo quando a API oferece um método inverso.
A recomendação ruim mais popular é reservar a aprovação apenas para exclusões explícitas. Ela é popular porque mantém os agentes em movimento e faz uma demonstração parecer fluida. Falha em produção porque as mudanças destrutivas geralmente são substituições, revogações, envios ou reconciliações. Aprove a mudança de estado relevante, não o vocabulário usado para descrevê-la.
Para uma ação que afeta uma coleção, exija que o registro de revisão inclua a regra de seleção e a quantidade. «Sincronizar usuários» é vago demais. «Remover 14 prestadores de serviço inativos selecionados pelos IDs fornecidos» permite que uma pessoa avalie o escopo. Se o servidor não consegue informar esse escopo antes de agir, não forneceu informações suficientes para um prompt de aprovação sério.
A idempotência precisa sobreviver a novas tentativas e à concorrência
idempotentHint é uma afirmação limitada: argumentos idênticos devem produzir nenhum efeito adicional depois da primeira chamada. Isso não significa que a chamada seja segura, barata, reversível ou apropriada para um agente repetir indefinidamente.
Uma atualização de status pode ser idempotente se definir state=closed duas vezes deixar o mesmo registro fechado. Mas o handler deixa de ser idempotente se enviar um e-mail a cada chamada, adicionar um comentário de auditoria a cada chamada ou incrementar um contador de versão. Muitas pessoas examinam a linha do banco de dados e não percebem os efeitos secundários que os usuários notam primeiro.
A igualdade das entradas também precisa de uma definição precisa. A ordem dos objetos JSON não deveria importar. Um servidor que trata note omitido de forma diferente de note: "" pode receber o que o agente considera a mesma solicitação, mas executar duas atualizações distintas. Valores de tempo, padrões gerados e expressões relativas como tomorrow enfraquecem a afirmação porque mudam o comando efetivo enquanto os argumentos visíveis parecem estáveis.
A concorrência é onde a idempotência casual desmorona. Dois processos de trabalho podem verificar ao mesmo tempo que um objeto não existe e, depois, criar os dois. Uma restrição de unicidade, um upsert transacional ou um recurso remoto de idempotência pode impedir isso. Um cache em memória de um único processo do servidor MCP não protege uma implantação que executa mais de um processo.
Use um registro de idempotência somente depois de definir seu escopo. Armazene um token fornecido pelo chamador junto com a identidade autenticada, o corpo da solicitação normalizado, o resultado e uma expiração adequada à operação. Rejeite um token reutilizado com uma entrada normalizada diferente. Caso contrário, um agente pode anexar acidentalmente um token antigo a uma nova solicitação e receber o resultado de uma ação diferente.
Não repita automaticamente uma chamada apenas porque a indicação é true. Repita somente as falhas em que você sabe se o servidor recebeu a chamada. Se não puder saber, a solução é um mecanismo de idempotência no ponto real da mutação. A indicação no cliente não resolve isso.
Crie aprovações a partir da ação executada
Um sistema de aprovação deve responder: qual processo está fazendo a solicitação, qual credencial será usada, qual alvo externo receberá a solicitação, qual estado ou dados estão no escopo e o que acontecerá se a chamada for bem-sucedida. As anotações de ferramentas podem tornar essa explicação mais curta. Elas não fornecem fatos que o servidor não expôs.
Mantenha separadas a aprovação da sessão e a aprovação por chamada. A aprovação da sessão serve para um processo de agente conhecido que executa uma tarefa delimitada com capacidades comuns. A aprovação por chamada serve para credenciais que podem transferir dinheiro, alterar acessos de produção, enviar mensagens, divulgar registros sensíveis ou criar um compromisso externo irreversível. A decisão pertence à credencial e ao contexto da ação, não a um readOnlyHint otimista.
Um prompt útil nomeia a operação concreta: «O agente assinado por esta autoridade usará a credencial de implantação para reiniciar o serviço X na conta Y». Um prompt fraco diz: «Permitir a ferramenta deploy?». O primeiro oferece algo que o revisor pode avaliar. O segundo pede que ele confie em um detalhe da implementação.
O Sallyport segue essa separação mantendo as credenciais de API e SSH em seu cofre criptografado, executando a ação HTTP ou SSH e retornando o resultado ao agente, não a credencial. Sua autorização de sessão identifica o processo solicitante, enquanto uma configuração por credencial pode exigir uma decisão a cada uso. Esse é um lugar melhor para colocar o controle humano do que um booleano fornecido pelo servidor.
Mesmo com um portão diante das credenciais, mantenha um registro de atividade que capture o alvo e o resultado da solicitação final. A aprovação responde se a ação pode prosseguir. O registro de auditoria responde o que aconteceu quando ela prosseguiu. Não misture essas perguntas em um único evento vago chamado «ferramenta usada».
Inclua as verificações das anotações na manutenção do servidor
O uso correto das anotações de ferramentas MCP é uma comunicação honesta apoiada por testes. O autor do servidor deve defini-las de forma conservadora, documentar qualquer caso de fronteira e alterá-las quando o comportamento mudar. O autor do cliente deve usá-las como uma entrada do desenho da interface, nunca como a única entrada da permissão.
Adicione testes que contradigam deliberadamente uma propriedade declarada. Para uma declaração de somente leitura, faça o teste falhar se o fixture detectar uma gravação, uma notificação de saída, uma atualização de credencial ou uma capacidade criada para recuperação posterior. Para uma declaração destrutiva, teste o caminho de falha e o caminho de desfazer, incluindo o que acontece depois que uma tarefa posterior consome a alteração. Para idempotência, execute a mesma solicitação normalizada depois de um timeout simulado e com chamadas concorrentes.
Não esconda uma divergência alterando o alvo do teste até que ele passe. Restrinja a ferramenta para que a indicação se torne verdadeira, altere a anotação ou exponha o efeito nos detalhes da aprovação. Cada opção informa ao próximo responsável o que o código realmente faz.
Execute sp audit verify como parte da análise de incidentes quando o Sallyport for o gateway de ações. O comando verifica offline a cadeia de hashes criptografada, para que você possa confirmar se o histórico registrado de ações permaneceu íntegro sem abrir o cofre. Isso não prova que a anotação do servidor foi honesta, mas fornece um registro resistente a alterações das chamadas que a seguiram.
O padrão prático é simples: uma anotação deve sobreviver a um teste adversarial contra o efeito que importa para o usuário. Se não conseguir, mantenha-a conservadora e deixe a aprovação onde a ação acontece.
FAQ
Posso aprovar automaticamente com segurança uma ferramenta MCP que tenha readOnlyHint?
Trate isso como uma declaração que precisa de evidências, não como uma concessão de permissão. Examine a implementação do servidor, execute a ferramenta contra um alvo descartável e verifique todos os efeitos colaterais que ela pode causar.
O que idempotentHint realmente garante?
Isso significa que o autor acredita que chamadas repetidas com os mesmos argumentos não produzem nenhum efeito adicional no ambiente. Ainda é preciso testar sistemas externos, comportamento de novas tentativas, marcas de tempo, notificações e normalização dos argumentos.
As anotações de ferramentas MCP são uma fronteira de segurança?
Não. A especificação do Model Context Protocol descreve esses campos como indicações de comportamento, não como substitutos para uma decisão de segurança do cliente. Um servidor malicioso, desatualizado ou simplesmente equivocado pode publicar metadados enganosos.
Quais ferramentas MCP ainda devem exigir aprovação?
Mantenha a aprovação quando uma chamada puder alterar o estado do negócio, expor dados sensíveis, iniciar uma tarefa dispendiosa ou alcançar um sistema fora do alvo de teste controlado. Um nome inofensivo e uma anotação otimista não eliminam esses riscos.
Como testo se uma ferramenta é idempotente?
Use uma conta descartável ou um fixture local, registre o estado inicial, chame a ferramenta duas vezes com argumentos idênticos e compare o estado resultante com as evidências externas. Repita o teste com uma entrada malformada e uma solicitação interrompida, pois os caminhos de nova tentativa costumam revelar os danos.
Uma ferramenta destrutiva pode ser segura quando nada muda?
Uma ferramenta de exclusão pode não ser destrutiva para um registro específico porque o registro já não existe, mas continuar sendo destrutiva em geral. O desenho da aprovação deve classificar a capacidade e o contexto do alvo, não apenas o resultado de uma chamada.
O que acontece quando faltam anotações MCP?
Considere a omissão de forma conservadora e examine a semântica dos campos antes de criar automações. Nas anotações atuais de ferramentas MCP, um destructiveHint omitido assume true, enquanto readOnlyHint e idempotentHint omitidos assumem false.
Como uma chamada de API somente leitura ainda pode ter efeitos colaterais?
Uma ferramenta pode retornar uma resposta plausível, criar uma entrada de auditoria, atualizar um campo de último acesso, disparar um webhook ou cobrar uma conta sem alterar o recurso mais evidente. Verifique registros posteriores, chamadas de rede e logs do sistema, não apenas a resposta da ferramenta.
Como devo criar aprovações para agentes autônomos de programação?
Aprove a execução do agente somente depois de identificar a autoridade de assinatura do código e o escopo pretendido. Reserve a aprovação por chamada para credenciais ou ações em que cada uso precise de uma decisão humana. Os metadados podem ajudar a redigir o texto da aprovação, mas não devem decidir o resultado.
Como o Sallyport ajuda a controlar ações MCP?
O Sallyport mantém as credenciais fora do agente e permite que uma pessoa aprove uma sessão ou exija aprovação a cada uso de uma credencial selecionada. Isso apoia um esquema baseado na ação realmente executada, em vez de uma anotação fornecida pelo servidor.