Torna agli aggiornamenti
New releaseJul 26, 2026

mcpsnoop v0.15.0

Wireshark per MCP. Un proxy trasparente che mostra ogni vera tool call tra il tuo client AI e i tuoi server MCP, in tempo reale nel tuo terminale.

Condividi

mcpsnoop

Wireshark per MCP. Un proxy trasparente che mostra ogni chiamata effettiva agli strumenti tra il tuo client AI e i tuoi server MCP, in diretta nel tuo terminale.

CI Go Reference MIT

demo di mcpsnoop

Il problema

L'MCP Inspector ufficiale si connette come client a sé stante, quindi non vede mai ciò che il tuo client (Cursor, Claude Code, Codex) invia realmente al tuo server. E qualsiasi cosa attenda che una richiesta arrivi non può mostrare la chiamata che il modello non ha mai fatto, o fatta con gli argomenti sbagliati. Quando uno strumento non viene chiamato silenziosamente, le funzionalità non combaciano, o una chiamata resta in sospeso, ti ritrovi a scavare nei log e a tirare a indovinare.

mcpsnoop si inserisce invece nel percorso dati reale. Avvolgi il comando del tuo server con esso e guarda ogni frame JSON-RPC in diretta, mentre il tuo client e il tuo server reali dialogano.

Avvio rapido

Vedilo subito, senza nulla da configurare.```bash mcpsnoop demo

Per usarlo davvero, avvolgi il tuo server nella configurazione MCP del tuo client.```json
{
  "mcpServers": {
    "my-server": {
      "command": "mcpsnoop",
      "args": ["--", "node", "build/index.js"]
    }
  }
}

Tutto ciò che segue -- è il comando che normalmente avvia il tuo server. Inserisci quello che già usi, come python server.py, npx -y @scope/server, o un binario compilato.

Su Claude Desktop non devi fare quella modifica a mano.```bash mcpsnoop wrap my-server # route my-server through mcpsnoop mcpsnoop unwrap my-server # put it back

`wrap` trova `claude_desktop_config.json`, lo copia in
`claude_desktop_config.json.mcpsnoop.bak` la prima volta e riscrive solo
la voce di quel singolo server, così la tua formattazione e tutti gli altri
server restano intatti. All'interno della voce riscritta le chiavi tornano
in ordine alfabetico. `unwrap` ripristina il file e rimuove il backup quando
nessun server è più avvolto. Riavvia Claude Desktop dopo l'una o l'altra
operazione, poiché i server MCP vengono avviati una sola volta all'avvio.

Quindi usa il tuo client come al solito e apri la UI.```bash
mcpsnoop

Nessun flag, nessun percorso di socket, nessun ordine di avvio da ricordare. Lo shim e la UI si trovano da soli, e la UI recupera le sessioni passate dal disco.

Per un server streamable-HTTP, esegui mcpsnoop come reverse proxy.```bash mcpsnoop http --target http://localhost:3000/mcp --listen :7000

Lo stato HTTP di ogni risposta appare nello stream, quindi una risposta che non trasporta alcun messaggio JSON-RPC proprio è comunque un frame visibile, non un nulla: la sfida 401, il 403 su un Origin rifiutato, il 202 che riconosce una notifica e il 502 quando il target non può essere raggiunto affatto. L'header `WWW-Authenticate` di un 401 viene conservato verbatim e mostrato nell'ispettore, poiché indica lo schema di autenticazione e i metadati della risorsa a cui andare successivamente. Filtra per stato con `status:401` nella TUI, oppure per qualsiasi errore con `status:err`. Un 4xx o 5xx conta come errore, quindi un'esecuzione predefinita di `mcpsnoop check` fallisce su di esso.

Non hai un server tuo? [Provalo sul serio](https://github.com/kerlenton/mcpsnoop/blob/HEAD/docs/TRY_IT.md) contro un server di test pubblicato, guidato dal tuo client. Per ispezionare una sessione dopo che è avvenuta, vedi [rivedere sessioni passate dai log](https://github.com/kerlenton/mcpsnoop/blob/HEAD/docs/POST_MORTEM.md).

### File di configurazione

Se riutilizzi gli stessi flag shim in un progetto, inseriscili in un file `.mcpsnoop.toml` nella directory di lavoro corrente.```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

Ripeti redact-key, redact-value e redact-path su righe separate per aggiungerne più di uno per ciascuno.

Queste sono tutte le chiavi supportate.

Il file viene cercato solo nella directory di lavoro corrente, non nelle directory superiori.

Le opzioni esplicite da riga di comando sovrascrivono i valori del file di configurazione.

