Voltar às atualizações
New releaseJul 21, 2026

mcpsnoop v0.13.0

Wireshark para MCP. Um proxy transparente que mostra todas as chamadas reais de ferramentas entre seu cliente de IA e seus servidores MCP, ao vivo no seu terminal.

Compartilhar

mcpsnoop

Wireshark para MCP. Um proxy transparente que mostra cada chamada de ferramenta real entre o seu cliente de IA e os seus servidores MCP, ao vivo no seu terminal.

CI Go Reference MIT

mcpsnoop demo

O problema

O MCP Inspector oficial conecta-se como seu próprio cliente, então ele nunca vê o que o seu cliente (Cursor, Claude Code, Codex) realmente envia ao seu servidor. E qualquer coisa que espera por uma requisição chegar não consegue mostrar a chamada que o modelo nunca fez, ou fez com os argumentos errados. Quando uma ferramenta silenciosamente não é chamada, as capacidades não se alinham, ou uma chamada simplesmente trava, você fica vasculhando logs e tentando adivinhar.

O mcpsnoop fica no caminho real dos dados. Envolva o comando do seu servidor com ele e observe cada quadro JSON-RPC ao vivo, enquanto o seu cliente e servidor reais conversam.

Início rápido

Veja agora mesmo, sem nada para configurar.```bash mcpsnoop demo

Para usar de verdade, envolva o seu servidor na configuração MCP do seu cliente.```json
{
  "mcpServers": {
    "my-server": {
      "command": "mcpsnoop",
      "args": ["--", "node", "build/index.js"]
    }
  }
}

Tudo o que vem depois de -- é o comando que normalmente inicia o seu servidor. Substitua pelo que você já usa, como python server.py, npx -y @scope/server, ou um binário compilado.

No Claude Desktop você não precisa fazer essa edição manualmente.```bash mcpsnoop wrap my-server # route my-server through mcpsnoop mcpsnoop unwrap my-server # put it back

`wrap` localiza o `claude_desktop_config.json`, copia-o para
`claude_desktop_config.json.mcpsnoop.bak` na primeira vez e reescreve apenas
a entrada desse único servidor, para que a sua formatação e todos os outros
servidores sejam mantidos intactos. Dentro da entrada reescrita, as chaves
voltam à ordem alfabética. `unwrap` restaura o arquivo e remove o backup
assim que nenhum servidor estiver mais envolvido. Reinicie o Claude Desktop
após qualquer um dos dois, pois os servidores MCP são iniciados apenas uma
vez, na inicialização.

Em seguida, use o seu cliente como de costume e abra a interface.```bash
mcpsnoop

Sem flags, sem caminhos de socket, sem ordem de inicialização para lembrar. O shim e a UI encontram-se por conta própria, e a UI recupera sessões passadas do disco.

Para um servidor streamable-HTTP, execute o mcpsnoop como um proxy reverso.```bash mcpsnoop http --target http://localhost:3000/mcp --listen :7000

O status HTTP de cada resposta aparece no fluxo, então uma resposta que não traz
nenhuma mensagem JSON-RPC própria ainda é um frame visível em vez de nada: o
desafio 401, o 403 em uma Origin rejeitada, o 202 que confirma uma notificação
e o 502 quando o destino não pode ser alcançado de forma alguma. O cabeçalho
`WWW-Authenticate` de um 401 é mantido na íntegra e exibido no inspetor, pois
nomeia o esquema de autenticação e os metadados do recurso para onde ir em
seguida. Filtre por status com `status:401` no TUI, ou por qualquer falha com
`status:err`. Um 4xx ou 5xx conta como erro, então uma execução padrão de
`mcpsnoop check` falha nele.

Sem servidor próprio? [Experimente de verdade](https://github.com/kerlenton/mcpsnoop/blob/HEAD/docs/TRY_IT.md) contra um
servidor de teste publicado, usando seu próprio cliente. Para inspecionar uma
sessão depois que ela aconteceu, veja [revisar sessões passadas a partir dos
logs](https://github.com/kerlenton/mcpsnoop/blob/HEAD/docs/POST_MORTEM.md).

### Arquivo de configuração

Se você reutilizar os mesmos flags de shim em um projeto, coloque-os em um
arquivo `.mcpsnoop.toml` no diretório de trabalho atual.```toml
label = "filesystem"
trace-file = "trace.jsonl"
redact-secrets = true
redact-key = "token,authorization"
redact-value = "sk-[A-Za-z0-9]+"
redact-path = "$.params.arguments.password"
no-trace = false

Repita redact-key, redact-value e redact-path em linhas próprias para adicionar mais de um de cada.

Essas são todas as chaves que ele suporta.

O arquivo é procurado apenas no diretório de trabalho atual, não nos diretórios pais.

Flags explícitas de linha de comando substituem os valores do arquivo de configuração.

Comandos

