Requisições MCP aninhadas: rastreie cada ação externa com segurança
Requisições MCP aninhadas precisam de mais do que logs de ferramentas. Aprenda a rastrear chamadas pai, novas tentativas, aprovações e as ações externas que os agentes realmente acionam.

Um agente pode chamar uma ferramenta que chama outra ferramenta, que pede a um auxiliar para resolver um ambiente, que finalmente envia uma requisição HTTP ou executa um comando SSH. Se seus registros terminam no primeiro nome de ferramenta, você tem uma história sobre a intenção. Não tem um relato do que aconteceu fora do processo.
As requisições MCP aninhadas precisam de um rastreamento que siga a cadeia causal até cada ação externa que usa credenciais. Isso significa registrar mais do que uma transcrição do agente e mais do que um log de requisição convencional. Você precisa de um grafo capaz de responder: qual execução causou esta chamada de saída, ao que a chamada foi resolvida, quem a aprovou e ela realmente foi executada?
Já vi equipes tratarem um rastreamento organizado de ferramentas como prova de que seus controles funcionam. Então surge um incidente perguntando qual chamada alterou um recurso de produção, e o rastreamento diz apenas release_service. Nomes amigáveis servem para operadores. Evidências de auditoria precisam mostrar a operação concreta.
A ação externa é o registro que conta
A última chamada de uma cadeia aninhada costuma carregar a consequência, por isso precisa de seu próprio registro durável, mesmo quando todas as chamadas anteriores já têm um span. Uma ferramenta que verifica o nome de uma branch pode ser inofensiva. Um auxiliar posterior que usa o resultado para chamar um endpoint de implantação é outro evento.
Separe três coisas que as equipes costumam juntar:
- Uma invocação de ferramenta é uma solicitação para executar uma capacidade nomeada com argumentos.
- Um span de rastreamento descreve uma unidade de trabalho e seu lugar em um grafo causal.
- Uma ação externa é uma operação concreta que atravessa um limite de confiança, como uma requisição HTTP com credenciais injetadas ou um comando SSH em um host remoto.
Errar essa distinção produz duas falhas opostas. Algumas equipes criam um registro de auditoria para cada chamada de função interna. O diário se enche de ruído e ninguém consegue identificar as poucas chamadas que mudaram alguma coisa. Outras registram apenas uma linha final de sucesso. Elas perdem a cadeia que explica qual decisão do agente, consulta e aprovação causaram a chamada.
Dê a uma ação externa um action_id estável. Conecte-o ao span que a iniciou, mas não use o ID do span como identidade. Um span pode abranger a preparação local, uma tentativa HTTP, um redirecionamento e a análise da resposta. Esses detalhes são úteis. O registro da ação deve continuar compreensível mesmo depois que alguém alterar a implementação.
Um registro de ação precisa descrever o destino depois da resolução. Registrar environment=production ou target=customer-api não basta. Armazene o host, a porta, o protocolo, o método da requisição ou o comando SSH resolvidos, além da referência da credencial selecionada pelo executor. Armazene referências de segredos, nunca os valores dos segredos.
Essa distinção também muda a forma de avaliar o sucesso. Uma ferramenta intermediária pode retornar ok porque colocou o trabalho em uma fila. Uma camada inferior pode falhar antes de abrir uma conexão. Registre separadamente o resultado do span da ferramenta intermediária e o resultado da ação externa. Esses fatos podem divergir sem que nenhum dos registros esteja errado.
O MCP não fornece todo o seu grafo de chamadas
O MCP oferece a clientes e servidores um protocolo para descobrir e chamar ferramentas. Ele não obriga cada implementação a expor um grafo interno de chamadas. Um host pode orquestrar vários servidores. Um servidor pode chamar auxiliares locais. Uma ferramenta pode iniciar um trabalho que continua depois da resposta. Seu projeto de auditoria precisa considerar explicitamente essas formas.
A especificação do Model Context Protocol descreve tools/call como uma solicitação do cliente para o servidor, destinada a uma ferramenta nomeada e seus argumentos. Isso é um contrato de interface, não um contrato de rastreamento. O protocolo não transforma uma chamada de função local em um evento filho observável e não define um campo de pai universal que todo intermediário precise preservar.
Isso importa quando uma equipe diz que uma ferramenta «chamou outra ferramenta MCP». Às vezes, a frase significa uma segunda chamada real de protocolo. Às vezes, significa que um servidor invocou uma função de biblioteca com nome semelhante. Às vezes, um host de agente recebeu um resultado, raciocinou sobre ele e fez uma nova chamada. O grafo resultante pode parecer parecido, mas seus limites de confiança são diferentes.
Trate estes casos como tipos de aresta distintos nos registros:
protocol_callconecta uma requisição de cliente MCP a uma invocação de ferramenta no servidor.local_callconecta código dentro de um único processo confiável.delegated_jobconecta uma solicitação ao trabalho que outro worker executará mais tarde.external_actionconecta um span a uma operação HTTP ou SSH.
Não infira o tipo de aresta a partir do nome da ferramenta. Registre-o no ponto em que a transferência acontece. É ali que você sabe se identidade, credenciais e regras de cancelamento atravessaram para outro processo.
Um trabalho atrasado merece atenção especial. Se uma ferramenta coloca uma tarefa em uma fila e retorna, mantenha o ID de rastreamento de origem e o ID da execução raiz junto com os dados do trabalho. Quando um worker executar a tarefa mais tarde, crie um novo span para essa execução e aponte-o para o plano de ação original. Não finja que o worker permaneceu dentro da requisição original. Seu tempo de execução, sua identidade e seu estado de autorização podem ter mudado.
Crie identificadores em cada limite de confiança
Um rastreamento só ajuda se cada participante puder vincular seu trabalho à mesma cadeia causal sem aceitar como fato um histórico falsificado. Gere seus próprios identificadores quando uma requisição entrar em um componente sob seu controle e preserve o contexto recebido como entrada de diagnóstico não confiável, a menos que um par autenticado o tenha fornecido.
A recomendação W3C Trace Context define o cabeçalho traceparent com uma versão, um ID de rastreamento de 32 caracteres hexadecimais, um ID de pai de 16 caracteres hexadecimais e flags. O OpenTelemetry usa esse formato amplamente. Use-o quando HTTP ou outro transporte puder carregar cabeçalhos, porque as ferramentas de rastreamento existentes o entendem. Não confunda compatibilidade com um modelo de auditoria.
No MCP sobre stdio, talvez não exista nenhum cabeçalho HTTP. Coloque um contexto equivalente no envelope da aplicação ou mantenha o contexto no processo que despacha a chamada. O mecanismo importa menos do que duas propriedades: cada operação filha precisa conhecer seu pai imediato, e o componente receptor precisa registrar quem lhe entregou o contexto.
Uma estrutura mínima de evento pode ser assim:
{
"event_id": "evt_01J8...",
"time": "2025-03-08T14:32:11.214Z",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"parent_span_id": "b7ad6b7169203331",
"root_run_id": "run_8d43",
"edge_type": "external_action",
"actor": {
"kind": "agent_process",
"identity": "signed-process-identity"
},
"action": {
"action_id": "act_5f17",
"channel": "http",
"method": "POST",
"host": "deploy.internal.example",
"path_template": "/v1/releases/{name}",
"credential_ref": "ops-deploy"
},
"outcome": {
"state": "sent",
"http_status": 202
}
}
O timestamp acima é um exemplo de estrutura, não um formato de retenção recomendado. Em um sistema real, use um formato de relógio que seu verificador de logs consiga analisar de maneira consistente. Mantenha endpoints e argumentos brutos fora das exportações amplas de telemetria quando contiverem nomes de tenants, caminhos de repositórios ou dados pessoais. Um template acompanhado de um registro forense protegido costuma dar aos operadores contexto suficiente sem copiar material sensível para cada painel.
Um serviço receptor não deve simplesmente confiar em parent_span_id porque um agente o forneceu. Crie um novo span local, registre o valor recebido e inclua um campo como upstream_context_source=authenticated_mcp_client ou upstream_context_source=unverified_input. Essa pequena distinção impede que um chamador malicioso vincule sua ação a uma execução legítima depois do fato.
Registre a resolução, não apenas os argumentos da ferramenta
A ação solicitada pelo agente e a ação executada pelo executor podem mudar depois de templates, padrões, aliases, redirecionamentos, consultas ao ambiente e seleção de credenciais. Seus registros precisam preservar os dois lados, porque essa diferença costuma revelar um projeto inseguro.
Considere uma chamada de agente com estes argumentos:
{
"tool": "publish_release",
"arguments": {
"environment": "prod",
"release": "2025.03.08-rc2"
}
}
Uma ferramenta pode traduzir prod em uma URL base, selecionar uma credencial, transformar a string da versão em um caminho de requisição e adicionar cabeçalhos. A chamada inicial não prova o destino. Uma cadeia completa deve expor uma transição como esta:
- O agente invoca
publish_releasena execuçãorun_8d43. - A ferramenta resolve
prodpara um endpoint permitido específico e seleciona a referência de credencialops-deploy. - O executor registra
act_5f17imediatamente antes de enviar a requisição. - O executor registra a resposta ou o erro de transporte associado a essa ação.
- A ferramenta intermediária retorna um resultado que cita
act_5f17sem expor credenciais.
Essa sequência dá ao investigador um caminho entre a solicitação do agente e uma operação de rede. Também dá ao aprovador algo concreto para inspecionar antes que o executor envie qualquer coisa.
Não registre cabeçalhos de autorização completos, cookies, comandos privados ou corpos de requisição arbitrários como substituto de uma boa modelagem. As pessoas fazem isso sob pressão porque uma cópia bruta resolve o problema de depuração do dia. Ela cria o vazamento de credenciais de amanhã. Em vez disso, capture uma referência de credencial, o nome do cabeçalho, um resumo criptográfico do corpo, campos não secretos selecionados e um status explícito de mascaramento.
O SSH exige a mesma disciplina. Um registro que diz ssh deploy é vago demais se o auxiliar depois expandir um alias de host, selecionar uma identidade e construir um comando remoto. Registre o host e a porta resolvidos, o nome da conta, a referência da identidade, um template ou resumo criptográfico do comando e o status de saída. Se o comando incluir dados sensíveis, guarde uma cópia forense protegida apenas quando houver uma razão clara e uma regra de retenção definida.
Uma nova tentativa é outra tentativa, não uma nota de rodapé
Novas tentativas e ramificações transformam uma árvore simples em um grafo. Se você as força a caber em um único span com um campo final success, apaga as informações que explicam efeitos colaterais duplicados e falhas parciais.
Use três IDs quando uma operação puder ser repetida: um ID de rastreamento para a execução geral, um ID de ação para a operação lógica pretendida e um ID de tentativa para cada envio efetivo. Cada tentativa recebe seu próprio span. O registro da ação aponta então para todas as tentativas.
Suponha que uma ferramenta envie uma requisição de versão, expire o tempo limite depois que o serviço remoto a aceitou e tente novamente. A segunda requisição pode criar a mesma versão duas vezes se o endpoint remoto não tiver tratamento de idempotência. Um 200 final diz muito pouco. Seu log deve mostrar que a primeira tentativa chegou à rede, terminou localmente por timeout e que a segunda recebeu uma resposta.
Use um token de idempotência sempre que o destino oferecer suporte a ele. Derive-o do ID da ação lógica, não de um ID de span temporário. Assim, o serviço remoto reconhece uma duplicata mesmo que seu executor reinicie ou sua biblioteca de rastreamento gere novos spans.
trace_id=4bf92f... action_id=act_5f17 attempt=1 state=timeout bytes_sent=418
trace_id=4bf92f... action_id=act_5f17 attempt=2 state=completed http_status=200
bytes_sent ajuda a distinguir uma falha de conexão antes da transmissão da requisição de um timeout depois que o cliente enviou os dados, mas não prova o que o serviço remoto confirmou. Registre essa incerteza. Não rotule a primeira tentativa como failed de uma forma que sugira que o lado remoto não fez nada.
O trabalho paralelo precisa de spans irmãos, não de um campo de rastreamento mutável compartilhado que os workers sobrescrevem. Se uma ferramenta de planejamento chamar quatro verificações de ambiente, crie quatro spans filhos e quatro resultados separados. Se duas ramificações levarem a ações externas, emita dois IDs de ação. Um operador precisa conseguir revogar ou investigar uma ramificação sem confundi-la com sua irmã.
A aprovação deve estar vinculada à operação resolvida
Uma aprovação humana só é útil quando o revisor consegue ver a operação que acontecerá depois da resolução. Aprovar um rótulo amplo como deploy dá à pessoa quase nada para avaliar, especialmente quando ferramentas aninhadas selecionam o host e a credencial reais mais tarde.
Monte os dados da aprovação a partir do registro da ação externa pendente: canal, destino resolvido, formato da operação, referência da credencial, identidade do processo e uma descrição concisa do impacto. Mantenha os IDs de rastreamento e de ação na decisão de aprovação. Quando o executor enviar a requisição, ele precisa provar que consumiu a mesma ação aprovada, não apenas uma aprovação da mesma execução do agente.
Não aprove uma cadeia inteira só porque sua primeira ferramenta parecia inofensiva. Uma cadeia pode começar com find_release e terminar com um comando SSH que modifica um host. Se o projeto permitir que uma resolução posterior amplie o que a cadeia pode fazer, exija uma nova decisão no limite de saída.
Isso não significa perguntar a uma pessoa sobre cada concatenação de strings dentro de uma ferramenta. Significa colocar a decisão no ponto em que uma capacidade sai do caminho de execução confiável. Esse limite oferece às pessoas uma solicitação compreensível e dá ao seu log uma conexão durável entre consentimento e efeito.
O Sallyport adota essa abordagem em seus canais HTTP e SSH compatíveis: mantém as credenciais em seu cofre, executa a ação por conta própria e pode exigir aprovação para cada uso de uma credencial selecionada. O ponto de integração útil é a ação externa resultante, não a afirmação de um agente sobre o que seu auxiliar aninhado pretendia.
Os registros de aprovação também precisam de regras de expiração e vinculação. Vincule uma decisão ao ID da ação, ao destino resolvido, à referência da credencial e ao resumo criptográfico dos argumentos. Se algum desses elementos mudar entre a solicitação e a execução, descarte a decisão e peça outra. Um token de aprovação reutilizável que acompanhe o processo do agente em chamadas não relacionadas acabará autorizando algo que ninguém leu.
Um diário de auditoria precisa de ordem e verificação
Logs convencionais de aplicação ajudam a diagnosticar uma falha, mas raramente informam se alguém removeu a linha inconveniente. Para ações feitas por agentes autônomos, preserve a ordem dos eventos e torne perceptível qualquer alteração posterior.
Uma cadeia simples de hashes registra os bytes serializados de forma canônica de cada evento, o hash do evento anterior e o novo hash do evento. Um verificador começa no primeiro registro retido e recalcula cada elo. Se um invasor alterar, inserir ou remover um registro no meio, a verificação falhará no ponto afetado.
A serialização canônica importa. Se um processo ordenar os campos JSON e outro não, registros equivalentes produzirão hashes diferentes. Defina a ordem dos campos, a normalização Unicode, a precisão dos timestamps, o tratamento de campos ausentes e a codificação dos bytes. Teste essas regras em todas as linguagens que escrevem eventos. A maioria das cadeias de auditoria quebradas falha aqui, não na função de hash.
Um verificador deve produzir resultados que um operador possa usar:
$ audit verify journal.events
records_checked: 1842
first_sequence: 91001
last_sequence: 92842
chain: valid
signature: valid
Quando encontrar danos, ele deve identificar a primeira sequência inválida e comparar o hash predecessor esperado com o observado. Não deve tentar reparar o arquivo silenciosamente. O reparo destrói as evidências sobre o que deu errado.
As cadeias de hashes não resolvem todas as ameaças. Alguém que controle o material de assinatura e o armazenamento pode reescrever uma história alternativa completa. Checkpoints assinados periodicamente e armazenados fora do controle normal do gravador reduzem esse risco. O mesmo vale para separar os caminhos de acesso de gravação e leitura. Seja preciso sobre a proteção existente, em vez de chamar qualquer log de imutável.
Mantenha o evento da ação próximo do ponto de execução. Um coletor em segundo plano que recebe lotes depois do fato pode perder o único registro que prova que uma chamada de saída ocorreu. O executor deve anexar um registro «planned» antes da transmissão e um registro de conclusão imediatamente depois de receber um resultado. Se ele travar entre os dois, o par incompleto informa que a ação pode ter escapado.
Teste o rastreamento contra falhas que as pessoas realmente causam
Um projeto de rastreamento só é confiável depois de sobreviver a contextos malformados, workers perdidos, envios duplicados e revogação pelo operador. Demonstrações do caminho feliz escondem justamente os limites que importam quando um agente se comporta de forma inesperada.
Execute uma pequena suíte de testes contra um endpoint ou host descartável. Faça cada teste verificar o grafo de auditoria, não apenas a resposta da ferramenta:
- Envie uma chamada aninhada com um ID de pai falsificado e confirme que o receptor marca o contexto como não verificado.
- Force um timeout depois que os bytes da requisição deixarem o cliente, tente novamente e confirme que as duas tentativas compartilham um ID de ação.
- Coloque um trabalho na fila, reinicie o worker e confirme que o span retomado se vincula à execução original sem fingir que rodou continuamente.
- Revogue a autorização depois do planejamento, mas antes da execução, e confirme que nenhum registro de ação externa chega a
sent. - Faça duas chamadas irmãs em paralelo e confirme que nenhuma ramificação adota o span pai ou o resultado da outra.
O terceiro teste detecta uma mentira comum nos logs. Os sistemas costumam informar uma única requisição ininterrupta, embora um worker tenha reiniciado horas depois sob uma identidade de processo diferente. Isso esconde quem realmente executou a ação. Registre a identidade e o horário de início do worker como fatos do novo span.
O quarto teste detecta outra falha conhecida. Uma ferramenta obtém autorização antes de resolver seu destino final e depois usa essa decisão desatualizada para a chamada resolvida. Seu teste deve alterar o destino ou a referência da credencial depois da aprovação e esperar que o executor a rejeite.
Não se contente com capturas de tela de um visualizador de rastreamento. Exporte os registros brutos, verifique a cadeia e escreva asserções sobre IDs de pai, IDs de ação, destinos, vínculos de autorização e resultados. O visualizador é uma conveniência. O fluxo de eventos é a evidência.
Construa primeiro o limite, depois preencha o grafo
Comece pelo código que executa operações HTTP e SSH. Faça com que ele aceite um contexto de rastreamento explícito, crie um registro de ação antes do envio, crie um registro de resultado depois e recuse a execução quando a autorização não estiver vinculada à operação resolvida.
Quando esse limite funcionar corretamente, instrumente as chamadas de ferramentas aninhadas acima dele. Você saberá quais informações cada pai precisa transmitir, porque o executor exigirá essas informações. Trabalhar na direção oposta costuma produzir árvores bonitas com nós finais não confiáveis.
Mantenha nomes de ferramentas, notas de planejamento e raciocínio do modelo separados do diário de ações externas. Eles podem ajudar alguém a entender por que uma execução aconteceu. Não substituem um registro de qual sistema remoto recebeu uma requisição, sob qual referência de credencial, depois de qual aprovação e com qual resultado. Essa é a cadeia de que você precisará quando a resposta do agente parecer plausível e o sistema remoto disser o contrário.
FAQ
O que devo auditar quando uma ferramenta MCP chama várias outras ferramentas?
Trate a operação final que usa credenciais como a ação principal do registro. A chamada MCP que iniciou a cadeia ainda é importante para o diagnóstico, mas não informa ao auditor se a cadeia criou um repositório, alterou um registro DNS ou reiniciou um serviço.
O MCP preserva automaticamente o rastreamento entre pais e filhos nas chamadas de ferramentas?
Não. O MCP define a descoberta e as chamadas de ferramentas, mas um host de agente ou servidor pode criar seu próprio grafo interno de chamadas. Seu sistema de rastreamento precisa capturar esse grafo, em vez de presumir que o protocolo o fornece.
Como propago um ID de rastreamento pelas ferramentas aninhadas de um agente?
Dê a cada chamada recebida um ID de rastreamento e um ID de span. Depois, faça cada chamada filha carregar o mesmo ID de rastreamento, usando o ID do span do chamador como pai. Gere um novo ID de span para cada operação, incluindo novas tentativas e ramificações paralelas.
Quais campos devem estar em um registro de auditoria de ação externa?
Registre a operação solicitada, o destino totalmente resolvido, a referência da credencial, a decisão de autorização, os horários, o resultado e um resumo seguro da resposta. Não coloque tokens de acesso, chaves privadas ou corpos de requisição sem mascaramento em um rastreamento apenas para facilitar a depuração.
As novas tentativas devem compartilhar o mesmo span da requisição original?
Uma nova tentativa precisa de seu próprio registro, porque cria outra requisição de saída e pode gerar outro efeito colateral. Conecte cada tentativa a um único ID de ação lógica para que o investigador veja tanto a operação pretendida pelo usuário quanto cada tentativa de rede feita para realizá-la.
Como devo rastrear chamadas paralelas de ferramentas MCP?
Sim, desde que cada ramificação receba seu próprio span e cada operação externa tenha seu próprio registro de ação. Ter apenas um pai compartilhado não basta, porque uma ramificação bem-sucedida pode esconder uma chamada irmã que falhou ou era perigosa.
Posso aprovar toda uma cadeia de ferramentas aninhadas com uma única solicitação?
A aprovação deve nomear a operação externa resolvida, não apenas o nome amigável da ferramenta. «Implantar» é amplo demais. «Enviar esta requisição assinada para este endpoint de produção» dá ao revisor algo concreto para avaliar.
Um agente pode falsificar as informações do pai do rastreamento?
Não trate os IDs de pai fornecidos pelo agente como verdade de auditoria. Mantenha-os como contexto de diagnóstico, se forem úteis, mas crie relações de rastreamento no servidor em cada limite de confiança e registre o processo ou principal autenticado que fez a chamada.
Como torno um log de ações MCP evidente contra adulterações?
Um log de eventos somente para gravação, com números de sequência, hashes dos registros anteriores e verificações periódicas, oferece evidências mais fortes do que logs de aplicação editáveis. Ele não torna uma máquina comprometida honesta, mas dificulta muito esconder exclusões ou reordenações posteriores.
Por onde uma equipe deve começar com o rastreamento de MCP aninhado?
Comece com uma ferramenta de limite que execute uma requisição HTTP ou um comando SSH. Exija que ela emita um ID de ação e o contexto de rastreamento. Quando esse registro estiver confiável, adicione os spans internos que explicam como a cadeia chegou até ele.