Comandi

ComandoCosa fa
mcpsnoop -- <server>avvolge un server stdio come uno shim trasparente
mcpsnoopapre la TUI live
mcpsnoop http --target <url>fa da proxy a un server HTTP streamabile
mcpsnoop exportgenera una sessione in json, html, text, har o otlp
mcpsnoop checkfa fallire la CI su errori, frame non validi, avvisi, disallineamenti di routing, chiamate bloccate o risultati in ritardo
mcpsnoop baselineispeziona, accetta o reimposta le definizioni degli strumenti affidabili
mcpsnoop diffconfronta strumenti e chiamate tra due sessioni catturate
mcpsnoop openapre una sessione salvata nella TUI
mcpsnoop pruneelimina i log delle sessioni salvate più vecchi di una soglia
mcpsnoop wrap <server>instrada uno dei server di Claude Desktop attraverso mcpsnoop
mcpsnoop unwrap <server>ripristina la voce di quel server com'era
mcpsnoop remote <user@host>stampa il comando del tunnel SSH
mcpsnoop demoriproduce una sessione scriptata

Esegui mcpsnoop help per l'elenco completo, oppure mcpsnoop help <command> per i flag di un singolo comando.

Come si confronta

MCP Inspectormcpsnoop
Vede il traffico reale di client e serverno
Segnala chiamate bloccate ed errori di streamingno
Segnala output spurio che corrompe lo streamno
Segnala frame JSON-RPC malformatino
Rileva la deriva delle definizioni degli strumenti dopo l'approvazioneno
UI terminale interattivano
Zero configurazione, senza flag o ordinamentino
Ispettore di capacitàparziale
Riproduce una chiamata catturatano
Esportazione sessione (json / html / text / otlp)no
Binario singolo, nessuna dipendenza a runtimeno

Installazione

Go```bash

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

### Homebrew```bash
brew install mcpsnoop

Binari precompilati per ogni piattaforma sono nella pagina Releases.

Completamenti per shell

mcpsnoop include completamenti per bash, zsh, fish e PowerShell. Esegui mcpsnoop completion <shell> --help per i passaggi di configurazione, che coprono l'abilitazione dei completamenti e il percorso di installazione per il tuo sistema operativo.

Come funziona

mcpsnoop si inserisce nella pipeline tra il tuo client AI e i tuoi server MCP, copiando ogni frame JSON-RPC in una UI terminale live

mcpsnoop è due ruoli in un unico binario. mcpsnoop -- <server> è lo shim trasparente che il tuo client avvia, inoltrando i byte verbatim mentre invia una copia di ogni frame all'hub. mcpsnoop senza argomenti è quell'hub e la sua TUI live. Loro comunicano attraverso un socket noto e log su disco, quindi nessuno dei due deve avviarsi per primo.

L'hub carica le 100 sessioni salvate più recenti per impostazione predefinita, mantenendo limitato il lavoro di avvio senza eliminare le tracce più vecchie. Usa mcpsnoop --history-limit N per scegliere un altro limite, oppure mcpsnoop --history-limit 0 per caricare l'intera cronologia. Le sessioni più vecchie rimangono disponibili tramite mcpsnoop open <session-id> e mcpsnoop export <session-id>.

Il limite di cronologia limita ciò che viene caricato; mcpsnoop prune limita ciò che viene conservato. Elimina i log delle sessioni salvate più vecchi di una soglia, e non viene mai eseguito da solo.```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` è obbligatorio (non esiste un default che cancellerebbe qualcosa) e accetta un conteggio in giorni come `30d` o una durata Go come `72h`. Le baseline degli strumenti vengono lasciate intatte, poiché una baseline è associata all'etichetta del server piuttosto che alla sessione.

Poiché si trova nel pipe effettivo, non in disparte come l'Inspector, vede esattamente ciò che il tuo client e server reali si dicono a vicenda, qualunque sia il linguaggio in cui è scritto il server.

## Tasti di scelta rapida

| Tasto | Azione | | Tasto | Azione |
|---|---|---|---|---|
| `enter` | ispeziona / approfondisci | | `/` | filtra |
| `esc` | indietro | | `:` | comando |
| `j` / `k` | sposta | | `r` | riproduci una chiamata |
| `g` / `G` | in alto / in basso | | `c` | capacità |
| `ctrl-f` / `ctrl-b` | pagina | | `s` | riepilogo strumento |
| `p` | pausa | | `y` | copia |
| `shift`+`<key>` | ordina per colonna | | `e` | esporta |
| `ctrl-d` | elimina sessione | | `f` | segui |
| `?` | aiuto | | | |