ComandoO que faz
mcpsnoop -- <server>encapsula um servidor stdio como um shim transparente
mcpsnoopabre a TUI ao vivo
mcpsnoop http --target <url>faz proxy de um servidor streamable-HTTP
mcpsnoop exportrenderiza uma sessão em json, html, text, har ou otlp
mcpsnoop checkfaz a CI falhar em erros, quadros inválidos, avisos, incompatibilidades de roteamento, chamadas travadas ou resultados atrasados
mcpsnoop baselineinspeciona, aceita ou redefine definições de ferramentas confiáveis
mcpsnoop diffcompara ferramentas e chamadas entre duas sessões capturadas
mcpsnoop openabre uma sessão salva na TUI
mcpsnoop pruneexclui logs de sessões salvas mais antigos que um limite
mcpsnoop wrap <server>encaminha um dos servidores do Claude Desktop através do mcpsnoop
mcpsnoop unwrap <server>restaura a entrada desse servidor ao estado original
mcpsnoop remote <user@host>imprime o comando do túnel SSH
mcpsnoop demoreproduz uma sessão roteirizada

Execute mcpsnoop help para a lista completa, ou mcpsnoop help <command> para as flags de um.

Como se compara

MCP Inspectormcpsnoop
Vê o tráfego real do cliente e do servidornãosim
Sinaliza chamadas travadas e erros de streamnãosim
Sinaliza saída extra que corrompe o streamnãosim
Sinaliza quadros JSON-RPC malformadosnãosim
Detecta divergência nas definições de ferramentas após a aprovaçãonãosim
Interface de terminal interativanãosim
Zero configuração, sem flags ou ordenaçãonãosim
Inspetor de capacidadesparcialsim
Reproduz uma chamada capturadanãosim
Exportação de sessão (json / html / text / otlp)nãosim
Binário único, sem dependências de runtimenãosim

Instalação

Go```bash

go install github.com/kerlenton/mcpsnoop/cmd/mcpsnoop@latest

### Homebrew```bash
brew install mcpsnoop

Prebuilt binaries for every platform are on the Releases page.

Shell completions

mcpsnoop ships completions for bash, zsh, fish, and PowerShell. Run mcpsnoop completion <shell> --help for the setup steps, which cover enabling completion and the install path for your OS.

Como funciona

o mcpsnoop fica no caminho entre o seu cliente de IA e os seus servidores MCP, copiando cada frame JSON-RPC para uma interface de terminal ao vivo

O mcpsnoop reúne dois papéis em um único binário. mcpsnoop -- <server> é o shim transparente que o seu cliente inicia, encaminhando bytes exatamente como estão enquanto envia uma cópia de cada frame para o hub. mcpsnoop sem argumentos é esse hub e a sua TUI ao vivo. Eles se conectam por meio de um socket bem conhecido e de logs em disco, portanto nenhum deles precisa iniciar primeiro.

O hub carrega as 100 sessões salvas mais recentes por padrão, mantendo o trabalho de inicialização limitado sem excluir rastros mais antigos. Use mcpsnoop --history-limit N para escolher outro limite, ou mcpsnoop --history-limit 0 para carregar todo o histórico. Sessões mais antigas continuam disponíveis por meio de mcpsnoop open <session-id> e mcpsnoop export <session-id>.

O limite de histórico restringe o que é carregado; mcpsnoop prune restringe o que é mantido. Ele exclui os logs de sessões salvas mais antigos que um ponto de corte e nunca executa por conta própria.```bash mcpsnoop prune --older-than 30d --dry-run # list what would go, remove nothing mcpsnoop prune --older-than 30d # delete after confirming mcpsnoop prune --older-than 72h --yes # skip the prompt in a script

`--older-than` é obrigatório (não há padrão que excluiria qualquer coisa) e
aceita uma contagem de dias como `30d` ou uma duração Go como `72h`. As
linhas de base das ferramentas são deixadas intactas, pois uma linha de base é identificada pelo rótulo do servidor, e não pela sessão.

Como ele fica no pipe real, e não à margem como o Inspector, ele
vê exatamente o que o seu cliente e servidor reais dizem um ao outro, seja qual for a
linguagem em que o servidor foi escrito.

## Atalhos de teclado

| Tecla | Ação | | Tecla | Ação |
|---|---|---|---|---|
| `enter` | inspecionar / aprofundar | | `/` | filtrar |
| `esc` | voltar | | `:` | comando |
| `j` / `k` | mover | | `r` | repetir chamada |
| `g` / `G` | topo / fundo | | `c` | capacidades |
| `ctrl-f` / `ctrl-b` | página | | `s` | resumo da ferramenta |
| `p` | pausar | | `y` | copiar |
| `shift`+`<key>` | ordenar por coluna | | `e` | exportar |
| `ctrl-d` | excluir sessão | | `f` | seguir |
| `?` | ajuda | | | |

Pressione `?` no aplicativo para a lista completa.

## Filtrando o fluxo

Pressione `/` em uma sessão e combine tokens separados por espaços, com E lógico. Texto
simples corresponde ao método, à ferramenta, ao id e ao payload.

| Token | Filtra por | Exemplo |
|---|---|---|
| `tool:` | nome da ferramenta | `tool:search` |
| `method:` | método JSON-RPC | `method:tools/call` |
| `id:` | id da solicitação e qualquer nova tentativa que o continue | `id:7` |
| `task:` | id da tarefa | `task:01J...` |
| `dir:` | direção (`c2s`, `s2c`) | `dir:s2c` |
| `kind:` | tipo de frame (`req`, `resp`, `notify`, `stderr`, `invalid`) | `kind:invalid` |
| `status:` | resultado da chamada (`ok`, `error`, `cancel`, `late`, `cancelled`, `pending`, `bad`, `warn`, `mismatch` ou um status HTTP como `401`) | `status:error` |

Empilhe tokens para ser específico.```text
tool:search status:pending        # in-flight calls to one search tool
status:cancel                     # calls the client gave up on (status:cancelled is a cancelled task)
status:late                       # results that arrived after the cancellation
method:tools/call status:error    # tool calls that failed
dir:s2c kind:req                  # server-initiated requests (servers before 2026-07-28)

