Wireshark for MCP. A transparent proxy that shows every real tool call between your AI client and your MCP servers, live in your terminal.
The official MCP Inspector connects as its own client, so it never sees what your client (Cursor, Claude Code, Codex) actually sends your server. A breakpoint only fires once a request arrives, so it can't show the call the model never made, or made with the wrong arguments. When a tool silently isn't called, capabilities don't line up, or a call just hangs, you're left digging through logs and guessing.
mcpsnoop sits in the real data path instead. Wrap your server command with it and watch every JSON-RPC frame live, as your real client and server talk.
See it right away, with nothing to set up.
mcpsnoop demoTo use it for real, wrap your server in your client's MCP config.
{
"mcpServers": {
"my-server": {
"command": "mcpsnoop",
"args": ["--", "node", "build/index.js"]
}
}
}Everything after -- is the command that normally launches your server. Swap in
whatever you already use, like python server.py, npx -y @scope/server, or a
compiled binary. Then use your client as usual and open the UI.
mcpsnoopNo flags, no socket paths, no startup order to remember. The shim and the UI find each other on their own, and the UI backfills past sessions from disk.
For a streamable-HTTP server, run mcpsnoop as a reverse proxy.
mcpsnoop http --target http://localhost:3000/mcp --listen :7000No server of your own? Try it for real against a published test server, driven by your own client. To inspect a session after it happened, see review past sessions from logs.
| MCP Inspector | mcpsnoop | |
|---|---|---|
| Sees your real client and server traffic | no | yes |
| Flags slow and hung calls | no | yes |
| Flags stray output that corrupts the stream | no | yes |
| Flags malformed JSON-RPC frames | no | yes |
| Interactive terminal UI | no | yes |
| Zero-config, no flags or ordering | no | yes |
| Capability inspector | partial | yes |
| Replay a captured call | no | yes |
| Session export (json / html / text / otlp) | no | yes |
| Single binary, no runtime deps | no | yes |
go install github.com/kerlenton/mcpsnoop/cmd/mcpsnoop@latestbrew install kerlenton/mcpsnoop/mcpsnoopPrebuilt binaries for every platform are on the Releases page.
mcpsnoop is two roles in one binary. mcpsnoop -- <server> is the transparent
shim your client spawns, forwarding bytes verbatim while shipping a copy of every
frame to mcpsnoop with no arguments, the hub and TUI. They pair through a
well-known socket and on-disk logs, so neither has to start first.
Because it sits in the actual pipe, not off to the side like the Inspector, it sees exactly what your real client and server say to each other, whatever the server is written in.
| Key | Action | Key | Action | |
|---|---|---|---|---|
enter |
inspect / drill in | / |
filter | |
esc |
back | : |
command | |
j / k |
move | r |
replay a call | |
g / G |
top / bottom | c |
capabilities | |
ctrl-f / ctrl-b |
page | y |
copy | |
shift+column |
sort | e |
export | |
p |
pause | ctrl-d |
delete session | |
f |
follow | ? |
help |
Press ? in the app for the full list.
Press / in a session and combine space-separated tokens, ANDed. Plain text
matches the method, tool, id, and payload.
| Token | Filters by | Example |
|---|---|---|
tool: |
tool name | tool:search |
method: |
JSON-RPC method | method:tools/call |
id: |
request id | id:7 |
dir: |
direction (c2s, s2c) |
dir:s2c |
kind: |
frame type (req, resp, notify, stderr, invalid) |
kind:invalid |
status: |
call outcome (ok, error, slow, pending, bad, warn) |
status:slow |
Stack tokens to get specific.
tool:search status:slow # slow calls to one search tool
method:tools/call status:error # tool calls that failed
dir:s2c kind:req # server-initiated requests (sampling, roots)
Turn any captured session into a portable file.
mcpsnoop export -T json|html|text|otlp [-o file|-] [session-id|log.jsonl]| Format | What you get |
|---|---|
json |
correlated calls, durations, status, tool-level isError, capabilities, and raw frames |
html |
a self-contained browser file with search and collapsible JSON |
text |
a pretty plain-text dump |
otlp |
OTLP JSON with a trace per session and a span per correlated call |
mcpsnoop export -T html -o out.html # an HTML file to open in a browser
mcpsnoop export -T text server.py-48213 # a specific session, as text
mcpsnoop export -T json | jq # the newest session, piped to jq
mcpsnoop export -T otlp -o trace.json # import into an OTLP-compatible tracing backendOmit -o to write to stdout, and omit the session to take the newest. In the
TUI, press e to export the selected session as HTML, or run
:export json|html|text|otlp [path] from command mode.
Keep capture local to the machine where the traffic happens and use SSH for the network hop, so mcpsnoop never needs a remote transport of its own.
Run the TUI on your workstation and forward the remote machine's mcpsnoop socket
back to it. The socket path follows MCPSNOOP_HOME, XDG_STATE_HOME, and the
remote home, defaulting to ~/.local/state/mcpsnoop/hub.sock.
# 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.jsIf the remote uses a non-default home or MCPSNOOP_HOME, pass it explicitly.
mcpsnoop remote --remote-home /Users/remote-user remote-user@remote-host
mcpsnoop remote --remote-mcpsnoop-home /srv/mcpsnoop remote-user@remote-hostStream a remote session straight into the TUI over SSH, no local copy needed.
ssh remote-user@remote-host 'cat ~/.local/state/mcpsnoop/sessions/session.jsonl' | mcpsnoop open -To keep a local copy instead, scp the logs into your sessions directory and run the TUI as normal.
# 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
mcpsnoopmcpsnoop runs the server command you wrap, so only wrap servers you trust, and run untrusted ones in a container. It never executes anything you didn't put in your client config.
Captured frames can include prompts, tool arguments, credentials, and tool results. If payloads can carry secrets, opt in to redaction to scrub the observed trace copies while the proxied bytes still pass through unchanged. Key-based redaction replaces whole values under matching JSON object keys. Value-based redaction applies regular expressions to observed string values, stderr text, and non-JSON text frames. Both modes are best effort. Regexes can miss secrets, overmatch harmless text, or fail to see transformed or encoded values.
# 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 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+'For remote workflows, use SSH tunnelling or SSH file transfer so transport auth, encryption, host verification, key rotation, and audit policy stay in your existing SSH setup.
Issues and pull requests are welcome. See CONTRIBUTING.md for the details.