Premi `?` nell'app per l'elenco completo.

## Filtrare il flusso

Premi `/` in una sessione e combina token separati da spazi, con AND logico. Il testo semplice corrisponde a metodo, strumento, id e payload.

| Token | Filtra per | Esempio |
|---|---|---|
| `tool:` | nome dello strumento | `tool:search` |
| `method:` | metodo JSON-RPC | `method:tools/call` |
| `id:` | id della richiesta e qualsiasi nuovo tentativo che la prosegue | `id:7` |
| `task:` | id dell'attività | `task:01J...` |
| `dir:` | direzione (`c2s`, `s2c`) | `dir:s2c` |
| `kind:` | tipo di frame (`req`, `resp`, `notify`, `stderr`, `invalid`) | `kind:invalid` |
| `status:` | esito della chiamata (`ok`, `error`, `cancel`, `late`, `cancelled`, `pending`, `bad`, `warn`, `mismatch`, o uno stato HTTP come `401`) | `status:error` |

Combina i token per essere più specifico.```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)

L'ultimo trova qualcosa solo su un server che parla la versione 2025-11-25 o precedenti. La revisione del 2026-07-28 ha rimosso le richieste avviate dal server; un server che ha bisogno di qualcosa dal client ora risponde alla richiesta del client chiedendogliela, poi il client ritenta. mcpsnoop collega quei tentativi alla richiesta che proseguono, così lo scambio viene letto come una singola chiamata piuttosto che come più chiamate.

Esportare le sessioni

Trasforma qualsiasi sessione catturata in un file portabile.```bash mcpsnoop export -T json|html|text|har|otlp [-o file|-] [session-id|log.jsonl|-]

| Formato | Cosa ottieni |
|---|---|
| `json` | chiamate correlate, conteggi per strumento e latenza p50/p95/p99, chiamate più lente, capacità e frame grezzi |
| `html` | un file browser autonomo con ricerca e JSON richiudibile |
| `text` | un dump di testo semplice e leggibile |
| `har` | una voce per chiamata correlata, apribile negli strumenti di sviluppo del browser e in qualsiasi altro programma che legga HAR |
| `otlp` | JSON OTLP con uno span per chiamata correlata; il contesto di traccia W3C unisce le tracce del chiamante, altrimenti viene usata una traccia per sessione |

MCP non è HTTP, quindi l'URL, il codice di stato e i tempi di una voce HAR sono una
mappatura deliberata di ogni chiamata piuttosto che una trascrizione di rete.

Per OTLP, il `_meta.traceparent` di una richiesta fornisce l'ID di traccia e di span
genitore di quella chiamata, e `_meta.tracestate` viaggia insieme allo span. Quando il
traceparent è assente o non valido, mcpsnoop mantiene la traccia derivata dalla sessione
e non trasporta alcuno stato. mcpsnoop osserva piuttosto che partecipare, quindi non
aggiunge una propria voce di vendor e passa lo stato del chiamante inalterato.```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

Omiti -o per scrivere su stdout, e ometti la sessione per prendere la più recente, oppure passa - per leggere JSONL da stdin. Nella TUI, premi e per esportare la sessione selezionata come HTML, oppure esegui :export json|html|text|har|otlp [path] dalla modalità comando.

Per ripulire una cattura esistente prima di ispezionarla o condividerla, passa gli stessi flag di redazione usati durante la cattura a export o open:```bash mcpsnoop export session.jsonl --redact-secrets --redact-key project_token -o shared.json mcpsnoop open session.jsonl --redact-path '$.params.arguments.password'

These flags rewrite the exported file or the in-memory TUI view, never the
source JSONL. `export` refuses an output that names the same file as its input,
and writes through a temporary file that is renamed into place, so a run that
fails leaves the previous file whole.

L'`inputSchema` e l'`outputSchema` di uno strumento, come pubblicizzati in un risultato di `tools/list`, non vengono modificati da `--redact-key` e `--redact-secrets`. Un nome all'interno di uno schema è una dichiarazione di tipo piuttosto che un valore; il nome stesso rimane comunque nel log, e ripulire il sottoschema sotto una proprietà chiamata `token` si porterebbe dietro anche i controlli dello strumento. L'esenzione vale solo per quella posizione; quindi un argomento che capita di chiamarsi `inputSchema` viene ripulito come qualsiasi altro, e la pulizia si ferma a `default`, `const`, `examples` e `enum`, che contengono dati piuttosto che struttura. Usa `--redact-path` per indicare qualcosa all'interno di uno schema, oppure `--redact-value`, che trova corrispondenze di testo ovunque si trovi, tranne nelle due parole chiave che mcpsnoop analizza, `type` e `x-mcp-header`.