O último só encontra algo num servidor que fale 2025-11-25 ou anterior. A revisão de 2026-07-28 removeu pedidos iniciados pelo servidor, e um servidor que precise de algo do cliente agora responde ao próprio pedido do cliente pedindo isso, e então o cliente tenta novamente. O mcpsnoop liga essas tentativas de volta ao pedido que elas continuam, de modo que a troca é lida como uma única chamada em vez de várias.

Exportando sessões

Transforme qualquer sessão capturada em um arquivo portátil.```bash mcpsnoop export -T json|html|text|har|otlp [-o file|-] [session-id|log.jsonl|-]

| Formato | O que você obtém |
|---|---|
| `json` | chamadas correlacionadas, contagens por ferramenta e latência p50/p95/p99, chamadas mais lentas, capacidades e quadros brutos |
| `html` | um arquivo de navegador autocontido com pesquisa e JSON recolhível |
| `text` | um despejo de texto simples e organizado |
| `har` | uma entrada por chamada correlacionada, abrível nas ferramentas de desenvolvedor do navegador e em qualquer outra ferramenta que leia HAR |
| `otlp` | JSON OTLP com um span por chamada correlacionada; o contexto de rastreamento W3C une os rastreamentos do chamador; caso contrário, um rastreamento é usado por sessão |

MCP não é HTTP, portanto a URL, o código de status e os tempos de uma entrada HAR são um
mapeamento proposital de cada chamada, e não uma transcrição de rede.

Para OTLP, o `_meta.traceparent` de uma solicitação fornece o rastreamento e os IDs
de span pai daquela chamada, e `_meta.tracestate` acompanha o span. Quando o traceparent está
ausente ou inválido, o mcpsnoop mantém o rastreamento derivado da sessão e não carrega estado.
O mcpsnoop observa em vez de participar, portanto não adiciona uma entrada de fornecedor própria
e repassa o estado do chamador sem alterações.```bash
mcpsnoop export -T html -o out.html                    # an HTML file to open in a browser
mcpsnoop export -T text server.py-48213-7f3a1c9e2b04   # a specific session, as text
mcpsnoop export -T json | jq                           # the newest session, piped to jq
mcpsnoop export -T har -o session.har                  # a HAR file to open in browser devtools
mcpsnoop export -T otlp -o trace.json                  # import into an OTLP-compatible tracing backend

Omita -o para escrever em stdout e omita a sessão para usar a mais recente, ou passe - para ler JSONL a partir de stdin. Na TUI, pressione e para exportar a sessão selecionada como HTML, ou execute :export json|html|text|har|otlp [path] a partir do modo de comando.

Para limpar uma captura existente antes de inspecioná-la ou compartilhá-la, passe os mesmos flags de redação usados durante a captura para export ou open:```bash mcpsnoop export session.jsonl --redact-secrets --redact-key project_token -o shared.json mcpsnoop open session.jsonl --redact-path '$.params.arguments.password'

Essas flags reescrevem o arquivo exportado ou a visualização TUI em memória, nunca o
JSONL de origem. `export` recusa uma saída que nomeie o mesmo arquivo que sua entrada,
e grava por meio de um arquivo temporário que é renomeado para o lugar, de modo que uma execução que
falha deixa o arquivo anterior intacto.

O `inputSchema` e o `outputSchema` de uma ferramenta, como anunciados em um resultado
de `tools/list`, não são alterados por `--redact-key` e `--redact-secrets`. Um nome dentro de um
esquema é uma declaração de tipo em vez de um valor; o nome em si permanece no
log de qualquer forma, e limpar o subesquema sob uma propriedade chamada `token`
levaria junto as próprias verificações da ferramenta. A exceção vale apenas para essa posição,
então um argumento que por acaso se chame `inputSchema` é limpo como qualquer
outro, e a limpeza para em `default`, `const`, `examples` e `enum`, que contêm
dados em vez de estrutura. Use `--redact-path` para nomear algo dentro de um
esquema, ou `--redact-value`, que corresponde ao texto onde quer que esteja, exceto nas
duas palavras-chave que o mcpsnoop analisa, `type` e `x-mcp-header`.

O que cada flag alcança difere, então confira o resultado em vez de presumir. Todas
as quatro limpam cargas úteis JSON-RPC, e `--redact-key`, `--redact-path` e
`--redact-secrets` alcançam apenas essas. Somente `--redact-value` também limpa o stderr,
outro texto não JSON e o interior de uma string. Um cabeçalho `Mcp-Param-*` é
limpo junto com o valor do corpo que ele espelha; os outros metadados do envelope,
rótulos do servidor, `Mcp-Name`, `Mcp-Method` e o status HTTP, são deixados como
capturados. A redação é de melhor esforço, então use um caminho de saída separado e leia o
resultado antes de compartilhá-lo.

### Transmita chamadas concluídas para um coletor OTLP

Envie spans enquanto o proxy está em execução apontando-o para um endpoint OTLP/HTTP JSON
de traces. Repita `--otlp-header` para autenticação do coletor ou cabeçalhos de
tenant.```bash
mcpsnoop \
  --otlp-endpoint http://localhost:4318/v1/traces \
  --otlp-header "Authorization=Bearer $OTLP_TOKEN" \
  -- node build/index.js

