
Wireshark pour MCP. Un proxy transparent qui affiche chaque appel d'outil réel entre votre client IA et vos serveurs MCP, en direct dans votre terminal.
Wireshark pour MCP. Un proxy transparent qui affiche chaque véritable appel d'outil entre votre client IA et vos serveurs MCP, en direct dans votre terminal.
L'Inspecteur MCP officiel se connecte en tant que client propre, donc il ne voit jamais ce que votre client (Cursor, Claude Code, Codex) envoie réellement à votre serveur. Et tout ce qui attend qu'une requête arrive ne peut pas afficher l'appel que le modèle n'a jamais fait, ou qu'il a fait avec les mauvais arguments. Lorsqu'un outil n'est pas appelé silencieusement, que les capacités ne correspondent pas, ou qu'un appel reste bloqué, vous vous retrouvez à fouiller les journaux et à deviner.
mcpsnoop se place plutôt dans le chemin de données réel. Enveloppez la commande de votre serveur avec lui et regardez chaque trame JSON-RPC en direct, pendant que votre vrai client et votre serveur communiquent.
Cette page est également la fiche de l'Action GitHub mcpsnoop, voici donc l'ensemble. Elle vérifie une session capturée, consigne chaque constat comme une alerte d'analyse de code, et fait échouer le job sur ce que vous avez conditionné.```yaml permissions: security-events: write contents: read
steps:
Épinglez la version que vous souhaitez. La plus récente se trouve sur la
[page des versions](https://github.com/kerlenton/mcpsnoop/releases). Chaque entrée,
la signification des codes de sortie, et comment le brancher sans l'action sont détaillés
dans [L'action GitHub](#the-github-action) plus bas.
## Démarrage rapide
Voyez-le immédiatement, sans rien à configurer.```bash
mcpsnoop demo
Pour l’utiliser en conditions réelles, enveloppez votre serveur dans la configuration MCP de votre client.```json { "mcpServers": { "my-server": { "command": "mcpsnoop", "args": ["--", "node", "build/index.js"] } } }
Tout ce qui suit `--` est la commande qui lance normalement votre serveur. Remplacez-la par
ce que vous utilisez déjà, comme `python server.py`, `npx -y @scope/server`, ou un
binaire compilé.
Sur Claude Desktop, vous n'avez pas à effectuer cette modification à la main.```bash
mcpsnoop wrap my-server # route my-server through mcpsnoop
mcpsnoop unwrap my-server # put it back
wrap trouve claude_desktop_config.json, le copie vers
claude_desktop_config.json.mcpsnoop.bak la première fois, et ne réécrit que
l'entrée de ce seul serveur, afin que votre mise en forme et tous les autres serveurs soient laissés intacts.
Dans l'entrée réécrite, les clés reviennent dans l'ordre alphabétique. unwrap
restaure le fichier et supprime la sauvegarde une fois qu'aucun serveur n'est plus enveloppé.
Redémarrez Claude Desktop après l'une ou l'autre opération, car les serveurs MCP sont lancés une seule fois au
démarrage.
Utilisez ensuite votre client comme d'habitude et ouvrez l'interface utilisateur.```bash mcpsnoop
Aucun drapeau, aucun chemin de socket, aucun ordre de démarrage à retenir. Le shim et l’interface se trouvent
mutuellement, et l’interface reconstitue les sessions passées depuis le disque.
Pour un serveur HTTP streamable, exécutez mcpsnoop en tant que proxy inverse.```bash
mcpsnoop http --target http://localhost:3000/mcp --listen :7000
Le statut HTTP de chaque réponse apparaît dans le flux, de sorte qu’une réponse qui ne porte
aucun message JSON-RPC propre reste un cadre visible plutôt que rien : le
défi 401, le 403 sur un Origin rejeté, le 202 qui accuse réception d’une
notification, et le 502 lorsque la cible est totalement injoignable. L’en-tête WWW-Authenticate d’un 401 est conservé tel quel et affiché dans l’inspecteur,
car il nomme le schéma d’authentification et les métadonnées de ressource à consulter ensuite. Filtrez par
statut avec status:401 dans la TUI, ou par toute erreur avec status:err. Un 4xx
ou un 5xx compte comme une erreur, donc une exécution par défaut de mcpsnoop check échoue dessus.
Pas de serveur à vous ? Essayez pour de vrai contre un serveur de test publié, piloté par votre propre client. Pour inspecter une session après coup, voir revoir les sessions passées à partir des journaux.
Si vous réutilisez les mêmes options de shim dans un projet, placez-les dans un
fichier .mcpsnoop.toml dans le répertoire de travail courant.```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
Répétez `redact-key`, `redact-value` et `redact-path` sur leurs propres lignes pour en ajouter plus d’un de chaque.
Ce sont toutes les clés qu’il prend en charge.
Le fichier n’est recherché que dans le répertoire de travail actuel, pas dans les répertoires parents.
Les indicateurs explicites de la ligne de commande remplacent les valeurs du fichier de configuration.
## Commandes
| Commande | Ce qu’elle fait |
|---|---|
| `mcpsnoop -- <server>` | enveloppe un serveur stdio comme un shim transparent |
| `mcpsnoop` | ouvre la TUI en direct |
| `mcpsnoop http --target <url>` | sert de proxy à un serveur HTTP streamable |
| `mcpsnoop export` | rend une session en json, html, text, har ou otlp |
| `mcpsnoop check` | fait échouer la CI sur les erreurs, les trames invalides, les avertissements, les incohérences de routage, les appels bloqués, les résultats tardifs ou un budget de latence |
| `mcpsnoop baseline` | inspecte, accepte ou réinitialise les définitions d’outils approuvées |
| `mcpsnoop diff` | compare les outils et les appels entre deux sessions capturées |
| `mcpsnoop open` | ouvre une session enregistrée dans la TUI |
| `mcpsnoop inventory` | liste chaque serveur qui a transité par mcpsnoop sur cette machine |
| `mcpsnoop stats` | regroupe chaque capture stockée en une ligne par serveur et outil |
| `mcpsnoop prune` | supprime les journaux de sessions enregistrés plus anciens qu’un seuil |
| `mcpsnoop wrap <server>` | route l’un des serveurs de Claude Desktop via mcpsnoop |
| `mcpsnoop unwrap <server>` | remet l’entrée de ce serveur dans son état d’origine |
| `mcpsnoop remote <user@host>` | affiche la commande de tunnel SSH |
| `mcpsnoop demo` | joue une session scénarisée |
Exécutez `mcpsnoop help` pour la liste complète, ou `mcpsnoop help <command>` pour les indicateurs d’une commande.
## Comparaison
| | MCP Inspector | mcpsnoop |
|---|:---:|:---:|
| Voit votre trafic réel client et serveur | non | oui |
| Signale les appels bloqués et les erreurs de flux | non | oui |
| Signale les sorties parasites qui corrompent le flux | non | oui |
| Signale les trames JSON-RPC malformées | non | oui |
| Détecte la dérive des définitions d’outils après approbation | non | oui |
| Interface terminal interactive | non | oui |
| Zéro configuration, sans indicateurs ni ordre | non | oui |
| Inspecteur de capacités | partiel | oui |
| Rejoue un appel capturé | non | oui, via stdio et via HTTP |
| Export de session (json / html / text / otlp) | non | oui |
| Binaire unique, sans dépendances d’exécution | non | oui |
## Installation
### npm
Aucune chaîne d’outils Go requise. La plupart des serveurs MCP sont écrits en Node ou Python, c’est donc le chemin le plus court.```bash
npx mcpsnoop -- node build/index.js
The npm package ships no code of its own. Six platform packages each carry one
build, and npm installs the single one that matches your machine, so there is
nothing to download at install time and nothing to unblock in a proxy. To keep it
around rather than fetching it each run, npm i -g mcpsnoop.
go install github.com/kerlenton/mcpsnoop/cmd/mcpsnoop@latest
### Homebrew```bash
brew install mcpsnoop
Binaires précompilés pour chaque plateforme sur la page Releases.
mcpsnoop fournit des complétions pour bash, zsh, fish et PowerShell. Exécutez
mcpsnoop completion <shell> --help pour les étapes de configuration, qui couvrent l'activation
de la complétion et le chemin d'installation pour votre système d'exploitation.
mcpsnoop regroupe deux rôles dans un seul binaire. mcpsnoop -- <server> est le
shim transparent que votre client lance, transférant les octets tels quels tout en envoyant une copie de chaque
trame au hub. mcpsnoop sans argument est ce hub et son interface TUI en direct. Ils
communiquent via un socket bien connu et des journaux sur disque, de sorte qu'aucun des deux n'a besoin de démarrer en premier.
Le hub charge par défaut les 100 sessions enregistrées les plus récentes, ce qui maintient le travail de démarrage
borné sans supprimer les traces plus anciennes. Utilisez mcpsnoop --history-limit N pour choisir
une autre limite, ou mcpsnoop --history-limit 0 pour charger tout l'historique. Les sessions plus
anciennes restent disponibles via mcpsnoop open <session-id> et
mcpsnoop export <session-id>.
La limite d'historique borne le nombre de sessions chargées. À l'intérieur d'une session, la TUI en direct est bornée deux fois, car un hub laissé à surveiller un serveur bavard croîtrait sinon jusqu'à être tué. Il conserve au maximum 64 Mio de corps de trames, en libérant les plus anciennes en premier, et au maximum 200 000 trames, en supprimant entièrement les plus anciennes au-delà. La première limite est celle que rencontre une capture de gros payloads et la seconde celle d'un long flux de petites notifications.
Aucune des deux limites ne modifie une réponse. Une trame dont le corps a été libéré conserve sa ligne,
son verdict et sa place dans la chronologie, et son inspecteur indique que le corps est
absent plutôt que d'afficher une trame vide. Une trame entièrement supprimée emporte d'abord
les statistiques de son appel d'outil dans les totaux cumulés, de sorte que le résumé
des outils et ce que le serveur vous coûte en contexte décrivent chaque appel effectué par la
session, et pas seulement les récents. Le pied de page du flux indique combien de trames plus anciennes sont
uniquement sur disque, et r refuse une trame dont les paramètres ne sont plus conservés plutôt que de
rejouer autre chose.
mcpsnoop open <session-id> lit le journal et le conserve entièrement, et l'exportation
depuis la TUI lit aussi le journal, de sorte qu'aucun des deux n'est borné. check, export et
diff construisent volontairement un stock non borné, car une passerelle qui sous-rapporte sur une
grosse capture est pire qu'une qui utilise la mémoire.
La limite d'historique borne ce qui est chargé. mcpsnoop prune borne ce qui est conservé.
Il supprime les journaux de sessions enregistrées plus anciens qu'un seuil, et ne s'exécute jamais de lui-même.```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` est requis (il n'y a pas de valeur par défaut qui supprimerait quoi que ce soit) et
accepte un nombre de jours comme `30d` ou une durée Go comme `72h`. Les références d'outils sont
laissées intactes, car une référence est associée à une étiquette de serveur plutôt qu'à une session.
Comme il se trouve dans le flux réel, et non en marge comme l'Inspector, il
voit exactement ce que votre client et votre serveur réels se disent, quel que soit le langage
dans lequel le serveur est écrit.
## Raccourcis clavier
| Touche | Action | | Touche | Action |
|---|---|---|---|---|
| `enter` | inspecter / explorer | | `/` | filtrer |
| `esc` | retour | | `:` | commande |
| `j` / `k` | déplacer | | `r` / `R` | rejouer / modifier et rejouer |
| `g` / `G` | haut / bas | | `c` | capacités |
| `ctrl-f` / `ctrl-b` | page | | `s` | résumé de l'outil |
| `p` | pause | | `y` | copier |
| `shift`+`<touche>` | trier par colonne | | `e` | exporter |
| `ctrl-d` | supprimer la session | | `f` | suivre |
| `?` | aide | | | |
Appuyez sur `?` dans l'application pour la liste complète.
## Filtrer le flux
Appuyez sur `/` dans une session et combinez des jetons séparés par des espaces, reliés par ET. Le texte brut
correspond à la méthode, à l'outil, à l'identifiant et à la charge utile.
| Jeton | Filtre par | Exemple |
|---|---|---|
| `tool:` | nom de l'outil | `tool:search` |
| `method:` | méthode JSON-RPC | `method:tools/call` |
| `id:` | identifiant de requête, et toute nouvelle tentative qui le poursuit | `id:7` |
| `task:` | identifiant de tâche | `task:01J...` |
| `dir:` | direction (`c2s`, `s2c`) | `dir:s2c` |
| `kind:` | type de trame (`req`, `resp`, `notify`, `stderr`, `invalid`) | `kind:invalid` |
| `status:` | résultat de l'appel (`ok`, `error`, `cancel`, `late`, `cancelled`, `pending`, `bad`, `warn`, `mismatch`, ou un statut HTTP comme `401`) | `status:error` |
Empilez les jetons pour être précis.```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)
The last one only finds anything on a server speaking 2025-11-25 or earlier. The 2026-07-28 revision removed server-initiated requests, and a server that needs something from the client now answers the client's own request asking for it, then the client retries. mcpsnoop links those retries back to the request they continue, so the exchange reads as one call rather than several.
Turn any captured session into a portable file.```bash mcpsnoop export -T json|html|text|har|otlp [-o file|-] [session-id|log.jsonl|-]
| Format | Ce que vous obtenez |
|---|---|
| `json` | appels corrélés, comptages par outil et latences p50/p95/p99, appels les plus lents, capacités et trames brutes |
| `html` | un fichier navigateur autonome avec recherche et JSON repliable |
| `text` | un vidage en texte brut lisible |
| `har` | une entrée par appel corrélé, ouvrable dans les devtools du navigateur et tout autre outil lisant le HAR |
| `otlp` | JSON OTLP avec une span par appel corrélé, avec le contexte de trace W3C rejoignant les traces de l'appelant lorsqu'il est présent, et une trace par session sinon |
MCP n'est pas HTTP, donc l'URL, le code de statut et les timings d'une entrée HAR sont un
mappage délibéré de chaque appel plutôt qu'une transcription filaire.
Pour OTLP, le `_meta.traceparent` d'une requête fournit la trace et les IDs de span
parent de cet appel, et `_meta.tracestate` voyage avec la span. Lorsque le traceparent est
absent ou invalide, mcpsnoop conserve la trace dérivée de la session et ne transporte aucun état.
mcpsnoop observe plutôt qu'il ne participe, donc il n'ajoute aucune entrée de fournisseur qui lui soit propre
et transmet l'état de l'appelant sans modification.```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
Omettez -o pour écrire sur stdout, et omettez la session pour prendre la plus récente, ou passez
- pour lire du JSONL depuis stdin. Dans la TUI, appuyez sur e pour exporter la session
sélectionnée en HTML, ou exécutez :export json|html|text|har|otlp [path] depuis le mode commande.
Pour nettoyer une capture existante avant de l'inspecter ou de la partager, passez les mêmes
drapeaux de redaction utilisés lors de la capture à 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'
Ces drapeaux réécrivent le fichier exporté ou la vue TUI en mémoire, jamais le
JSONL source. `export` refuse une sortie qui porte le même nom que son fichier
d'entrée, et écrit via un fichier temporaire renommé en place, de sorte qu'une
exécution qui échoue laisse le fichier précédent intact.
Les `inputSchema` et `outputSchema` d'un outil, telles qu'annoncées dans un
résultat `tools/list`, ne sont pas touchées par `--redact-key` et
`--redact-secrets`, pour trois raisons.
- Un nom dans un schéma est une déclaration de type plutôt qu'une valeur.
- Le nom lui-même reste dans le journal de toute façon.
- Nettoyer le sous-schéma sous une propriété appelée `token` emporterait avec
lui les propres vérifications de l'outil.
L'exemption ne vaut qu'à cette position, donc un argument qui s'appelle par
hasard `inputSchema` est nettoyé comme n'importe quel autre, et elle s'arrête à
`default`, `const`, `examples` et `enum`, qui contiennent des données plutôt
que de la structure. Utilisez `--redact-path` pour nommer quelque chose à
l'intérieur d'un schéma, ou `--redact-value`, qui fait correspondre le texte où
qu'il se trouve, sauf dans les deux mots-clés que mcpsnoop analyse, `type` et
`x-mcp-header`.
Ce que chaque drapeau atteint diffère, alors vérifiez le résultat plutôt que de
supposer. Les quatre nettoient les charges utiles JSON-RPC, et `--redact-key`,
`--redact-path` et `--redact-secrets` n'atteignent que celles-ci. Seul
`--redact-value` nettoie aussi stderr, les autres textes non-JSON et
l'intérieur d'une chaîne. Un en-tête `Mcp-Param-*` est nettoyé en même temps
que la valeur du corps qu'il reflète. Les autres métadonnées d'enveloppe, les
étiquettes de serveur, `Mcp-Name`, `Mcp-Method` et le statut HTTP, sont
laissées telles que capturées. La rédaction est au mieux, alors utilisez un
chemin de sortie séparé et lisez le résultat avant de le partager.
### Diffuser les appels terminés vers un collecteur OTLP
Envoyez des spans pendant que le proxy tourne en le pointant vers un point de
terminaison de traces OTLP/HTTP JSON. Répétez `--otlp-header` pour
l'authentification du collecteur ou les en-têtes de locataire.```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 livraison est au mieux et ne bloque jamais le trafic MCP mandaté. Si le collecteur est indisponible, mcpsnoop réessaie en arrière-plan et abandonne les nouvelles trames de trace lorsque sa file d'attente bornée est pleine. Le journal de session JSONL normal reste l'enregistrement durable.
Comparez deux sessions enregistrées par identifiant ou chemin JSONL.```bash mcpsnoop diff before-session after-session mcpsnoop diff old.jsonl new.jsonl
Le rapport montre les outils qui ont été ajoutés ou supprimés, les modifications de description et de `inputSchema`,
les appels d'outils correspondants dont le statut a changé, ainsi que les écarts de durée notables. Les appels
sont mis en correspondance par nom d'outil et arguments, de sorte que les appels réordonnés sont toujours comparés correctement.
Par défaut, les changements de durée doivent différer d'au moins 100 ms et d'un facteur 2. Utilisez
`--duration-threshold` et `--duration-ratio` pour ajuster ces seuils.
Passez `--exit-code` pour déclencher l'IC en cas de régressions. Il se termine avec un code non nul lorsque la session
« après » :
- supprime un outil
- modifie la description, le titre, le schéma d'entrée, le schéma de sortie ou les annotations d'un outil
- contient un appel dont le statut s'est dégradé
- ralentit
Un changement d'icône ne déclenche pas cela, car il modifie l'apparence d'un outil sans changer ce qu'il
fait. Les améliorations, c'est-à-dire les outils ajoutés, les appels corrigés et les accélérations, se terminent toujours
avec un code zéro.
## Vérification des sessions dans l'IC
Filtrez une session d'agent enregistrée sur les erreurs, la corruption du flux, les avertissements de protocole,
les incohérences d'en-tête de routage, les appels n'ayant jamais reçu de réponse, les trames supprimées qui
laissent la capture incomplète, la dérive des définitions d'outils ou l'utilisation de fonctionnalités de protocole
obsolètes.```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 et warn font échouer la vérification à eux seuls. Les autres sont facultatifs.
Passez un sous-ensemble séparé par des virgules pour ne contrôler que ce qui intéresse un travail, omettez la
session pour vérifier la capture la plus récente, ou utilisez - pour lire du JSONL depuis stdin.
| Signal | Fait échouer sur |
|---|---|
error | un appel répondant avec une erreur JSON-RPC, un résultat marqué isError, ou une tâche qui s'est terminée par un échec |
invalid | une trame sur le canal de protocole qui n'est pas du JSON-RPC valide, généralement un serveur journalisant sur stdout |
warn | une trame enfreignant une attente définie par la spécification MCP ou JSON-RPC |
mismatch | un en-tête de routage en désaccord avec le corps, chevauchant un lot, ou manquant là où la révision l'exige |
pending | une requête encore ouverte à la fin de la capture, laissant ainsi l'appelant en attente |
late-result | une réponse arrivée après l'annulation de sa requête |
drift | une définition d'outil annoncée qui change après l'approbation de la référence |
deprecated | une fonctionnalité que la spécification a dépréciée |
incomplete | des trames supprimées en amont, ce qui fait de chaque autre compte un minimum plutôt qu'un total |
schema | un schéma annoncé utilisant une construction ou un dialecte qui se propage mal entre les clients |
Chaque signal est compté qu'il soit ou non bloquant, donc une exécution indique ce qu'elle a trouvé avant que vous décidiez de ce qui doit faire échouer la vérification.``` 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
The dropped-frame count travels with the artifacts too, so a capture that
understates itself says so wherever it is opened:
- `missing_frames` in the JSON export
- `log.comment` in HAR
- the `mcpsnoop.session.missing_frames` resource attribute in OTLP```bash
mcpsnoop check build-agent
mcpsnoop check --fail-on error,invalid artifacts/session.jsonl
mcpsnoop check --fail-on mismatch gateway-run.jsonl
Le code de sortie indique laquelle des deux situations s'est produite, et un wrapper CI a besoin de la différence. 1 signifie que la vérification a été exécutée et que quelque chose a échoué au contrôle, donc les résultats sont réels et méritent d'être publiés. 2 signifie que la vérification n'a jamais eu lieu : un chemin qui n'existe pas, un fichier qui n'est pas un journal de session, un répertoire d'état ne contenant rien, un indicateur qui ne s'analyse pas. Rien n'est écrit sur stdout en cas de code 2, donc un pipeline ne téléverse jamais un rapport vide comme s'il s'agissait d'un verdict.
Au-delà des comptages de signaux, affirmez la forme de l'exécution. Ceux-ci se composent les uns avec les
autres et avec --fail-on, et toute défaillance se termine par le code 1, celui qui signifie que la vérification
a été exécutée et a trouvé quelque chose.
| Indicateur | Échoue lorsque |
|---|---|
--max-duration <dur> | un ou plusieurs appels d'outil terminés ont dépassé le budget, signalant leur nombre et le pire appel |
--expect-tool <name> | l'outil nommé n'a jamais été appelé (répétable) |
--forbid-tool <name> | l'outil nommé a été appelé (répétable) |
mcpsnoop check --expect-tool search --forbid-tool delete --max-duration 2s run.jsonl
### Signalez-le là où la CI regarde déjà
`--format junit` écrit un `<testcase>` par signal et par session, et ses échecs
suivent la même sélection `--fail-on` que la sortie texte.```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 écrit un journal SARIF 2.1.0 à la place. Là où junit rapporte un
agrégat par signal, SARIF rapporte un résultat par constatation, portant la session,
le Seq de la frame et le texte d'avertissement ou de dérive propre à la frame, et
pointant vers la ligne du journal à partir de laquelle la frame a été décodée. Un
signal nommé dans --fail-on est rapporté au niveau error et un signal hors de
celui-ci au niveau note, de sorte que le rapport et la porte ne divergent jamais.
Un résultat pointe vers le journal d'où provient la constatation, et la manière dépend de l'endroit où le journal a été lu.
file:// absolue.L'alerte s'affiche avec ses lignes environnantes uniquement lorsque ce chemin est
un fichier dans le commit analysé, donc une capture que le workflow a générée dans
artifacts/ ouvre une alerte portant le message, la règle et le numéro de ligne,
mais sans vue source. Commiter une capture que vous souhaitez afficher en entier
est le seul moyen d'en obtenir une.
Le code scanning rejette un fichier dont l'exécution contient plus de 25 000
résultats et n'affiche que les 5 000 premiers de ce qu'il accepte, donc le rapport
est plafonné à 5 000 : les constatations sur lesquelles la porte a échoué en
premier, puis un résultat mcpsnoop/report-truncated indiquant combien ont été
omises. Les formats texte et junit restent complets.
Tout ce qui suit est ce que l'action fait pour vous. Elle installe mcpsnoop, vérifie la capture, dépose les constatations dans l'onglet Sécurité, et fait échouer le job sur ce sur quoi vous avez défini la porte.```yaml permissions: security-events: write contents: read
steps:
Épingler une release, celle que vous voulez. La plus récente se trouve sur la
[page des releases](https://github.com/kerlenton/mcpsnoop/releases). Il n’y a
volontairement pas de `v1` flottant. La release épinglée est aussi le binaire
que l’action installe, donc les deux ne peuvent jamais diverger et il n’y a pas
de version par défaut qui risque de devenir obsolète.
| Entrée | |
|---|---|
| `session` | la capture `.jsonl` à vérifier, relative à la racine du dépôt. Requis |
| `fail-on` | comme `--fail-on`, avec pour défaut ce que le CLI utilise par défaut |
| `args` | tous les autres drapeaux de `check`, entre guillemets comme sur une ligne de commande. `--format` est refusé, car l’action lit le rapport |
| `upload-sarif` | envoyer le rapport au code scanning. `true` |
| `category` | l’espace de noms du code scanning. `mcpsnoop`. Variez-le par branche d’une matrice, sinon les branches s’écrasent mutuellement |
| `fail-on-findings` | faire échouer le job sur un constat. `true`. Mettez `false` pour déposer les alertes et laisser la vérification requise du code scanning décider |
| `version` | quelle version de mcpsnoop installer. Par défaut, la release que vous avez épinglée |
| `install` | `false` lorsque mcpsnoop est déjà dans le PATH, ce qui est la voie à suivre sur une plateforme pour laquelle aucune release n’est construite |
Les sorties sont `outcome`, `sarif` et `exit-code`. `outcome` vaut `passed`,
`findings` ou `error`, et la troisième mérite d’être traitée séparément. Elle
signifie que rien n’a été vérifié, ce qui n’est pas la même chose que rien n’a
été trouvé. **Une exécution qui n’a pas pu vérifier fait échouer le job, quoi
que dise `fail-on-findings`**, car un pipeline qui passe au vert sans avoir
rien vérifié est pire qu’un qui échoue.
Le job a besoin de `security-events: write`, sinon l’envoi répond 403. Mettez
`upload-sarif: false` dans un dépôt sans code scanning.
### Ou branchez-le vous-même
L’action est quatre étapes et aucune magie. Le faire à la main demande le même
soin qu’il faut. L’envoi doit s’exécuter sur les exécutions qui ont un rapport,
c’est-à-dire celles qui sont sorties avec 0 ou 1 et non celles sorties avec 2,
et l’étape qui fait échouer le job doit venir après, sinon les constats
n’atteignent jamais l’onglet pour lequel ils existent.```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
id: check
run: |
code=0
mcpsnoop check --format sarif artifacts/session.jsonl > mcpsnoop.sarif || code=$?
echo "exit-code=$code" >> "$GITHUB_OUTPUT"
# 2 means the check never happened, so there is no report to publish and
# nothing was verified. Stop here rather than uploading an empty file.
[ "$code" -le 1 ] || exit 1
- name: Upload mcpsnoop SARIF report
if: ${{ !cancelled() }}
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: mcpsnoop.sarif
category: mcpsnoop
- name: Fail on findings
# Separate, and after the upload, so the findings reach the Security tab on
# exactly the runs that have some.
if: ${{ !cancelled() && steps.check.outputs.exit-code == '1' }}
run: exit 1
Sur le transport HTTP streamable, une passerelle route en fonction de Mcp-Method et Mcp-Name
tandis que le serveur lit le corps, donc un en-tête qui contredit le corps signifie
que les deux regardent deux requêtes différentes. Le signal mismatch couvre cela,
un en-tête porté par un lot qu'il ne peut pas adresser, et un en-tête requis manquant
entièrement.
En 2026-07-28, un en-tête de routage manquant est une erreur de validation, et un serveur
conforme rejette la requête avec 400 et -32020. mcpsnoop ne le signale qu'une
fois que la session est connue pour parler cette révision ou une version ultérieure, car les révisions antérieures ne
définissent pas du tout ces en-têtes et les omettre y est correct. Le rejet
-32020 propre au serveur compte comme le même signal.
Un nom ou un URI de ressource qui ne tiendra pas dans une valeur de champ HTTP voyage en Base64 dans
un sentinelle =?base64?…?=, qui est décodé avant la comparaison, donc un client
qui encode correctement n'est jamais signalé.
Sur les requêtes HTTP tools/call, mcpsnoop affiche également chaque en-tête
Mcp-Param-{Name} et, lorsque la définition d'outil annoncée correspondante est connue, la compare avec le
chemin d'argument annoté. Les propriétés imbriquées, la sentinelle Base64, les booléens et les
entiers sûrs équivalents numériquement sont gérés sans faux positifs de comparaison de chaînes.
Les en-têtes de paramètres inconnus et les sessions sans définition d'outil correspondante
restent observationnels. La rédaction basée sur les clés et les valeurs s'applique aux valeurs
d'en-têtes de paramètres capturées avant qu'elles n'atteignent une sortie, et une valeur que mcpsnoop a
lui-même masquée n'est jamais signalée comme un désaccord.
Les en-têtes de routage ci-dessus étaient les seuls qu'une trame portait, donc le reste des
en-têtes obligatoires du transport HTTP streamable n'atteignait rien qui puisse les vérifier.
Content-Type était le cas le plus net. Le côté réponse le lisait déjà pour
distinguer un flux SSE d'un corps JSON, puis le jetait.
Une trame HTTP porte désormais les en-têtes pour lesquels le transport énonce des règles, et deux de ces règles sont vérifiables.
| Règle | Signalé comme |
|---|---|
le client DOIT envoyer un Accept listant à la fois application/json et text/event-stream | warn sur la requête |
un serveur répondant à une requête JSON-RPC DOIT renvoyer Content-Type: application/json ou text/event-stream | warn sur la réponse |
Les deux phrases se lisent de la même manière en 2025-11-25 et 2026-07-28, donc contrairement aux
vérifications de dérive et d'extension, celles-ci n'ont pas besoin de porte de révision. Origin est également enregistré, car
les serveurs DOIVENT le valider et DOIVENT répondre 403 lorsqu'il est invalide, mais
mcpsnoop ne peut pas connaître vos origines autorisées, il affiche donc la valeur plutôt que de la
juger.
Les caractères génériques comptent. Un client envoyant */* a proposé les deux types et n'est jamais
signalé, et un paramètre charset sur un Content-Type est ignoré. Un journal
capturé avant que mcpsnoop n'enregistre ces en-têtes reste silencieux plutôt que de
signaler chaque trame qu'il contient pour un en-tête que personne n'a noté, et stdio ne les a
jamais du tout.
Authorization n'est délibérément pas capturé. Transformer un défi en faits de jeton
est son propre problème et mettre un jeton porteur sur disque n'est pas la réponse à
cela. Mcp-Session-Id et Last-Event-ID ne sont pas non plus capturés. La
révision 2026-07-28 a supprimé les deux et dit à un serveur de les ignorer, donc il n'y a
plus de règle à vérifier.
La première tools/list complète observée pour une étiquette de serveur devient sa
référence de confiance. Les sessions ultérieures comparent cette référence champ par champ :
Les outils qui ont été ajoutés ou supprimés sont également comparés, ce qui est une comparaison d'ensemble plutôt que de champ.
Les annotations comptent le plus, car un outil approuvé avec readOnlyHint qui se déclare
ensuite destructif est le retrait de tapis pour lequel cette vérification existe, et la spécification
dit aux clients de traiter les annotations comme non fiables. Le titre et les icônes sont
suivis car ce sont ce que l'utilisateur voit, et la spécification classe le title d'un outil
au-dessus de annotations.title et de son nom. La table des sessions et le résumé des outils
signalent la dérive sans bloquer ni modifier le trafic MCP.
Les annotations sont comparées via leurs valeurs par défaut de spécification, donc un serveur qui commence
à expliciter un indice sur lequel il s'appuyait déjà n'est pas signalé. Une référence
enregistrée avant que mcpsnoop ne suive un champ continue de fonctionner pour les champs qu'elle
enregistre et indique ceux pour lesquels elle ne peut pas répondre. Réenregistrez avec
mcpsnoop baseline --accept une fois que vous faites confiance aux définitions actuelles.
Modifier ce que la rédaction enregistre modifie ce que la dérive compare. Une référence prise
sans --redact-value puis vérifiée contre une capture prise avec une
signale les champs masqués comme modifiés, ce qui est correct, puisque la définition
enregistrée a réellement changé. Réenregistrez avec --accept après avoir modifié les paramètres
de rédaction.
Utilisez une --label stable et unique pour chaque serveur dont le nom de commande ou l'hôte cible
entrerait sinon en collision. Les références sont stockées sous le répertoire d'état normal de mcpsnoop,
donc MCPSNOOP_HOME et XDG_STATE_HOME s'appliquent.```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
Dans le CI éphémère, le répertoire d'état démarre vide, donc une exécution n'a rien à comparer et enregistre la référence au lieu de la vérifier. **Une exécution qui devait échouer en cas de dérive puis n'a rien vérifié ne passe pas**, et indique quel répertoire conserver. C'est le seul cas où l'enregistrement d'une référence constitue un échec. Sans `drift` dans `--fail-on`, l'enregistrer est une opération courante et ne modifie aucun code de sortie.
La référence doit donc survivre entre les exécutions pour qu'un contrôle de dérive ait un sens. Pointez `--baseline` vers un répertoire versionné ou mis en cache, ou définissez `MCPSNOOP_HOME` sur un chemin persisté.```
recorded first-seen tool baseline (trusted, not verified)
check failed: drift
(empty response due to missing input content)```bash mcpsnoop check --fail-on drift --baseline .mcpsnoop/baselines session.jsonl
`drift` est activé sur option pour `check`. La porte par défaut `error,invalid,warn` reste inchangée.
### Détecter une fonctionnalité qu'aucun des deux côtés n'a négociée
SEP-2133 a déplacé les fonctionnalités optionnelles hors du protocole de base et dans des extensions,
annoncées dans la carte `extensions` des capacités de chaque côté. Tasks en fait partie,
donc le 2026-07-28, un `tasks/get`, un `notifications/tasks` ou un `tools/call`
répondu avec un identifiant de tâche n'a de sens que lorsque l'autre côté a déclaré
qu'il parle Tasks.
Lorsque ce n'est pas le cas, la spécification est explicite : la partie prenante DOIT soit
revenir au comportement de base, soit rejeter la requête. Le faire quand même est la raison pour laquelle une fonctionnalité
semble être câblée puis ne fait silencieusement rien, et ce qu'un lecteur obtient
à la place, c'est un `-32601` ou un `-32021` plusieurs trames plus tard, ou une tâche qui ne
progresse jamais. mcpsnoop émet un avertissement sur la trame qui a atteint l'extension et nomme
quel côté ne l'a jamais annoncée.```
tool "slow" answered with a task handle uses the io.modelcontextprotocol/tasks
extension, which the client never advertised
Il s'agit d'un warn, donc une exécution check par défaut échoue dessus. Il reste silencieux chaque fois que la capture ne peut pas montrer ce qui a été négocié, c'est-à-dire une capture qui commence après la poignée de main ou une dont les capacités ont été nettoyées par votre propre rédaction, ainsi que sur les révisions antérieures au 2026-07-28, où tasks/* font partie du protocole de base et où leur utilisation est correcte.
La révision du 2026-07-28 rend obsolètes Roots, Sampling et Logging. Elles continuent de fonctionner pendant au moins un an, donc mcpsnoop les marque plutôt que de les traiter comme des erreurs. Le flux, l'inspecteur de capacités et l'exportation les signalent tous, et chaque marqueur nomme le remplacement.
Deux des trois ne sont désormais accessibles que via une requête multi-allers-retours, où le nom de la méthode se trouve dans la carte inputRequests du serveur plutôt que sur la trame elle-même. Ceux-ci sont également signalés, afin qu'un serveur qui est passé au nouveau modèle ne cesse pas silencieusement de faire des rapports.```bash
mcpsnoop check --fail-on deprecated session.jsonl
Comme `drift`, `deprecated` est opt-in. Une exécution par défaut signale le nombre et reste
verte, donc une session utilisant une fonctionnalité dépréciée encore légale ne fait jamais passer le CI au rouge à elle seule.
### Indicateurs de schéma que les clients gèrent mal
Un serveur peut être parfaitement valide et rester difficile à utiliser pour un agent. Les clients
diffèrent dans la mesure où ils prennent réellement en charge JSON Schema, et un outil que le modèle
continue d’appeler à tort est souvent un outil dont le schéma demandait plus que ce que le client
fournit.
Le résumé des outils, ouvert avec `s`, possède une colonne SCHEMA nommant l’élément le plus notable
du schéma de chaque outil annoncé, avec un `+` final lorsqu’il existe plus d’un type.
| Affiché | Signification |
|---|---|
| `no root` | le `inputSchema` est absent, n’est pas un objet JSON, ou possède un type racine autre que `"object"` |
| `dialect` | un `$schema` nommant un dialecte autre que le 2020-12 par défaut de la révision |
| `ext ref` | un `$ref` pointant hors du document, ce qui est aussi le cas que la spécification déconseille aux implémenteurs de suivre aveuglément |
| `oneOf`, `anyOf`, `allOf`, `not` | un mot-clé de composition, géré de manière incohérente selon les clients |
| `ref` | un `$ref` pointant à l’intérieur du même document |
| `untyped` | une propriété qui ne déclare aucun type ni aucun autre moyen d’indiquer ce qu’elle accepte |
Tous sauf le premier sont des observations plutôt que des verdicts. Un schéma utilisant `oneOf`
n’est pas faux, seulement susceptible d’être lu différemment par différents clients, et un
schéma peut déclarer le dialecte qu’il souhaite. `no root` est l’exception : la
définition `Tool` exige `inputSchema` et fixe son type racine à `"object"`, donc
un client validant un listing rejette cet outil d’emblée et il ne devient jamais
appelable, sans aucune indication sur le fil pour expliquer pourquoi. `no root` mène la colonne pour
cette raison, et un schéma que la propre rédaction de mcpsnoop a masqué n’est jamais signalé,
puisqu’un schéma illisible n’est pas un schéma erroné.
Cette répartition décide de ce que `check` en fait. `no root` est un avertissement sur la trame
`tools/list`, donc il échoue à la porte par défaut `error,invalid,warn` sans
aucun indicateur, ce qui est le but : un serveur qui livre un outil inutilisable répond
normalement à chaque poignée de main et ne reçoit simplement jamais de `tools/call`. Les
observations sont comptées comme `schema_findings` et signalées sous `schema
findings:`, et ne font échouer l’exécution que lorsque vous ajoutez `schema` à `--fail-on`. Les deux
atteignent `--format junit` et `--format sarif`, et `export` transporte la liste
par outil sous `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 colonne porte la couleur d’avertissement et jamais le rouge de la colonne ERR, et mcpsnoop ne modifie toujours rien au trafic qu’il transmet.
Rien n’est résolu ni récupéré. Un $ref externe est reconnu par sa seule
forme, et le schéma qu’il pointe n’est jamais lu.
r réémet un appel capturé contre un serveur en direct. Pour une capture stdio, la
commande figure dans le journal, donc mcpsnoop lance une copie isolée et envoie la
requête à celle-ci. Une capture HTTP n’a aucune commande à lancer, et le point de terminaison qu’elle
enregistre est dépouillé de ses informations utilisateur et de chaque valeur de requête, de sorte qu’il désigne le serveur
sans être une adresse à composer.
Vous indiquez donc où une relecture doit aller, et mcpsnoop ne compose jamais un point de terminaison de production
parce que quelqu’un a appuyé sur une touche.```bash
mcpsnoop open --replay-target https://api.example.com/mcp session.jsonl
mcpsnoop open --replay-target https://api.example.com/mcp
--replay-header 'Authorization: Bearer sk-…' session.jsonl
Sans `--replay-target`, une session HTTP le signale plutôt que de proposer une clé qui ne peut pas fonctionner. Avec cette option, `r` demande toujours confirmation avant le premier envoi d'une session, de la même manière qu'une commande enregistrée est confirmée avant d'être exécutée.
Une information d'identification atteint le serveur via `--replay-header` et nulle part ailleurs. mcpsnoop n'enregistre aucun en-tête `Authorization` et n'en rejoue aucun, donc il n'y a rien de capturé qu'une relecture puisse divulguer.
Le POST rejoué transporte ce que le transport rend obligatoire, ce qu'un POST du corps capturé brut ne fait pas : `MCP-Protocol-Version`, un `Accept` listant à la fois `application/json` et `text/event-stream`, `Mcp-Method`, `Mcp-Name` là où la spécification l'exige, et chaque `Mcp-Param-*` capturé. Ceux-ci sont renvoyés tels quels depuis la capture, sentinelle base64 comprise, donc ils ne peuvent pas être en désaccord avec le corps comme une re-dérivation le pourrait. Le seul en-tête qui n'est pas copié est la version du protocole, car le corps rejoué déclare la révision que mcpsnoop parle et l'en-tête doit correspondre au corps.
`Mcp-Name` est dérivé du corps envoyé plutôt que copié, car la spécification le tire de `params.name` ou `params.uri` et exige qu'un serveur rejette un en-tête en désaccord avec le corps, donc une modification qui renomme l'outil enverrait sinon l'ancien nom. Les en-têtes `Mcp-Param-*` reflètent les arguments capturés, donc une relecture modifiée n'en envoie aucun plutôt que d'affirmer quelque chose sur un corps que quelqu'un a réécrit. Une capture ne peut définir des en-têtes que dans cette seule famille. Un journal est un fichier que les gens se transmettent, et lui permettre de nommer n'importe quel en-tête lui permettrait d'écraser les en-têtes obligatoires ou d'ajouter une information d'identification que personne n'a fournie.
Un `Mcp-Param-*` qu'une règle de rédaction a nettoyé arrête la relecture avec une raison. Envoyer le substitut mettrait les propres octets de mcpsnoop sur un serveur en direct comme si un utilisateur les avait saisis.
Une redirection est refusée plutôt que suivie. L'adresse est celle que vous avez nommée et confirmée, et suivre un 307 confierait ce choix à l'extrémité distante, renvoyant le corps et, sur un saut qui ne change que le port, l'information d'identification aussi. mcpsnoop signale où le serveur voulait l'envoyer et vous laisse décider si vous préférez nommer celle-ci à la place.
Une réponse arrivant comme un objet JSON unique et une arrivant comme un flux d'événements sont toutes deux lues, et un échec est nommé plutôt que numéroté :
- un 401 signale le schéma que le serveur a exigé
- un `-32020` signale ce à quoi il s'est opposé
- un 400 ou 404 non-JSON-RPC indique que l'adresse n'est pas un point de terminaison HTTP Streamable de cette révision
### Distinguer la latence du serveur de celle de l'utilisateur
Sous les requêtes multi-allers-retours, un appel d'outil correspond à plusieurs requêtes, et les secondes qu'une personne a passées à répondre à une sollicitation se situent à l'intérieur de l'intervalle. C'est délibéré, car cet intervalle est généralement celui que vous souhaitez le plus voir, mais cela signifie qu'un seul nombre ne peut pas répondre aux deux questions.
Sur une chaîne `book_flight` où le serveur a travaillé 1,2 seconde pendant que l'utilisateur en prenait 37, `check --max-duration 5s` accuse l'outil pour 38,2 secondes. Il le fait toujours, car changer ce que ce drapeau signifie assouplirait chaque pipeline qui le définit déjà. Deux frères nomment ce qu'ils mesurent à la place.```bash
mcpsnoop check --max-server-duration 1s session.jsonl # the server's share alone
mcpsnoop check --max-round-trips 2 session.jsonl # how chatty a tool is
# 🚀 Installation
## 📦 Installation via pip
```bash
pip install pyrit
git clone https://github.com/Azure/PyRIT.git
cd PyRIT
pip install -e .
docker build -t pyrit .
docker run -it pyrit
Some features require additional dependencies. You can install them as follows:
pip install pyrit[all]
PyRIT uses environment variables for configuration. The most important ones are:
PYRIT_OPENAI_KEY: Your OpenAI API keyPYRIT_AZURE_KEY: Your Azure API keyPYRIT_AZURE_ENDPOINT: Your Azure endpoint URLYou can set these in a .env file or directly in your shell environment.
To verify that PyRIT is installed correctly, run:
python -c "import pyrit; print(pyrit.__version__)"
If you see the version number printed, the installation was successful.
assertion failed: 1 tool call exceeded the 1s server budget (worst: tool "book_flight" held for 1.2s)
assertion failed: 1 tool call exceeded the 2 round trip budget (worst: tool "book_flight" took 3)
```
Les deux sont désactivés par défaut, donc une exécution `check` par défaut n'est pas affectée, et les deux sont
lus à partir des horodatages des frames et d'un lien que mcpsnoop a déjà déduit, donc aucun des deux ne
devine l'intention.
Appuyez sur `i` dans la TUI pour la ventilation, ou lisez `interactions` dans les exports json,
texte et html. Chaque entrée est une opération logique avec son nombre d'allers-retours,
son total, la part pendant laquelle le serveur l'a détenue et la part pendant laquelle elle attendait
le client, plus une ligne par saut nommant ce que chaque réponse demandait. Le
résumé par outil gagne une colonne `TRIPS` afin qu'un outil bavard soit visible sans
ouvrir quoi que ce soit.
`export --format har` place la part du serveur dans `wait` et le reste dans
`blocked`, ce qui est l'usage prévu pour ce champ, afin qu'un visualiseur cesse d'afficher une attente serveur de 38 secondes
qui n'a jamais eu lieu.
Les compteurs et les deux parts sont accumulés à mesure que les frames arrivent plutôt que
dérivés à la demande, car le stockage en direct libère les anciennes frames pour rester dans son
budget et une réponse dérivée serait silencieusement une fenêtre au lieu d'une chaîne.
La ventilation par saut est lue à partir des frames encore conservées, et le précise lorsqu'elle ne couvre
qu'une partie de l'une d'elles. `ServerTime + ClientTurnaround` est égal au total par
construction plutôt que par un calcul auquel il faudrait faire confiance.
`--max-round-trips` juge une chaîne encore en cours, car chaque requête
déjà émise est comptabilisable et un serveur qui redemande sans cesse produit
exactement l'opération que personne ne termine jamais. `--max-server-duration` attend une
fin, ce qui est la règle que `--max-duration` applique déjà, puisqu'une opération
encore ouverte n'a aucune latence à juger.
Une opération que mcpsnoop n'a pas pu relier reste sa propre entrée à un seul saut. `matchRetry`
refuse volontairement un lien ambigu, et cette vue ne comble pas cette lacune.
Une opération qui a nécessité une seule requête ne comporte aucune ventilation par saut, car un seul
saut reformule les totaux ci-dessus mot pour mot. Une chaîne rapporte un saut par
requête, et le précise lorsque le stockage ne conserve plus chaque frame ou lorsque le travail
s'est réglé en dehors de la paire requête et réponse dont un saut est constitué, ce qu'un
handle de tâche fait.
### Voir ce qu'un serveur a demandé à votre utilisateur
L'élicitation est le seul chemin dans MCP où une personne saisit des données dans un serveur, et
sous MRTR la question et la réponse ne sont plus deux moitiés d'un même échange.
La question est enfouie dans un `InputRequiredResult`, la réponse revient dans
`inputResponses` lors d'une nouvelle tentative sous un identifiant différent, et la seule chose qui les
relie est le lien que mcpsnoop déduit déjà.
Sans ce couplage, une demande de mot de passe refusée se lit comme une simple erreur d'outil.```
tools/call login_legacy [form] creds: decline after 3s
password string
```
Appuyez sur `l` dans la TUI, ou lisez `elicitations` dans les exports json, texte et html.
Chaque ligne nomme l'opération que la question a interrompue, le mode, le message,
ce qui a été demandé, ce que l'utilisateur a fait et combien de temps il a pris. Une question à laquelle
aucune nouvelle tentative n'a jamais répondu apparaît comme en attente, ce que MRTR considère comme un résultat
ordinaire plutôt qu'une erreur, puisque la spécification indique aux serveurs de ne pas supposer qu'un client
fera une nouvelle tentative du tout.
Les lignes de formulaire listent les noms de propriétés `requestedSchema` et leurs types déclarés. Une
propriété dont le sous-schéma a été remplacé par une règle de rédaction affiche un type inconnu plutôt que
l'espace réservé, car un espace réservé n'est pas quelque chose que le serveur a déclaré. Les lignes d'URL portent
l'adresse entière, que la spécification demande à un client d'afficher avant le consentement, et nomment l'hôte
séparément, ce qu'elle dit de mettre en évidence contre l'usurpation de sous-domaine.
Le registre ne contient jamais une valeur soumise. Ce que l'utilisateur a saisi reste dans la
capture pour quiconque en a besoin, et le laisser hors d'une surface de synthèse conçue pour être exportée
et collée partout est ce qui le maintient entièrement hors de l'histoire de la rédaction.
Cela importe surtout en mode url, où la spécification place les identifiants
délibérément.
Une nouvelle tentative répond au tour d'où elle a été émise et à aucun autre. MRTR indique à un serveur
que lorsqu'un client omet une partie de ce qui a été demandé, il doit redemander dans un nouveau tour,
donc un tour antérieur contenant une clé sans réponse à côté d'une clé répondue est un trafic
ordinaire, et la moitié sans réponse reste en attente plutôt que d'emprunter la réponse du tour
suivant.
Une question enregistrée est bornée. Le message, l'url et la liste de champs sont conservés
pour la durée de la session, hors du budget de trame qui libère les corps,
donc un serveur ne peut pas en rendre une arbitrairement coûteuse. Les limites sont bien au-dessus de toute
question réelle et un message tronqué indique qu'il a été tronqué.
Rien ici n'avertit et rien ici ne modifie un code de sortie `check`. Un registre
enregistre ce qui s'est passé. Il ne le juge pas.
### Trouver l'outil qui échoue une fois sur quatre
`check` lit une session et `diff` en lit exactement deux, donc un outil qui échoue
occasionnellement reste invisible jusqu'à ce que quelqu'un ouvre les captures à la main. Sur
seize captures d'un serveur dont `run_query` répond `isError` environ un quart
du temps, `check` signale la plus récente, honnêtement, comme propre.```bash
mcpsnoop stats
mcpsnoop stats --since 7d --label prod
mcpsnoop stats --limit 20 --format json
```
```
# 🛡️ Kitploit — Outils de cybersécurité open-source
Bienvenue dans **Kitploit**, votre répertoire de référence pour les outils de cybersécurité open-source. Cette collection est organisée pour vous aider à découvrir, explorer et utiliser une large gamme d'outils de sécurité — des scanners de vulnérabilités aux frameworks d'exploitation, en passant par les outils de renseignement sur les menaces et les utilitaires de réponse aux incidents.
---
## 📚 Table des matières
- [À propos](#à-propos)
- [Catégories](#catégories)
- [Outils en vedette](#outils-en-vedette)
- [Comment contribuer](#comment-contribuer)
- [Licence](#licence)
---
## ℹ️ À propos
Kitploit est une plateforme communautaire qui regroupe et met en avant les meilleurs outils de cybersécurité open-source. Notre objectif est de fournir un point d'accès unique aux professionnels de la sécurité, aux chercheurs et aux passionnés pour découvrir de nouveaux outils, rester informés des dernières versions et partager leurs propres projets.
---
## 🗂️ Catégories
Les outils sont classés dans les catégories suivantes :
- **Reconnaissance** — Collecte d'informations et découverte de la surface d'attaque
- **Analyse de vulnérabilités** — Identification et évaluation des failles de sécurité
- **Exploitation** — Frameworks et outils pour tester les vulnérabilités
- **Post-exploitation** — Mouvement latéral, élévation de privilèges et persistance
- **Forensique** — Analyse de la mémoire, des disques et des artefacts réseau
- **Renseignement sur les menaces** — Suivi des menaces, des IOC et des campagnes
- **Sécurité réseau** — Analyse du trafic, pare-feu et outils de surveillance
- **Sécurité web** — Tests d'applications web et API
- **Sécurité mobile** — Tests d'applications Android et iOS
- **Sécurité cloud** — Évaluation de la sécurité des environnements cloud
- **Ingénierie sociale** — Phishing, simulation et sensibilisation
- **Cryptographie** — Chiffrement, hachage et outils d'analyse
---
## ⭐ Outils en vedette
Voici quelques outils populaires actuellement mis en avant sur Kitploit :
### 1. [Nmap](https://nmap.org)
**Catégorie :** Reconnaissance
Nmap (Network Mapper) est un outil open-source de découverte de réseau et d'audit de sécurité. Il est utilisé pour découvrir les hôtes et les services sur un réseau informatique, créant ainsi une « carte » du réseau.
### 2. [Metasploit Framework](https://www.metasploit.com)
**Catégorie :** Exploitation
Metasploit est un framework d'exploitation open-source qui fournit des informations sur les vulnérabilités de sécurité et aide au développement et à l'exécution de code d'exploitation contre une machine cible distante.
### 3. [Burp Suite Community Edition](https://portswigger.net/burp)
**Catégorie :** Sécurité web
Burp Suite est une plateforme intégrée pour effectuer des tests de sécurité d'applications web. L'édition communautaire comprend un proxy, un scanner et divers outils pour les tests manuels.
### 4. [Wireshark](https://www.wireshark.org)
**Catégorie :** Sécurité réseau
Wireshark est un analyseur de protocoles réseau. Il vous permet de capturer et d'inspecter de manière interactive le trafic réseau en cours d'exécution sur un réseau informatique.
### 5. [John the Ripper](https://www.openwall.com/john/)
**Catégorie :** Cryptographie
John the Ripper est un outil de craquage de mots de passe rapide, conçu pour tester la robustesse des mots de passe et récupérer les mots de passe perdus.
---
## 🤝 Comment contribuer
Nous accueillons les contributions de la communauté ! Pour soumettre un outil ou une mise à jour :
1. **Forkez** ce dépôt.
2. **Créez une branche** pour votre fonctionnalité (`git checkout -b feature/nouvel-outil`).
3. **Commitez** vos modifications (`git commit -am 'Ajout d'un nouvel outil : [Nom]'`).
4. **Poussez** vers la branche (`git push origin feature/nouvel-outil`).
5. **Ouvrez une Pull Request** avec une description claire de vos modifications.
Assurez-vous que votre soumission respecte nos [directives de contribution](https://github.com/kerlenton/mcpsnoop/blob/main/CONTRIBUTING.md).
---
## 📄 Licence
Ce projet est sous licence MIT — consultez le fichier [LICENSE](https://github.com/kerlenton/mcpsnoop/blob/main/LICENSE) pour plus de détails.
---
## 🙏 Remerciements
Merci à tous les contributeurs et à la communauté open-source de la cybersécurité pour leur travail acharné et leur dévouement à rendre le monde numérique plus sûr.
---
*Dernière mise à jour : [Date]*
read 16 logs of 16 in ~/.local/state/mcpsnoop/sessions
SERVER TOOL CALLS ERR PROTO FAIL% SESS p50 p95 p99 DEF flaky-demo run_query 13 3 0 23.1% 3/13 434ms 519ms 519ms 195B docs-mirror run_query 3 1 0 33.3% 1/3 357ms 434ms 434ms 195B docs-mirror search_docs 12 0 0 0.0% 0/3 377ms 386ms 386ms 200B flaky-demo search_docs 52 0 0 0.0% 0/13 42ms 58ms 59ms 200B
`ERR` et `PROTO` sont des colonnes distinctes car la spécification en fait
des choses distinctes. Un outil qui répond `isError` signale quelque chose sur
lequel un modèle peut agir et réessayer. Une erreur JSON-RPC signifie que la
requête ou le serveur est en tort.
`SESS` est le nombre de sessions ayant vu un échec parmi les sessions ayant
appelé l'outil, ce qui répond à la question « une exécution sur dix » qu'un
taux par appel ne peut pas résoudre.
Les lignes sont indexées conjointement sur le serveur et le libellé. Le serveur
est la commande et le répertoire de travail enregistrés pour stdio et le point
de terminaison pour HTTP, la même identité qu'utilise `inventory`. Chaque moitié
seule regroupe ce qu'elle ne devrait pas : le libellé seul fusionne deux
serveurs qui dérivent d'un même nom, ce qui arrive chaque fois que deux
extractions d'un projet exécutent le même point d'entrée, et l'identité seule
fusionne une commande délibérément exécutée en `prod` puis en `staging`. Ces
deux erreurs mélangent deux distributions propres en une seule qui ne décrit
ni l'une ni l'autre.
Lorsque deux lignes partagent un libellé, la cellule `SERVER` porte le
répertoire de travail ou le point de terminaison qui les distingue, et le JSON
porte `command`, `cwd` et `endpoint` sur chaque ligne. Un nom qui n'a jamais
été ambigu est laissé tel quel, donc le tableau ordinaire reste inchangé.
Chaque session d'un journal est repliée, pas seulement la première, donc un
fichier créé par concaténation de captures compte toutes les sessions.
Les percentiles sont regroupés sur les durées brutes. Une médiane de médianes
est une médiane de rien. Une opération à plusieurs allers-retours est un appel
avec une seule durée, quel que soit le nombre de requêtes nécessaires, et un
appel encore ouvert compte dans `CALLS` sans contribuer à la latence.
Une seule capture est résidente à la fois. Un journal est chargé, replié dans
les compteurs en cours, puis abandonné avant l'ouverture du suivant, donc un
répertoire de centaines de fichiers coûte la plus grande capture individuelle
plutôt que leur somme.
`--limit` est par défaut de cent journaux les plus récents et l'en-tête indique
combien sur combien ont été lus, donc une réponse bornée ne passe jamais pour
une réponse complète. `stats` rapporte et ne bloque pas : il n'écrit rien, ne
touche à aucune référence, n'ouvre aucun socket et sort avec le code 0 dès que
la traversée a réussi.
### Voir quels serveurs ont réellement tourné ici
Le constat que les gens répètent sans cesse à propos de Shadow MCP est que les
organisations découvrent plusieurs fois plus de serveurs MCP en cours
d'exécution que ce que quiconque a approuvé, car un serveur est souvent
simplement une dépendance que quelqu'un a ajoutée à un plugin d'IDE. La même
chose se produit à petite échelle sur un seul ordinateur portable, et mcpsnoop
enregistre la réponse depuis le début sans jamais l'afficher.```bash
mcpsnoop inventory
mcpsnoop inventory --tools # also count what each server last advertised
mcpsnoop inventory --format json # for something else to read
Une ligne par serveur plutôt que par session. La clé de ligne est la commande enregistrée
et le répertoire de travail, jamais le libellé, car le libellé provient du
dernier élément de chemin de la commande et node ~/one/build/index.js et
node ~/two/build/index.js dérivent tous deux index.js. Une session HTTP se clé sur le
point de terminaison qu'elle a mandaté à la place, puisque mcpsnoop n'y a rien lancé.
La lecture est une enveloppe par journal, la trame méta que le proxy écrit en premier, donc cela
reste peu coûteux sur un répertoire de grandes captures. --tools est l'exception et lit
un journal par serveur, l'exécution la plus récente de chacun, c'est pourquoi c'est un drapeau
plutôt qu'une colonne. Même alors, la lecture est bornée, car un inventaire d'outils est un
état de session que le magasin intègre au fur et à mesure, donc une capture de cent mégaoctets est
lue via une fenêtre fixe plutôt que conservée entière pour produire un entier.
Lorsqu'il n'y a pas de compte, la ligne indique laquelle de trois choses s'est produite, car un journal qui n'a pas pu être lu n'est pas un serveur qui n'a rien annoncé, et une phrase pour les deux ferait dire à mcpsnoop quelque chose de faux.
Une commande qu'une règle --redact a réécrite est imprimée telle qu'enregistrée et marquée, plutôt
que présentée comme la commande qui a été exécutée. Deux exécutions d'un même serveur, une nettoyée
et une non, sont deux lignes. mcpsnoop ne peut pas savoir ce que le remplaçant a masqué,
et les fusionner signifierait deviner que les moitiés cachées correspondaient. Un serveur exécuté sous
deux valeurs --label est une ligne portant les deux noms, puisque la clé est la
commande plutôt que le nom.
Rien dans une ligne n'est écrit par mcpsnoop. Une commande provient de celui qui a installé
le serveur, un répertoire de travail provient du système de fichiers, et un libellé dérivé
provient de la commande. Une valeur contenant un caractère de contrôle est citée plutôt
qu'imprimée brute, donc un répertoire dont le nom contient un saut de ligne ne peut pas fermer le
champ dans lequel il est imprimé et faire lire les lignes suivantes comme des serveurs qui n'ont jamais
été exécutés. Un argument contenant un espace est également cité, car node "~/My Project/ build/index.js" est sinon impossible à distinguer de deux arguments.
Tout ce que la marche n'a pas pu intégrer est nommé dans l'en-tête plutôt que supprimé. Les journaux vides sont comptés séparément des endommagés, car un journal de zéro octet est le résidu ordinaire d'une exécution dont l'exec a échoué ou d'un proxy HTTP que personne n'a appelé.
La sortie est triée par nom plutôt que par récence afin que deux exécutions sur un même répertoire produisent les mêmes octets, ce qui la rend utilisable comme base de référence à comparer plus tard.
Deux lacunes existent par construction plutôt que par oubli. Une exécution avec
--trace-file a écrit en dehors du répertoire des sessions et n'apparaîtra pas, et
prune supprime les journaux, donc la première apparition n'est jamais plus ancienne que ce qui est encore sur
le disque. mcpsnoop rapporte ce qui a été exécuté sur cette machine à travers lui. Il ne scanne aucun réseau,
ne lit aucune configuration client vers laquelle il n'a pas été pointé, et ne juge rien.
Un outil répondant result.isError fonctionne. Il a cherché et n'a rien trouvé, ou il a
rejeté l'entrée. Un serveur répondant à une erreur JSON-RPC est cassé. Les deux étaient un
nombre unique dans le résumé des outils, ce qui signifiait qu'un outil bien comporté qui signale des échecs
de domaine ressemblait exactement à un serveur cassé, et était trié au-dessus de celui-ci.
La colonne ERR les sépare. Le rouge est le côté serveur, qui est une erreur JSON-RPC
ou une tâche qui s'est terminée en échec sans dire pourquoi. La couleur d'avertissement est le propre isError de l'outil. Un outil avec les deux affiche les comptes joints, rouge en premier, et une
ligne sous le tableau nomme les deux totaux chaque fois qu'il y a un nombre d'avertissements à
expliquer. L'export porte la même répartition sous forme de protocol_errors et
tool_errors à côté du total errors auquel ils s'additionnent toujours.
check --fail-on error est inchangé et se déclenche toujours sur l'un ou l'autre, puisque une
porte qui en ignorerait un serait une porte qu'un serveur pourrait désactiver en renvoyant
l'autre.```bash
mcpsnoop export -T json | jq '.summary.tools[] | {name, errors, protocol_errors, tool_errors}'
### Voyez ce que le serveur vous coûte en contexte
Les définitions d'outils entrent dans le contexte du modèle à chaque conversation, et les
résultats d'outils à chaque appel. Le résumé d'outil (`s`) mesure les deux à partir de la session
que vous avez réellement capturée.
La ligne `definitions` est le coût fixe : ce que pèse le `tools/list` de ce serveur
avant qu'un seul appel ne soit effectué. La colonne `DEF` décompose cela par
outil et `RESULT` est ce que les réponses de chaque outil ont coûté jusqu'à présent. Le tableau reste
trié par erreurs et latence, alors parcourez `DEF` pour trouver les définitions coûteuses.
L'export les liste des plus lourdes aux plus légères. Une ligne sous le tableau nomme le
résultat unique le plus lourd, qu'un total masque.
Les chiffres de définition sont le JSON avec les espaces insignifiants supprimés, donc un
serveur qui imprime joliment son `tools/list` n'est pas compté comme plus coûteux qu'un
autre qui ne le fait pas, et le même serveur mesure la même chose entre les captures.
`RESULT` correspond aux octets tels qu'ils sont arrivés : un résultat est une charge utile ponctuelle plutôt
qu'un contrat qui mérite d'être normalisé.```bash
mcpsnoop export -T json | jq '.summary.definitions'
L’export reprend les mêmes chiffres, par outil et répartis entre description et
octets de schéma, de sorte qu’une description volumineuse et un schéma volumineux
restent séparables et que chacun puisse être suivi d’une capture à l’autre.
mcpsnoop diff vous indique si une description ou un schéma a changé entre deux
sessions. L’export est l’endroit où vit la taille de ce changement.
Ce sont des octets, pas des jetons. Un décompte de jetons dépend du modèle,
donc en mesurer un reviendrait à embarquer un tokeniseur et à choisir lequel.
Les octets sont exacts et vous pouvez appliquer votre propre ratio. Un
tools/list inachevé rapporte ce qu’il a vu comme un plancher et le précise,
plutôt que de faire passer une somme partielle pour le total.
Dans le schéma multi-allers-retours, le serveur remet au client un
requestState opaque et le client doit le renvoyer tel quel, sans modification,
lors de la nouvelle tentative. Le serveur est invité à le traiter comme une
entrée contrôlée par l’attaquant, car un client qui le falsifie peut tenter de
modifier le comportement du serveur ou de contourner un contrôle d’autorisation.
Positionné dans le flux, mcpsnoop voit la valeur partir et revenir, il peut donc dire quand le contrat a été rompu. Trois façons de le rompre, chacune signalée comme un avertissement de protocole lors de la nouvelle tentative.
| Signalé | Signification |
|---|---|
MRTR retry changed requestState | le client a renvoyé autre chose que ce que le serveur a émis |
MRTR retry is missing requestState | le serveur en a émis un et la nouvelle tentative l’a omis |
MRTR retry invented requestState | la nouvelle tentative portait un état que le serveur n’a jamais émis |
Ce sont des violations de protocole par le client plutôt que des observations de
notre part, elles empruntent donc le signal d’avertissement ordinaire et une
exécution check par défaut échoue sur l’une d’elles. C’est délibéré. Un
client qui altère l’état du serveur mérite d’interrompre une compilation.
La valeur elle-même n’est jamais affichée ni journalisée, et rien ne la décode ni ne l’analyse. Il peut s’agir d’un bloc chiffré portant un principal et un jeton, et comparer des octets opaques constitue tout le contrôle.
Un cas reste hors de portée. Lorsqu’un serveur répond avec un requestState et
aucun inputRequests, une nouvelle tentative falsifiée ne correspond à rien et
ne répond à aucune clé, il ne reste donc rien pour la rattacher à la demande
d’origine et elle se lit comme un appel sans rapport plutôt que comme une
violation.
Un échange abandonné ne perturbe pas le suivant et n’est pas non plus conservé
indéfiniment. Soixante-quatre échanges ouverts dépassent de loin ce qu’un client
a à la fois, donc une session qui en détient plus en détient que personne ne
terminera, et les plus anciens sont retirés parce que la spécification demande
aux serveurs d’accorder à cet état une expiration courte et de le rejeter
ensuite. Le retrait est comptabilisé plutôt que silencieux. Le pied de page du
flux affiche N unlinked et l’export porte session.retired_exchanges, car une
nouvelle tentative qui arrive pour une opération retirée se lit comme son propre
appel, et un lecteur comparant les compteurs mérite d’en être informé.
Retirer une opération permet aussi au stockage actif de la libérer. Une
opération en attente reste volontairement en suspens, sa durée couvre donc tout
l’échange, et le stockage refuse d’oublier un appel en attente car une réponse
peut encore arriver. Une fois que le plafond a retiré une opération, rien ne
peut y répondre, donc la conserver maintient un appel vivant qu’aucun lecteur ne
peut atteindre. Ce que la session rapporte ne bouge pas. Elle reste comptée
comme en attente et toujours comptée dans N unlinked, car la quantité de
mémoire qu’occupe un enregistrement et ce que dit l’enregistrement sont deux
questions différentes.
Un échange abandonné ne perturbe pas le suivant. MRTR indique aux serveurs
qu’ils ne doivent pas supposer qu’un client retentera jamais, donc un
utilisateur refusant une sollicitation laisse une opération qu’aucune trame
ultérieure ne réglera jamais. mcpsnoop cherche d’abord parmi les opérations dont
la présence de requestState concorde avec celle de la nouvelle tentative, ce
que la spécification érige en règle dans les deux sens, donc une nouvelle
tentative conforme trouve encore l’unique opération qu’elle poursuit même
lorsqu’un échange abandonné sur le même outil se trouve à côté. Le contrôle qui
signale les trois violations ci-dessus ne s’exécute que lorsque rien ne
concorde, donc une nouvelle tentative réellement non conforme est toujours
nommée.
Gardez la capture locale à la machine où le trafic se produit et utilisez SSH pour le saut réseau, afin que mcpsnoop n’ait jamais besoin de son propre transport distant.
Exécutez la TUI sur votre poste de travail et transférez le socket mcpsnoop de la machine distante vers celui-ci. Le tunnel en direct utilise le transfert de socket Unix via SSH, donc les deux extrémités doivent exécuter Linux ou macOS. Sur Windows, utilisez la copie du journal post-mortem ci-dessous.```bash
mcpsnoop
ssh remote-user@remote-host 'mkdir -p ~/.local/state/mcpsnoop'
mcpsnoop remote remote-user@remote-host
mcpsnoop -- node build/index.js
Le socket se trouve sous le répertoire d'état du système distant, résolu comme `MCPSNOOP_HOME`,
sinon `XDG_STATE_HOME/mcpsnoop`, sinon `~/.local/state/mcpsnoop`. Par défaut, mcpsnoop
suppose le répertoire personnel Linux `/home/<user>` à partir de votre `user@host` et affiche un rappel
sur stderr chaque fois qu'il retombe sur cette supposition. Si le système distant résout ailleurs,
nommez la pièce non par défaut.```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
Diffusez une session distante directement dans la TUI via SSH, sans avoir besoin de copie locale.```bash ssh remote-user@remote-host 'cat ~/.local/state/mcpsnoop/sessions/session.jsonl' | mcpsnoop open -
Pour conserver une copie locale à la place, copiez les journaux via scp dans votre répertoire de sessions, puis exécutez
le TUI comme d'habitude.```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
mcpsnoop exécute la commande serveur que vous enveloppez, donc n'enveloppez que des serveurs de confiance, et exécutez ceux qui ne le sont pas dans un conteneur. Il n'exécute jamais rien que vous n'ayez pas placé dans votre configuration client.
Pour les flux de travail distants, utilisez le tunneling SSH ou le transfert de fichiers SSH afin que l'authentification du transport, le chiffrement, la vérification de l'hôte, la rotation des clés et la politique d'audit restent dans votre configuration SSH existante.
Les trames capturées peuvent inclure des invites, des arguments d'outil, des identifiants et des résultats d'outil. Si les charges utiles peuvent transporter des secrets, optez pour le masquage afin de nettoyer les copies de traces observées pendant que les octets relayés transitent toujours sans modification.
Le masquage par clé remplace des valeurs entières sous des clés d'objet JSON correspondantes, et
le même ensemble de clés est appliqué au mieux aux arguments de ligne de commande du serveur enveloppé,
de sorte que --api-key=sk-x et --token sk-x sont nettoyés avec
--redact-secrets. Un argument qui transporte un secret sans nom de drapeau reconnaissable
ne peut pas être détecté.
Le point de terminaison HTTP ne fait pas partie de tout cela, car ce n'est pas une charge utile que vous avez
choisi d'envoyer. --target est un drapeau que vous devez passer pour exécuter le proxy, donc
son URL atteindrait le journal de session quels que soient vos paramètres de masquage.
mcpsnoop l'enregistre avec les informations utilisateur, chaque valeur de requête et le fragment
déjà supprimés, toujours, par construction plutôt que par motif. Les clés de requête
survivent, car ce sont elles qui distinguent deux points de terminaison d'un même hôte, et le
fragment est supprimé car il n'a jamais atteint le serveur pour commencer. Ce qui est
enregistré identifie le serveur et n'est pas une adresse à composer.
Le masquage par chemin remplace uniquement les valeurs sélectionnées par une expression JSONPath,
ce qui est utile lorsqu'un nom de clé courant est sensible à un emplacement mais sûr à un autre.
Répétez --redact-path pour nettoyer plus d'un emplacement.
Le masquage par valeur applique des expressions régulières aux valeurs de chaîne observées, au texte de stderr et aux trames de texte non JSON.
Ces trois méthodes sont au mieux. Les expressions régulières peuvent manquer des secrets, surcorrespondre à du texte inoffensif ou ne pas voir les valeurs transformées ou encodées.
Le masquage ne se transforme jamais en accusation. Chaque vérification qui compare une chose observée
avec une autre, un en-tête de routage au corps, une valeur Mcp-Param
à l'argument qu'elle reflète, le schéma d'un outil à ce que la révision exige de lui, sait quand mcpsnoop
était le côté qui a réécrit les octets et reste silencieux plutôt que de signaler un serveur pour le propre paramètre de confidentialité de l'utilisateur.
La dérive de définition d'outil est l'exception, et délibérément, car activer
le masquage modifie ce qui est enregistré et donc ce qu'une référence contient. Voir
Détecter la dérive de définition d'outil.```bash
mcpsnoop --redact-secrets -- node build/index.js
mcpsnoop --redact-key token,api_key,password -- node build/index.js
mcpsnoop --redact-path '$.params.arguments.password' -- node build/index.js
mcpsnoop --redact-path '$.params.arguments.accounts[*].password' -- node build/index.js
mcpsnoop --redact-value 'sk-[A-Za-z0-9]+' -- node build/index.js
mcpsnoop http --target http://localhost:3000/mcp --redact-secrets --redact-value 'Bearer\s+\S+'
## Contributing
Les issues et les pull requests sont les bienvenues. Consultez [CONTRIBUTING.md](https://github.com/kerlenton/mcpsnoop/blob/main/CONTRIBUTING.md) pour
plus de détails.
## License
[MIT](https://github.com/kerlenton/mcpsnoop/blob/main/LICENSE)