Ciò che ogni flag raggiunge è diverso, quindi controlla il risultato piuttosto che dare per scontato. Tutti e quattro ripuliscono i payload JSON-RPC, e `--redact-key`, `--redact-path` e `--redact-secrets` raggiungono solo quelli. Solo `--redact-value` ripulisce anche stderr, altro testo non JSON e l'interno di una stringa. Un header `Mcp-Param-*` viene ripulito insieme al valore del corpo che rispecchia; gli altri metadati dell'involucro, le etichette dei server, `Mcp-Name`, `Mcp-Method` e lo stato HTTP, vengono lasciati così come catturati. La redazione è best effort, quindi usa un percorso di output separato e leggi il risultato prima di condividerlo.

### Trasmettere in streaming le chiamate completate a un collector OTLP

Invia span mentre il proxy è in esecuzione puntandolo verso un endpoint di trace JSON OTLP/HTTP. Ripeti `--otlp-header` per l'autenticazione del collector o per gli header 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

La consegna è best-effort e non blocca mai il traffico MCP in proxy. Se il collector non è disponibile, mcpsnoop riprova in background e scarta i nuovi frame di traccia quando la sua coda limitata è piena. Il normale log di sessione JSONL rimane il record durevole.

Confronto delle sessioni

Confronta due sessioni salvate per id o percorso JSONL.```bash mcpsnoop diff before-session after-session mcpsnoop diff old.jsonl new.jsonl

Il report mostra gli strumenti aggiunti o rimossi, le modifiche alla descrizione e allo `inputSchema`, le chiamate di strumenti corrispondenti il cui stato è cambiato e i notevoli cambiamenti di durata. Le chiamate vengono abbinate per nome dello strumento e argomenti, quindi le chiamate riordinate vengono comunque confrontate correttamente. Per impostazione predefinita, le variazioni di durata devono differire di almeno 100 ms e 2x; usa `--duration-threshold` e `--duration-ratio` per regolare queste soglie.

Passa `--exit-code` per controllare la CI sulle regressioni: esce con codice non zero quando la sessione successiva rimuove uno strumento, modifica una descrizione, un titolo, uno schema di input, uno schema di output o delle annotazioni, ha una chiamata il cui stato è peggiorato o rallenta. I miglioramenti (strumenti aggiunti, chiamate corrette, accelerazioni) escono comunque con zero, e così anche un cambio di icona, che altera l'aspetto di uno strumento senza cambiarne il funzionamento.

## Verifica delle sessioni in CI

Applica un controllo a una sessione registrata dell'agente in base a errori, corruzione dello stream, avvisi di protocollo, mancate corrispondenze dell'intestazione di routing, chiamate che non hanno mai ricevuto risposta, frame scartati che lasciano la cattura incompleta, deriva delle definizioni degli strumenti o uso di funzionalità di protocollo deprecate.```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 fanno fallire il controllo da soli. Gli altri sono opzionali. Passa un sottoinsieme separato da virgole per limitare il controllo solo a ciò che interessa a un job, ometti la sessione per controllare la cattura più recente, oppure usa - per leggere JSONL da stdin.

SegnaleFallisce su
erroruna chiamata a cui viene risposto con un errore JSON-RPC, un risultato marcato isError, o un task terminato con un fallimento
invalidun frame sul canale di protocollo che non è JSON-RPC valido, di solito un server che scrive su stdout
warnun frame che viola un'aspettativa posta dalle specifiche MCP o JSON-RPC
mismatchun header di routing in disaccordo con il body, che cavalca un batch, o mancante dove la revisione lo richiede
pendinguna richiesta ancora aperta quando la cattura è terminata, quindi il chiamante è rimasto in attesa
late-resultuna risposta arrivata dopo che la sua richiesta è stata cancellata
driftuna definizione di tool pubblicizzata che cambia dopo che la baseline è stata approvata
deprecateduna funzionalità che la specifica ha deprecato
incompleteframe scartati a monte, il che rende ogni altro conteggio un minimo piuttosto che un totale
schemauno schema pubblicizzato che usa una costruzione o un dialetto che si propaga male tra i client

Ogni segnale viene conteggiato sia che sia abilitato o meno, quindi un'esecuzione riporta ciò che ha trovato prima che tu decida su cosa deve fallire.``` 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