mcpsnoop http \
  --target http://localhost:3000/mcp \
  --otlp-endpoint http://localhost:4318/v1/traces

A entrega é de melhor esforço e nunca bloqueia o tráfego MCP proxyado. Se o coletor estiver indisponível, o mcpsnoop tenta novamente em segundo plano e descarta novos quadros de rastreamento quando sua fila limitada está cheia. O registro de sessão JSONL normal permanece como o registro durável.

Comparando sessões

Compare duas sessões salvas por id ou caminho JSONL.```bash mcpsnoop diff before-session after-session mcpsnoop diff old.jsonl new.jsonl

O relatório mostra ferramentas que foram adicionadas ou removidas, alterações na descrição e no `inputSchema`, chamadas de ferramenta correspondentes cujo status mudou e mudanças notáveis de duração. As chamadas são correspondidas por nome e argumentos da ferramenta, portanto chamadas reordenadas ainda são comparadas corretamente. Por padrão, as mudanças de duração devem diferir em pelo menos 100 ms e 2x; use `--duration-threshold` e `--duration-ratio` para ajustar esses limites.

Passe `--exit-code` para condicionar a CI a regressões: ele sai com código não zero quando a sessão posterior remove uma ferramenta, altera a descrição, o título, o esquema de entrada, o esquema de saída ou as anotações de uma ferramenta, tem uma chamada cujo status piorou ou fica mais lenta. Melhorias (ferramentas adicionadas, chamadas corrigidas, acelerações) ainda saem com zero, assim como uma mudança de ícone, que altera a aparência de uma ferramenta sem mudar o que ela faz.

## Verificando sessões em CI

Condicione a execução de um agente gravado a erros, corrupção de fluxo, avisos de protocolo, incompatibilidades de cabeçalho de roteamento, chamadas que nunca receberam resposta, quadros descartados que deixam a captura incompleta, deriva de definição de ferramenta ou uso de recursos de protocolo obsoletos.```bash
mcpsnoop check [--format text|junit|sarif] [--fail-on error,invalid,warn,mismatch,pending,late-result,drift,deprecated,incomplete,schema] [session-id|log.jsonl|-]

error, invalid e warn falham na verificação por conta própria. O restante é opcional. Passe um subconjunto separado por vírgulas para restringir apenas ao que um trabalho se preocupa, omita a sessão para verificar a captura mais recente, ou use - para ler JSONL da stdin.

SignalFails on
erroruma chamada respondida com um erro JSON-RPC, um resultado marcado como isError, ou uma tarefa que terminou em falha
invalidum quadro no canal de protocolo que não é JSON-RPC válido, geralmente um servidor registrando em stdout
warnum quadro que quebra uma expectativa definida pela especificação MCP ou JSON-RPC
mismatchum cabeçalho de roteamento em desacordo com o corpo, presente em um lote, ou ausente onde a revisão o exige
pendinguma solicitação ainda aberta quando a captura terminou, deixando o chamador esperando
late-resultuma resposta que chegou depois que sua solicitação foi cancelada
driftuma definição de ferramenta anunciada que muda após a linha de base ser aprovada
deprecatedum recurso que a especificação descontinuou
incompletequadros descartados a montante, o que faz de cada outra contagem um piso em vez de um total
schemaum esquema anunciado usando uma construção ou um dialeto que se propaga mal entre clientes

Cada sinal é contado esteja ou não servindo como porta de verificação, então uma execução mostra o que encontrou antes de você decidir o que deve falhar com base nele.``` session build-agent: errors=1 invalid=0 warnings=0 mismatches=0 pending=0 late_results=0 deprecated=0 missing_frames=0 schema_findings=1 schema findings: oneOf: search check failed: error

A contagem de quadros perdidos também viaja junto com os artefatos, então uma captura que
subestima a si mesma diz isso onde quer que seja aberta: `missing_frames` na
exportação JSON, `log.comment` no HAR e o atributo de recurso `mcpsnoop.session.missing_frames`
no OTLP.```bash
mcpsnoop check build-agent
mcpsnoop check --fail-on error,invalid artifacts/session.jsonl
mcpsnoop check --fail-on mismatch gateway-run.jsonl

Além das contagens de sinais, verifique a forma da execução. Elas se combinam entre si e com --fail-on, e qualquer falha resulta em código de saída diferente de zero.