Il conteggio dei frame persi viaggia anche con gli artefatti, quindi una cattura che si sottostima lo dichiara ovunque venga aperta: `missing_frames` nell'export JSON, `log.comment` in HAR e l'attributo di risorsa `mcpsnoop.session.missing_frames` in OTLP.```bash
mcpsnoop check build-agent
mcpsnoop check --fail-on error,invalid artifacts/session.jsonl
mcpsnoop check --fail-on mismatch gateway-run.jsonl

Oltre ai conteggi dei segnali, verifica la forma dell'esecuzione. Questi si combinano tra loro e con --fail-on, e qualsiasi errore termina con codice di uscita non zero.

FlagFallisce quando
--max-duration <dur>una o più chiamate di strumento completate hanno superato il budget; riporta il loro numero e la chiamata peggiore
--expect-tool <name>lo strumento indicato non è mai stato chiamato (ripetibile)
--forbid-tool <name>lo strumento indicato è stato chiamato (ripetibile)

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

### Segnalalo dove la CI guarda già

`--format junit` scrive un `<testcase>` per segnale e sessione, e i suoi fallimenti
seguono la stessa selezione `--fail-on` dell'output testuale.```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 scrive un log SARIF 2.1.0 invece. Dove junit riporta un aggregato per segnale, SARIF riporta un risultato per finding, trasportando la sessione, il frame Seq e il testo di warning o drift del frame stesso, e indicando la riga del log da cui il frame è stato decodificato. Un segnale nominato in --fail-on è riportato al livello error e uno al di fuori di esso al livello note, quindi il report e il gate non sono mai in disaccordo.

Un risultato punta al log con un percorso relativo alla directory di lavoro, che code scanning risolve poi rispetto alla radice del repository. L'avviso viene visualizzato con le righe circostanti solo quando quel percorso è un file nel commit analizzato, quindi una cattura generata dal workflow in artifacts/ apre un avviso che contiene il messaggio, la regola e il numero di riga ma nessuna vista del codice sorgente. Committare una cattura che si desidera visualizzare per intero è l'unico modo per ottenerne una. Un log letto dalla directory di stato o da stdin non riceve alcun percorso.

Code scanning rifiuta un file la cui esecuzione contiene più di 25.000 risultati e mostra solo i primi 5.000 di quelli che accetta, quindi il report è limitato a 5.000: i finding su cui il gate ha fallito per primi, poi un risultato mcpsnoop/report-truncated che indica quanti sono stati esclusi. I formati text e junit rimangono completi.

Per inserire i finding nella scheda Security, passa il log SARIF a upload-sarif. Il job necessita di security-events: write, altrimenti l'upload risponde 403. check esce con codice non zero in presenza di un finding, quindi lo step di upload necessita di if: always() per eseguirsi comunque sulle run che hanno qualcosa da segnalare; continue-on-error consegna il verdetto al check di code scanning, che fallisce su un avviso a livello error e può essere impostato come check richiesto. Rimuovilo se preferisci che lo step check stesso faccia diventare rosso il job.```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
### Rileva un header di routing in disaccordo con il body

Sul trasporto streamable-HTTP un gateway instrada in base a `Mcp-Method` e `Mcp-Name`
mentre il server legge il body, quindi un header in disaccordo con il body significa
che i due stanno osservando due richieste diverse. Il segnale `mismatch` copre questo caso,
un header che viaggia su un batch che non può indirizzare e un header obbligatorio mancante
del tutto.

In 2026-07-28 un header di routing mancante è un errore di validazione e un server
conforme rifiuta la richiesta con `400` e `-32020`. mcpsnoop lo segnala solo dopo
che la sessione è nota per supportare quella revisione o successiva, poiché le revisioni
precedenti non definiscono affatto questi header e ometterli lì è corretto. Il rifiuto
`-32020` del server stesso conta come lo stesso segnale.

Un nome o un URI di risorsa che non rientra in un valore di campo HTTP viaggia in Base64 in
un sentinel `=?base64?…?=`, che viene decodificato prima del confronto, quindi un client
che codifica correttamente non viene mai segnalato.

Nelle richieste HTTP `tools/call` mcpsnoop mostra anche ogni header `Mcp-Param-{Name}`
e, quando è nota la definizione dello strumento pubblicizzato corrispondente, la confronta
con il percorso annotato dell'argomento. Proprietà annidate, sentinel Base64, booleani e
interi sicuri numericamente equivalenti vengono gestiti senza falsi positivi da confronto
di stringhe. Gli header di parametri sconosciuti e le sessioni senza una definizione dello
strumento corrispondente restano a livello osservativo. La redazione basata su chiave e
valore si applica ai valori degli header dei parametri catturati prima che raggiungano un
sink, e un valore ripulito dallo stesso mcpsnoop non viene mai segnalato come disaccordo.