FlagFalha quando
--max-duration <dur>uma ou mais chamadas de ferramenta concluídas excederam o orçamento; relata a contagem delas e a pior chamada
--expect-tool <name>a ferramenta nomeada nunca foi chamada (repetível)
--forbid-tool <name>a ferramenta nomeada foi chamada (repetível)

a contract for the run: search must run, delete must not, nothing over 2s

mcpsnoop check --expect-tool search --forbid-tool delete --max-duration 2s run.jsonl

### Reporte onde a CI já procura

`--format junit` escreve um `<testcase>` por sinal e sessão, e suas falhas
seguem a mesma seleção de `--fail-on` que a saída de texto.```yaml
- name: Check captured MCP session
  run: |
    mkdir -p test-results
    mcpsnoop check --format junit artifacts/session.jsonl > test-results/mcpsnoop.xml
- name: Upload mcpsnoop JUnit report
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: mcpsnoop-junit
    path: test-results/mcpsnoop.xml

--format sarif escreve um log SARIF 2.1.0 em vez disso. Enquanto o junit relata um agregado por sinal, o SARIF relata um resultado por achado, carregando a sessão, o Seq do frame e o texto de aviso ou deriva do próprio frame, e apontando para a linha do log da qual o frame foi decodificado. Um sinal nomeado em --fail-on é relatado no nível error e um fora dele no nível note, de modo que o relatório e o gate nunca divergem.

Um resultado aponta para o log com um caminho relativo ao diretório de trabalho, que o code scanning então resolve contra a raiz do repositório. O alerta é renderizado com as linhas ao redor somente quando esse caminho é um arquivo no commit analisado; portanto, uma captura que o workflow gerou em artifacts/ abre um alerta com a mensagem, a regra e o número da linha, mas sem a visão do código-fonte. Fazer commit de uma captura que você deseja renderizar por completo é a única forma de obter essa visão. Um log lido do diretório de estado ou do stdin não recebe nenhum caminho.

O code scanning rejeita um arquivo cuja execução contenha mais de 25.000 resultados e exibe apenas os 5.000 primeiros do que aceita; portanto, o relatório é limitado a 5.000: primeiro os achados que falharam no gate e, em seguida, um resultado mcpsnoop/report-truncated informando quantos foram deixados de fora. Os formatos text e junit permanecem completos.

Para colocar os achados na aba Security, entregue o log SARIF ao upload-sarif. O job precisa de security-events: write; caso contrário, o upload responde 403. O check retorna código não zero ao encontrar um achado; portanto, o passo de upload precisa de if: always() para executar de fato nas execuções que têm algo a relatar; continue-on-error entrega a decisão ao check do code scanning, que falha em alertas de nível error e pode ser definido como um check obrigatório. Remova-o se você preferir que o próprio passo de check seja o que torne o job vermelho.```yaml permissions:

required for all workflows

security-events: write

only required for workflows in private repositories

actions: read contents: read

steps:

  • name: Check captured MCP session continue-on-error: true run: mcpsnoop check --format sarif artifacts/session.jsonl > mcpsnoop.sarif
  • name: Upload mcpsnoop SARIF report if: always() uses: github/codeql-action/upload-sarif@v4 with: sarif_file: mcpsnoop.sarif category: mcpsnoop
### Detectar um cabeçalho de roteamento que discorda do corpo

No transporte streamable-HTTP, um gateway roteia com base em `Mcp-Method` e `Mcp-Name`
enquanto o servidor lê o corpo; portanto, um cabeçalho que discorda do corpo significa
que os dois estão vendo duas requisições diferentes. O sinal `mismatch` cobre esse caso,
um cabeçalho que viaja num lote que não consegue endereçar e um cabeçalho obrigatório
totalmente ausente.

Em 2026-07-28, um cabeçalho de roteamento ausente é uma falha de validação, e um servidor
em conformidade rejeita a requisição com `400` e `-32020`. O mcpsnoop só o emite quando se
sabe que a sessão usa essa revisão ou uma posterior, pois as revisões anteriores não
definem esses cabeçalhos de forma alguma e omiti-los nelas é o correto. A rejeição
`-32020` feita pelo próprio servidor conta como o mesmo sinal.

Um nome ou URI de recurso que não caiba no valor de um campo HTTP viaja em Base64 dentro
de um sentinela `=?base64?…?=`, que é decodificado antes da comparação; assim, um cliente
que codifica corretamente nunca é sinalizado.

Em requisições HTTP `tools/call`, o mcpsnoop também mostra cada cabeçalho `Mcp-Param-{Name}`
e, quando a definição de ferramenta anunciada correspondente é conhecida, compara-o com o
caminho de argumento anotado. Propriedades aninhadas, o sentinela Base64, booleanos e
inteiros seguros numericamente equivalentes são tratados sem falsos positivos de comparação
de strings. Cabeçalhos de parâmetro desconhecidos e sessões sem uma definição de ferramenta
correspondente permanecem apenas observacionais. A redação baseada em chave e em valor é
aplicada aos valores de cabeçalho de parâmetro capturados antes que cheguem a um sink, e um
valor que o próprio mcpsnoop sanitizou nunca é relatado como divergência.

### Detectar desvio na definição de ferramenta