### Rileva la deriva delle definizioni degli strumenti

La prima `tools/list` completa osservata per un'etichetta di server diventa la sua baseline
attendibile. Le sessioni successive confrontano quella baseline campo per campo: la
descrizione, il titolo, gli schemi di input e output, le annotazioni e le icone, oltre
agli strumenti aggiunti o rimossi. Le annotazioni sono ciò che conta di più, poiché uno
strumento approvato con `readOnlyHint` che in seguito si dichiara distruttivo è il tiro
del tappeto per cui questo controllo esiste, e la specifica dice ai client di trattare le
annotazioni come non attendibili. Il titolo e le icone vengono tracciati perché sono ciò
che l'utente vede, e la specifica colloca il `title` di uno strumento al di sopra di
`annotations.title` e del suo nome. La tabella delle sessioni e il riepilogo degli
strumenti segnalano la deriva senza bloccare o modificare il traffico MCP.

Le annotazioni vengono confrontate tramite i loro valori predefiniti di specifica, quindi
un server che inizia a esplicitare un suggerimento su cui faceva già affidamento non viene
segnalato. Una baseline registrata prima che mcpsnoop tracciasse un campo continua a
funzionare per i campi che registra e indica quali non può verificare; registra di nuovo
con `mcpsnoop baseline --accept` una volta che ti fidi delle definizioni correnti.

Modificare ciò che la redazione registra modifica ciò che la deriva confronta. Una baseline
acquisita senza `--redact-value` e poi verificata con una cattura effettuata con
quell'opzione segnala i campi ripuliti come modificati, il che è corretto, poiché la
definizione registrata è davvero cambiata. Registra di nuovo con `--accept` dopo aver
modificato le impostazioni di redazione.

Usa un `--label` stabile e univoco per ogni server il cui nome di comando o host di
destinazione entrerebbe altrimenti in collisione. Le baseline sono archiviate nella
normale directory di stato di mcpsnoop, quindi si applicano `MCPSNOOP_HOME` e
`XDG_STATE_HOME`.```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

In una CI effimera la directory di stato parte vuota, quindi la prima esecuzione registra solo la baseline e non segnala alcuna deriva. La baseline deve persistere tra le esecuzioni affinché quelle successive possano verificare rispetto ad essa. Punta --baseline a una directory salvata nel repository o in cache, oppure imposta MCPSNOOP_HOME su un percorso persistente.```bash mcpsnoop check --fail-on drift --baseline .mcpsnoop/baselines session.jsonl

`drift` è opt-in per `check`; il gate predefinito `error,invalid,warn` rimane invariato.

### Contrassegna le funzionalità del protocollo deprecate

La revisione del 2026-07-28 depreca Roots, Sampling e Logging. Continuano a funzionare per almeno un anno, quindi mcpsnoop li contrassegna invece di trattarli come errori. Il flusso, l'ispettore delle capacità e l'esportazione li contrassegnano tutti, e ogni marcatore indica il sostituto.

Due dei tre sono ora raggiungibili solo tramite una richiesta multi round-trip, in cui il nome del metodo si trova all'interno della mappa `inputRequests` del server anziché sul frame stesso. Anche questi vengono contrassegnati, così un server che è passato al nuovo pattern non smette silenziosamente di segnalare.```bash
mcpsnoop check --fail-on deprecated session.jsonl

Come drift, deprecated è opt-in. Un'esecuzione predefinita riporta il conteggio e resta verde, quindi una sessione che usa una funzionalità deprecata ma ancora legale non fa mai diventare rossa la CI da sola.

Segnala i costrutti di schema che i client gestiscono male

Un server può essere perfettamente valido e comunque difficile da usare per un agente. I client differiscono su quanto del JSON Schema supportano davvero, e uno strumento che il modello continua a chiamare in modo errato è spesso uno strumento il cui schema ha richiesto più di quanto il client offra.

Il sommario degli strumenti, aperto con s, ha una colonna SCHEMA che indica la caratteristica più notevole dello schema di ogni strumento pubblicizzato, con un + finale quando ce n'è più di uno.

MostratoSignificato
no rootinputSchema è assente, non è un oggetto JSON o ha un tipo radice diverso da "object"
dialectun $schema che indica un dialetto diverso dal 2020-12 a cui la revisione fa default
ext refun $ref che punta all'esterno del documento, che è anche il caso in cui la spec avverte gli implementatori di non seguirlo alla cieca
oneOf, anyOf, allOf, notuna parola chiave di composizione, gestita in modo incoerente tra i client
refun $ref che punta all'interno dello stesso documento
untypeduna proprietà che non dichiara alcun tipo né altri modi per dire cosa accetta

Tutte le voci tranne la prima sono osservazioni, non verdetti. Uno schema che usa oneOf non è sbagliato, è solo probabile che venga letto in modo diverso da client diversi, e uno schema può dichiarare il dialetto che preferisce. no root è l'eccezione: la definizione di Tool richiede inputSchema e fissa il tipo radice a "object", quindi un client che valida un elenco rifiuta del tutto quello strumento e non diventa mai chiamabile, senza nulla sul wire che spieghi perché. Per questo no root apre la colonna, e uno schema che la stessa redazione di mcpsnoop ha ripulito non viene mai segnalato: uno schema illeggibile non è sbagliato.

Questa distinzione determina cosa fa check con queste voci. no root è un avviso sul frame tools/list, quindi fa fallire il gate predefinito error,invalid,warn senza alcun flag, ed è proprio questo il punto: un server che espone uno strumento inutilizzabile risponde normalmente a ogni handshake e semplicemente non riceve mai una tools/call. Le osservazioni vengono conteggiate come schema_findings e segnalate sotto schema findings:, e fanno fallire l'esecuzione solo quando aggiungi schema a --fail-on. Entrambi arrivano a --format junit e --format sarif, e export riporta l'elenco per strumento sotto 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

La colonna porta il colore di avviso e mai il rosso della colonna ERR, e mcpsnoop continua a non cambiare nulla nel traffico che inoltra.

Niente viene risolto o recuperato. Un `$ref` esterno viene riconosciuto solo dalla sua forma, e lo schema a cui punta non viene mai letto.

### Scopri quanto ti costa il server nel contesto

Le definizioni degli strumenti entrano nel contesto del modello a ogni conversazione, e i risultati degli strumenti a ogni chiamata. Il riepilogo degli strumenti (`s`) misura entrambi a partire dalla sessione che hai effettivamente catturato.

La riga `definitions` è il costo fisso: quanto pesa `tools/list` di questo server prima che venga effettuata una singola chiamata. La colonna `DEF` lo suddivide per strumento e `RESULT` è ciò che le risposte di ciascuno strumento sono costate finora. La tabella rimane ordinata per errori e latenza, quindi scorri `DEF` per trovare le definizioni costose; l'export le elenca prima quelle più pesanti. Una riga sotto la tabella indica il singolo risultato più pesante, che un totale nasconde.

Le cifre relative alle definizioni sono il JSON con rimossi gli spazi bianchi insignificanti, quindi un server che stampa in modo pretty il proprio `tools/list` non viene conteggiato come più costoso di uno che non lo fa, e lo stesso server misura uguale tra diverse catture. `RESULT` è il numero di byte così come sono arrivati: un risultato è un payload una tantum, non un contratto che vale la pena normalizzare.```bash
mcpsnoop export -T json | jq '.summary.definitions'

L'export riporta le stesse cifre, per strumento e suddivise in byte di descrizione e di schema, così una descrizione voluminosa e uno schema voluminoso restano separabili e entrambi possono essere monitorati tra una cattura e l'altra. mcpsnoop diff ti dice se una descrizione o uno schema è cambiato tra due sessioni; l'export è il punto in cui vive la dimensione di quel cambiamento.

Questi sono byte, non token. Il conteggio dei token dipende dal modello, quindi misurarli significherebbe includere un tokenizer e scegliere di chi. I byte sono esatti e puoi applicare il tuo rapporto. Una tools/list non terminata riporta ciò che ha visto come minimo e lo dichiara, invece di spacciare una somma parziale per il totale.

Rilevare un client che manomette lo stato del server

Nel pattern multi round-trip, il server consegna al client un requestState opaco e il client deve restituirlo invariato al retry. Al server viene detto di trattarlo come input controllato dall'attaccante, perché un client che lo manomette può tentare di alterare il comportamento del server o aggirare un controllo di autorizzazione.

In mezzo al canale, mcpsnoop vede il valore uscire e tornare, quindi può dire quando il contratto è stato violato. Tre sono i modi in cui può rompersi, ciascuno segnalato come avviso di protocollo al retry.

SegnalazioneSignificato
MRTR retry changed requestStateil client ha restituito qualcosa di diverso da quanto emesso dal server
MRTR retry is missing requestStateil server ne ha emesso uno e il retry lo ha omesso
MRTR retry invented requestStateil retry ne conteneva uno che il server non ha mai emesso

Queste sono violazioni di protocollo da parte del client, non osservazioni nostre, quindi viaggiano sul normale segnale di avviso e un'esecuzione predefinita di check fallisce su una di esse. È una scelta deliberata. Un client che manomette lo stato del server merita di bloccare una build.

Il valore stesso non viene mai mostrato né registrato, e nulla lo decodifica o lo analizza. Può essere un blob cifrato che trasporta un principal e un token, e il confronto di byte opachi è l'intero controllo.

Un caso è fuori portata. Quando un server risponde con un requestState e nessun inputRequests, un retry manomesso non corrisponde a nulla e non restituisce alcuna chiave, quindi non resta nulla per collegarlo alla richiesta originale e viene letto come una chiamata non correlata piuttosto che come una violazione.

Osservare da un'altra macchina

Tieni la cattura sulla macchina in cui avviene il traffico e usa SSH per il salto di rete, così mcpsnoop non ha mai bisogno di un trasporto remoto proprio.

Vista live

Esegui la TUI sulla tua workstation e inoltra la socket mcpsnoop della macchina remota verso di essa. Il tunnel live usa l'inoltro di Unix socket via SSH, quindi entrambe le estremità devono eseguire Linux o macOS. Su Windows, usa la copia del log post-mortem qui sotto.```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