A primeira listagem `tools/list` completa observada para um rótulo de servidor torna-se sua
linha de base confiável. Sessões posteriores comparam essa linha de base campo a campo: a
descrição, o título, os esquemas de entrada e saída, as anotações e os ícones, além das
ferramentas que foram adicionadas ou removidas. As anotações são o que mais importa, pois
uma ferramenta aprovada com `readOnlyHint` que depois se declara destrutiva é exatamente o
puxão de tapete para o qual essa verificação existe, e a especificação instrui os clientes a
tratar anotações como não confiáveis. O título e os ícones são monitorados porque são o que
o usuário vê, e a especificação classifica o `title` da ferramenta acima de `annotations.title`
e do seu nome. A tabela de sessões e o resumo de ferramentas sinalizam o desvio sem bloquear
ou alterar o tráfego MCP.

As anotações são comparadas por meio de seus padrões da especificação, de modo que um servidor
que passa a explicitar uma dica da qual já dependia não é relatado. Uma linha de base registrada
antes de o mcpsnoop monitorar um campo continua funcionando para os campos que registra e informa
quais não consegue responder; regrave com `mcpsnoop baseline --accept` quando confiar nas
definições atuais.

Alterar o que a redação registra muda o que o desvio compara. Uma linha de base obtida sem
`--redact-value` e depois verificada contra uma captura feita com ele relata os campos
sanitizados como alterados, o que é correto, já que a definição registrada de fato mudou.
Regrave com `--accept` após alterar as configurações de redação.

Use um `--label` estável e único para cada servidor cujo nome de comando ou host de destino
colidiria de outra forma. As linhas de base são armazenadas no diretório de estado normal do
mcpsnoop, portanto `MCPSNOOP_HOME` e `XDG_STATE_HOME` se aplicam.```bash
mcpsnoop check --fail-on drift session.jsonl
mcpsnoop baseline session.jsonl
mcpsnoop baseline --accept session.jsonl  # trust a legitimate definition change
mcpsnoop baseline --reset session.jsonl   # trust the next complete tools/list

Em CI efêmero, o diretório de estado começa vazio, então a primeira execução apenas registra o baseline e não relata divergências. O baseline precisa persistir entre execuções para que execuções posteriores possam verificá-lo. Aponte --baseline para um diretório versionado ou em cache, ou defina MCPSNOOP_HOME para um caminho persistente.```bash mcpsnoop check --fail-on drift --baseline .mcpsnoop/baselines session.jsonl

`drift` é opt-in para `check`; o gate padrão `error,invalid,warn` permanece inalterado.

### Sinalizar recursos de protocolo obsoletos

A revisão de 2026-07-28 torna obsoletos Roots, Sampling e Logging. Eles continuam funcionando por pelo menos um ano, então o mcpsnoop os sinaliza em vez de tratá-los como erros. O stream, o inspetor de capacidades e a exportação sinalizam todos eles, e cada marcador nomeia o substituto.

Dois dos três agora são acessíveis apenas por meio de uma solicitação com múltiplas idas e voltas (round-trips), onde o nome do método fica dentro do mapa `inputRequests` do servidor, em vez de na própria frame. Esses também são sinalizados, para que um servidor que migrou para o novo padrão não pare silenciosamente de reportar.```bash
mcpsnoop check --fail-on deprecated session.jsonl

Tal como drift, deprecated é opt-in. Uma execução padrão reporta a contagem e permanece verde, portanto uma sessão que utilize uma funcionalidade obsoleta ainda permitida nunca torna o CI vermelho por si só.

Sinalizar construções de esquema que os clientes lidam mal

Um servidor pode ser perfeitamente válido e ainda assim ser difícil para um agente usar. Os clientes diferem no quanto de JSON Schema realmente suportam, e uma ferramenta que o modelo continua a chamar incorretamente é muitas vezes uma ferramenta cujo esquema pediu mais do que o cliente entrega.

O resumo de ferramentas, aberto com s, tem uma coluna SCHEMA que indica o aspeto mais notável do esquema de cada ferramenta anunciada, com um + no final quando há mais do que um tipo.

ExibidoSignificado
no rooto inputSchema está ausente, não é um objeto JSON, ou tem um tipo de raiz diferente de "object"
dialectum $schema que indica um dialeto diferente do 2020-12 que a revisão utiliza por padrão
ext refum $ref que aponta para fora do documento, que é também o caso sobre o qual a especificação adverte os implementadores para não seguirem cegamente
oneOf, anyOf, allOf, notuma palavra-chave de composição, processada de forma inconsistente entre clientes
refum $ref que aponta para dentro do mesmo documento
untypeduma propriedade que não declara tipo nem qualquer outra forma de dizer o que aceita

Todas exceto a primeira são observações em vez de veredictos. Um esquema que usa oneOf não está errado, apenas provavelmente será lido de forma diferente por clientes diferentes, e um esquema pode declarar o dialeto que quiser. no root é a exceção: a definição de Tool exige inputSchema e fixa o seu tipo de raiz como "object", portanto um cliente que valida uma listagem rejeita essa ferramenta de imediato e ela nunca se torna chamável, sem nada no fio que diga porquê. no root lidera a coluna por esse motivo, e um esquema que a própria sanitização do mcpsnoop expurgou nunca é reportado, pois um esquema ilegível não é um esquema errado.

Essa divisão decide o que o check faz com elas. no root é um aviso no frame tools/list, portanto falha o gate padrão error,invalid,warn sem nenhuma flag, que é o objetivo: um servidor que fornece uma ferramenta inutilizável responde normalmente a todos os handshakes e simplesmente nunca recebe um tools/call. As observações são contadas como schema_findings e reportadas em schema findings:, e só fazem a execução falhar quando você adiciona schema a --fail-on. Ambas são incluídas em --format junit e --format sarif, e o export traz a lista por ferramenta em summary.definitions.per_tool[].findings.```bash mcpsnoop check session.jsonl # a non-object root already fails this mcpsnoop check --fail-on schema session.jsonl # and now so do the observations