Il socket risiede nella directory di stato del remoto, risolta come `MCPSNOOP_HOME`,
altrimenti `XDG_STATE_HOME/mcpsnoop`, altrimenti `~/.local/state/mcpsnoop`. Di default mcpsnoop
assume la home Linux `/home/<user>` dal tuo `user@host` e stampa un promemoria
su stderr ogni volta che ripiega su quella ipotesi. Se il remoto risolve altrove,
indica l'unico componente non predefinito.```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

Trasmetti in streaming una sessione remota direttamente nella TUI via SSH, senza bisogno di copie locali.```bash ssh remote-user@remote-host 'cat ~/.local/state/mcpsnoop/sessions/session.jsonl' | mcpsnoop open -

Per conservare invece una copia locale, scp i log nella tua directory delle sessioni ed esegui
la TUI come al solito.```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

Sicurezza

mcpsnoop esegue il comando del server che avvolgi, quindi avvolgi solo server di cui ti fidi ed esegui quelli non affidabili in un container. Non esegue mai nulla che non hai inserito nella configurazione del client.

I frame catturati possono includere prompt, argomenti degli strumenti, credenziali e risultati degli strumenti. Se i payload possono trasportare segreti, attiva la redazione per ripulire le copie della traccia osservata mentre i byte inoltrati passano comunque invariati.

La redazione basata su chiavi sostituisce interi valori sotto le chiavi JSON corrispondenti e lo stesso set di chiavi viene applicato al meglio delle possibilità agli argomenti della riga di comando del server avvolto, quindi --api-key=sk-x e --token sk-x vengono ripuliti con --redact-secrets. Un argomento che trasporta un segreto senza un nome di flag riconoscibile non può essere rilevato.

La redazione basata su percorsi sostituisce solo i valori selezionati da un'espressione JSONPath, il che è utile quando un nome di chiave comune è sensibile in una posizione ma sicuro in un'altra. Ripeti --redact-path per ripulire più di una posizione.

La redazione basata su valori applica espressioni regolari ai valori stringa osservati, al testo di stderr e ai frame di testo non JSON.

Tutte e tre sono al meglio delle possibilità. Le espressioni regolari possono non vedere i segreti, abbinare in eccesso testo innocuo o non riuscire a individuare valori trasformati o codificati.

La redazione non si trasforma mai in un'accusa. Ogni controllo che confronta una cosa osservata con un'altra, un'intestazione di routing con il corpo, un valore Mcp-Param con l'argomento che rispecchia, lo schema di uno strumento con ciò che la revisione richiede a uno strumento, sa quando è stato mcpsnoop a riscrivere i byte e rimane in silenzio invece di segnalare un server per l'impostazione di privacy dell'utente.

La deriva delle definizioni degli strumenti è l'eccezione, e volutamente, poiché attivare la redazione cambia ciò che viene registrato e quindi ciò che una baseline contiene. Vedi Rileva la deriva delle definizioni degli strumenti.```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+'

Per i flussi di lavoro remoti, usa il tunnelling SSH o il trasferimento file via SSH, così autenticazione del trasporto, cifratura, verifica dell'host, rotazione delle chiavi e policy di audit restano nella tua configurazione SSH esistente.

## Contributing

Issue e pull request sono benvenuti. Vedi [CONTRIBUTING.md](https://github.com/kerlenton/mcpsnoop/blob/HEAD/CONTRIBUTING.md) per i dettagli.

## License

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

Categorie