A coluna carrega a cor de aviso e nunca o vermelho da coluna ERR, e o
mcpsnoop continua sem alterar nada no tráfego que encaminha.

Nada é resolvido ou buscado. Um `$ref` externo é reconhecido apenas pela sua
forma, e o esquema para o qual aponta nunca é lido.

### Veja o quanto o servidor custa em contexto

As definições de ferramentas entram no contexto do modelo em todas as
conversas, e os resultados das ferramentas em todas as chamadas. O resumo da
ferramenta (`s`) mede ambos a partir da sessão que você realmente capturou.

A linha `definitions` é o custo fixo: quanto o `tools/list` deste servidor pesa
antes que uma única chamada seja feita. A coluna `DEF` detalha isso por ferramenta
e `RESULT` é o que as respostas de cada ferramenta custaram até agora. A tabela
permanece ordenada por erros e latência, então examine `DEF` para encontrar as
definições caras; a exportação as lista das mais pesadas primeiro. Uma linha
abaixo da tabela nomeia o resultado individual mais pesado, que um total esconde.

Os valores das definições são o JSON com espaços em branco insignificantes
removidos, portanto, um servidor que faz pretty-print do seu `tools/list` não é
contado como mais caro do que um que não o faz, e o mesmo servidor mede o mesmo
entre capturas. `RESULT` são os bytes como chegaram: um resultado é um payload
único, e não um contrato que valha a pena normalizar.```bash
mcpsnoop export -T json | jq '.summary.definitions'

A exportação traz os mesmos números, por ferramenta e divididos em bytes de descrição e bytes de schema, de modo que uma descrição grande e um schema grande permanecem separáveis e qualquer um deles pode ser rastreado entre capturas. mcpsnoop diff informa se uma descrição ou um schema mudou entre duas sessões; a exportação é onde reside o tamanho dessa mudança.

Estes são bytes, não tokens. Uma contagem de tokens depende do modelo, então medir tokens significaria incluir um tokenizador e escolher de quem. Bytes são exatos e você pode aplicar sua própria proporção. Um tools/list incompleto relata o que viu como um piso e diz isso, em vez de apresentar uma soma parcial como se fosse o total.

Detectar um cliente que adultera o estado do servidor

No padrão multi round-trip, o servidor entrega ao cliente um requestState opaco e o cliente deve devolvê-lo intacto na nova tentativa. O servidor é instruído a tratá-lo como entrada controlada pelo atacante, porque um cliente que o adultera pode tentar alterar o comportamento do servidor ou contornar uma verificação de autorização.

Posicionado no meio do fluxo, o mcpsnoop vê o valor sair e voltar, então pode dizer quando o contrato foi quebrado. Há três maneiras de isso acontecer, cada uma relatada como um aviso de protocolo na nova tentativa.

RelatadoSignificado
MRTR retry changed requestStateo cliente devolveu algo diferente do que o servidor emitiu
MRTR retry is missing requestStateo servidor emitiu um e a nova tentativa o omitiu
MRTR retry invented requestStatea nova tentativa trouxe um que o servidor nunca emitiu

Estas são violações de protocolo cometidas pelo cliente, e não observações nossas, então elas seguem o sinal de aviso comum e uma execução padrão de check falha com uma delas. Isso é intencional. Um cliente que adultera o estado do servidor é motivo suficiente para interromper um build.

O valor em si nunca é exibido nem registrado em log, e nada o decodifica ou analisa. Pode ser um blob criptografado contendo um principal e um token, e comparar bytes opacos é toda a verificação.

Um caso fica fora do alcance. Quando um servidor responde com um requestState e sem inputRequests, uma nova tentativa adulterada não corresponde a nada e não devolve nenhuma chave, então não resta nada para vinculá-la à requisição original e ela é lida como uma chamada não relacionada, em vez de uma violação.

Monitorando a partir de outra máquina

Mantenha a captura na máquina onde o tráfego ocorre e use SSH para o salto de rede, para que o mcpsnoop nunca precise de um transporte remoto próprio.

Visualização ao vivo

Execute o TUI na sua estação de trabalho e encaminhe o socket do mcpsnoop da máquina remota de volta para ela. O túnel ao vivo usa o encaminhamento de socket Unix do SSH, então ambas as extremidades devem executar Linux ou macOS. No Windows, use a cópia do log post-mortem abaixo.```bash

on your workstation, start the TUI

mcpsnoop

create the remote socket directory once

ssh remote-user@remote-host 'mkdir -p ~/.local/state/mcpsnoop'

print the tunnel command, then run the printed ssh -R line

mcpsnoop remote remote-user@remote-host

on the remote host, wrap your server as usual

mcpsnoop -- node build/index.js

O socket vive sob o diretório de estado do remoto, resolvido como `MCPSNOOP_HOME`,
senão `XDG_STATE_HOME/mcpsnoop`, senão `~/.local/state/mcpsnoop`. Por padrão, o mcpsnoop
assume o home Linux `/home/<user>` a partir do seu `user@host` e imprime um lembrete
no stderr sempre que recorre a essa suposição. Se o remoto resolver em outro lugar,
nomeie a peça não padrão.```bash
# a non-Linux or custom home, macOS is /Users/<user> and root is /root
mcpsnoop remote --remote-home /Users/remote-user remote-user@remote-host

# an explicit MCPSNOOP_HOME on the remote
mcpsnoop remote --remote-mcpsnoop-home /srv/mcpsnoop remote-user@remote-host

# an explicit XDG_STATE_HOME on the remote
mcpsnoop remote --remote-xdg-state-home /var/lib/state remote-user@remote-host

Post-mortem

Stream a remote session straight into the TUI over SSH, no local copy needed.```bash ssh remote-user@remote-host 'cat ~/.local/state/mcpsnoop/sessions/session.jsonl' | mcpsnoop open -

Para manter uma cópia local em vez disso, use scp para copiar os logs para o diretório de sessões e execute
o TUI normalmente.```bash
# copy the remote logs into your local sessions directory
mkdir -p ~/.local/state/mcpsnoop/sessions
scp remote-user@remote-host:'~/.local/state/mcpsnoop/sessions/*.jsonl' \
  ~/.local/state/mcpsnoop/sessions/

# open the TUI, it backfills the copied sessions
mcpsnoop

Segurança

O mcpsnoop executa o comando do servidor que você encapsula, portanto encapsule apenas servidores em que você confia e execute os não confiáveis em um contêiner. Ele nunca executa nada que você não tenha colocado na configuração do seu cliente.

Os frames capturados podem incluir prompts, argumentos de ferramentas, credenciais e resultados de ferramentas. Se os payloads puderem conter segredos, opte pela redação para limpar as cópias do registro observado, enquanto os bytes repassados continuam passando inalterados.

A redação baseada em chave substitui valores inteiros sob chaves de objeto JSON correspondentes, e o mesmo conjunto de chaves é aplicado, na medida do possível, aos argumentos de linha de comando do servidor encapsulado, de modo que --api-key=sk-x e --token sk-x são mascarados sob --redact-secrets. Um argumento que contenha um segredo sem um nome de flag reconhecível não pode ser detectado.

A redação baseada em caminho substitui apenas valores selecionados por uma expressão JSONPath, o que é útil quando um nome de chave comum é confidencial em um local, mas seguro em outro. Repita --redact-path para limpar mais de um local.

A redação baseada em valor aplica expressões regulares a valores de string observados, texto de stderr e frames de texto não JSON.

Os três são de melhor esforço. As regexes podem deixar passar segredos, corresponder excessivamente a texto inofensivo ou não detectar valores transformados ou codificados.

A redação nunca se transforma em uma acusação. Toda verificação que compara uma coisa observada com outra, um cabeçalho de roteamento com o corpo, um valor de Mcp-Param com o argumento que ele espelha, o schema de uma ferramenta com o que a revisão exige dela, sabe quando o mcpsnoop foi o lado que reescreveu os bytes e permanece em silêncio em vez de reportar um servidor pela própria configuração de privacidade do usuário. O desvio de definição de ferramenta é a exceção, e isso é proposital, pois ativar a redação altera o que é registrado e, portanto, o que uma linha de base contém. Consulte Detectar desvio de definição de ferramenta.```bash

built-in preset of common secret keys

mcpsnoop --redact-secrets -- node build/index.js

or name your own keys

mcpsnoop --redact-key token,api_key,password -- node build/index.js

scrub one location without redacting every field named password

mcpsnoop --redact-path '$.params.arguments.password' -- node build/index.js

wildcards scrub every matching array element

mcpsnoop --redact-path '$.params.arguments.accounts[*].password' -- node build/index.js

scrub obvious token-shaped values outside known keys

mcpsnoop --redact-value 'sk-[A-Za-z0-9]+' -- node build/index.js

combine the layers in http mode

mcpsnoop http --target http://localhost:3000/mcp --redact-secrets --redact-value 'Bearer\s+\S+'

Para fluxos de trabalho remotos, use tunelamento SSH ou transferência de arquivos via SSH para que a autenticação de transporte,
criptografia, verificação de host, rotação de chaves e política de auditoria permaneçam na sua
configuração SSH existente.

## Contribuindo

Issues e pull requests são bem-vindos. Consulte [CONTRIBUTING.md](https://github.com/kerlenton/mcpsnoop/blob/HEAD/CONTRIBUTING.md) para obter os detalhes.

## Licença

[MIT](https://github.com/kerlenton/mcpsnoop/blob/HEAD/LICENSE)

Categorias