Назад к обновлениям
New releaseJul 20, 2026

mcpsnoop v0.12.0

Wireshark для MCP. Прозрачный прокси, который показывает каждый реальный вызов инструмента между вашим AI-клиентом и вашими MCP-серверами в реальном времени в вашем терминале.

Поделиться

mcpsnoop

Wireshark для MCP. Прозрачный прокси, который показывает каждый реальный вызов инструмента между вашим ИИ-клиентом и вашими MCP-серверами, в реальном времени прямо в вашем терминале.

CI Go Reference MIT

демонстрация mcpsnoop

Проблема

Официальный MCP Inspector подключается как собственный клиент, поэтому он никогда не видит, что ваш клиент (Cursor, Claude Code, Codex) на самом деле отправляет вашему серверу. А всё, что ожидает поступления запроса, не может показать вызов, который модель так и не сделала, либо сделала с неправильными аргументами. Когда инструмент молча не вызывается, возможности не совпадают или вызов просто зависает, вам остаётся копаться в логах и гадать.

Вместо этого mcpsnoop встраивается в реальный путь данных. Оберните им команду вашего сервера и наблюдайте за каждым JSON-RPC кадром в реальном времени, пока ваш реальный клиент и сервер общаются.

Быстрый старт

Посмотрите сразу же, без какой-либо настройки.```bash mcpsnoop demo

Чтобы использовать это по-настоящему, оберните ваш сервер в конфигурацию MCP вашего клиента.```json
{
  "mcpServers": {
    "my-server": {
      "command": "mcpsnoop",
      "args": ["--", "node", "build/index.js"]
    }
  }
}

Всё, что идёт после --, — это команда, которая обычно запускает ваш сервер. Подставьте то, что вы уже используете, например python server.py, npx -y @scope/server или скомпилированный бинарный файл.

В Claude Desktop вам не нужно вносить эту правку вручную.```bash mcpsnoop wrap my-server # route my-server through mcpsnoop mcpsnoop unwrap my-server # put it back

`wrap` находит `claude_desktop_config.json`, копирует его в
`claude_desktop_config.json.mcpsnoop.bak` при первом запуске и перезаписывает только запись
этого одного сервера, так что ваше форматирование и все остальные серверы остаются нетронутыми.
Внутри перезаписанной записи ключи возвращаются в алфавитном порядке. `unwrap`
восстанавливает файл и удаляет резервную копию, как только ни один сервер больше не обёрнут.
После любого из этих действий перезапустите Claude Desktop, поскольку MCP-серверы запускаются один раз при старте.

Затем используйте ваш клиент как обычно и откройте интерфейс.```bash
mcpsnoop

Никаких флагов, никаких путей к сокетам, никакого порядка запуска, который нужно запоминать. Шим и интерфейс сами находят друг друга, а интерфейс восстанавливает прошлые сеансы с диска.

Для потокового HTTP-сервера запустите mcpsnoop как обратный прокси.```bash mcpsnoop http --target http://localhost:3000/mcp --listen :7000

HTTP-статус каждого ответа отображается в потоке, так что ответ, не несущий
собственного JSON-RPC-сообщения, всё равно остаётся видимым кадром, а не
пустотой: вызов 401, 403 при отклонённом Origin, 202, подтверждающий приём
уведомления, и 502, когда цель вообще недоступна. Заголовок `WWW-Authenticate`
в ответе 401 сохраняется дословно и показывается в инспекторе, поскольку он
называет схему аутентификации и метаданные ресурса для следующего шага.
Фильтруйте по статусу с помощью `status:401` в TUI или по любой ошибке
с помощью `status:err`. Статус 4xx или 5xx считается ошибкой, поэтому запуск
`mcpsnoop check` по умолчанию завершается неудачей.

Нет своего сервера? [Попробуйте вживую](https://github.com/kerlenton/mcpsnoop/blob/HEAD/docs/TRY_IT.md) на опубликованном
тестовом сервере под управлением вашего собственного клиента. Чтобы изучить
сеанс после его завершения, см. [разбор прошлых сеансов по журналам](https://github.com/kerlenton/mcpsnoop/blob/HEAD/docs/POST_MORTEM.md).

### Файл конфигурации

Если вы используете одни и те же флаги shim в рамках проекта, поместите их
в файл `.mcpsnoop.toml` в текущем рабочем каталоге.```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

Повторите redact-key, redact-value и redact-path на отдельных строках, чтобы добавить несколько значений каждого типа.

Это все ключи, которые он поддерживает.

Файл ищется только в текущей рабочей директории, а не в родительских директориях.

Явные флаги командной строки переопределяют значения из конфигурационного файла.

Команды

КомандаЧто делает
mcpsnoop -- <server>обернуть stdio-сервер как прозрачную прослойку
mcpsnoopоткрыть TUI в реальном времени
mcpsnoop http --target <url>проксировать потоковый HTTP-сервер
mcpsnoop exportпреобразовать сессию в json, html, text, har или otlp
mcpsnoop checkпроваливать CI при ошибках, невалидных фреймах, предупреждениях, несоответствиях маршрутизации, зависших вызовах или поздних результатах
mcpsnoop baselineпросматривать, принимать или сбрасывать доверенные определения инструментов
mcpsnoop diffсравнивать инструменты и вызовы между двумя захваченными сессиями
mcpsnoop openоткрыть сохранённую сессию в TUI
mcpsnoop pruneудалять сохранённые журналы сессий старее заданного порога
mcpsnoop wrap <server>направлять один из серверов Claude Desktop через mcpsnoop
mcpsnoop unwrap <server>вернуть запись этого сервера в исходное состояние
mcpsnoop remote <user@host>вывести команду SSH-туннеля
mcpsnoop demoвоспроизвести сессию по сценарию

Выполните mcpsnoop help, чтобы увидеть полный список, или mcpsnoop help <command>, чтобы узнать флаги конкретной команды.

Сравнение

MCP Inspectormcpsnoop
Видит реальный клиентский и серверный трафикнетда
Помечает зависшие вызовы и ошибки потоканетда
Помечает посторонний вывод, повреждающий потокнетда
Помечает некорректные JSON-RPC кадрынетда
Обнаруживает расхождение определений инструментов после одобрениянетда
Интерактивный терминальный интерфейснетда
Нулевая конфигурация, без флагов и порядканетда
Инспектор возможностейчастичнода
Воспроизведение захваченного вызованетда
Экспорт сессии (json / html / text / otlp)нетда
Один бинарник, без зависимостей во время выполнениянетда

Установка

Go```bash

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

### Homebrew```bash
brew install mcpsnoop

Готовые бинарные файлы для всех платформ доступны на странице Releases.

Автодополнение для оболочек

mcpsnoop поставляется с автодополнением для bash, zsh, fish и PowerShell. Выполните mcpsnoop completion <shell> --help, чтобы получить инструкции по настройке, включая включение автодополнения и путь установки для вашей ОС.

Как это работает

mcpsnoop находится в канале между вашим ИИ-клиентом и MCP-серверами, копируя каждый JSON-RPC кадр в живой терминальный интерфейс

mcpsnoop совмещает две роли в одном бинарном файле. mcpsnoop -- <server> — это прозрачный шим, который запускает ваш клиент, пересылая байты без изменений и отправляя копию каждого кадра в хаб. mcpsnoop без аргументов — это сам хаб и его живой TUI. Они связываются через заранее известный сокет и журналы на диске, поэтому ни одному из них не нужно запускаться первым.

По умолчанию хаб загружает 100 последних сохранённых сеансов, что ограничивает работу при запуске, не удаляя более старые записи. Используйте mcpsnoop --history-limit N, чтобы выбрать другой предел, или mcpsnoop --history-limit 0, чтобы загрузить всю историю. Более старые сеансы остаются доступными через mcpsnoop open <session-id> и mcpsnoop export <session-id>.

Ограничение истории определяет, что загружается; mcpsnoop prune определяет, что сохраняется. Он удаляет сохранённые журналы сеансов старше порогового срока и никогда не запускается сам по себе.```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` обязателен (по умолчанию ничего не удаляется) и
принимает количество дней, например `30d`, или Go-длительность, например `72h`. Базовые показатели инструментов
не затрагиваются, поскольку базовый показатель привязан к метке сервера, а не к сеансу.

Поскольку он находится в самом конвейере, а не в стороне, как Inspector, он
видит именно то, что ваши реальные клиент и сервер говорят друг другу, независимо от того, на чём
написан сервер.

## Сочетания клавиш

| Клавиша | Действие | | Клавиша | Действие |
|---|---|---|---|---|
| `enter` | просмотр / переход внутрь | | `/` | фильтр |
| `esc` | назад | | `:` | команда |
| `j` / `k` | перемещение | | `r` | повторить вызов |
| `g` / `G` | вверх / вниз | | `c` | возможности |
| `ctrl-f` / `ctrl-b` | страница | | `s` | сводка по инструментам |
| `p` | пауза | | `y` | копировать |
| `shift`+`<key>` | сортировать по столбцу | | `e` | экспорт |
| `ctrl-d` | удалить сеанс | | `f` | следовать |
| `?` | помощь | | | |

Нажмите `?` в приложении для полного списка.

## Фильтрация потока

Нажмите `/` в сеансе и комбинируйте токены, разделённые пробелами, через И. Простой текст
соответствует методу, инструменту, идентификатору и полезной нагрузке.

| Токен | Фильтрует по | Пример |
|---|---|---|
| `tool:` | имя инструмента | `tool:search` |
| `method:` | метод JSON-RPC | `method:tools/call` |
| `id:` | идентификатор запроса и любые повторные попытки, продолжающие его | `id:7` |
| `task:` | идентификатор задачи | `task:01J...` |
| `dir:` | направление (`c2s`, `s2c`) | `dir:s2c` |
| `kind:` | тип кадра (`req`, `resp`, `notify`, `stderr`, `invalid`) | `kind:invalid` |
| `status:` | результат вызова (`ok`, `error`, `cancel`, `late`, `cancelled`, `pending`, `bad`, `warn`, `mismatch` или HTTP-статус, например `401`) | `status:error` |

Объединяйте токены, чтобы получить конкретный результат.```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)

Последний находит что-либо только на сервере, поддерживающем версию 2025-11-25 или более раннюю. В редакции 2026-07-28 были удалены запросы, инициируемые сервером: сервер, которому что-то нужно от клиента, теперь отвечает на сам запрос клиента, запрашивая требуемое, после чего клиент повторяет попытку. mcpsnoop связывает эти повторы с тем запросом, продолжением которого они являются, так что обмен выглядит как один вызов, а не несколько.

Экспорт сеансов

Превратите любой захваченный сеанс в переносимый файл.```bash mcpsnoop export -T json|html|text|har|otlp [-o file|-] [session-id|log.jsonl|-]

| Формат | Что вы получаете |
|---|---|
| `json` | скоррелированные вызовы, количество по каждому инструменту и задержки p50/p95/p99, самые медленные вызовы, возможности и сырые кадры |
| `html` | автономный файл для браузера с поиском и сворачиваемым JSON |
| `text` | читаемый текстовый дамп |
| `har` | одна запись на каждый скоррелированный вызов; открывается в инструментах разработчика браузера и в любом другом ПО, поддерживающем HAR |
| `otlp` | OTLP JSON с одним span на каждый скоррелированный вызов; W3C trace context объединяет трассы вызывающей стороны, в противном случае используется одна трасса на сессию |

MCP — это не HTTP, поэтому URL, код состояния и тайминги записи HAR представляют собой намеренное сопоставление каждого вызова, а не точную запись сетевого обмена.

Для OTLP поле `_meta.traceparent` запроса задаёт идентификаторы трассы и родительского span для этого вызова, а `_meta.tracestate` передаётся вместе со span. Если traceparent отсутствует или недействителен, mcpsnoop сохраняет трассу, производную от сессии, и не несёт никакого состояния. mcpsnoop наблюдает, а не участвует, поэтому он не добавляет собственной записи поставщика и передаёт состояние вызывающего без изменений.```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

Не указывайте -o, чтобы выводить данные в stdout, и не указывайте сессию, чтобы выбрать самую новую, или передайте - для чтения JSONL из stdin. В TUI нажмите e, чтобы экспортировать выбранную сессию как HTML, или выполните :export json|html|text|har|otlp [path] из командного режима.

Чтобы очистить существующую запись перед просмотром или передачей, передайте те же флаги редактирования, использованные при захвате, командам export или open:```bash mcpsnoop export session.jsonl --redact-secrets --redact-key project_token -o shared.json mcpsnoop open session.jsonl --redact-path '$.params.arguments.password'

Эти флаги перезаписывают экспортированный файл или представление TUI в памяти, но никогда — исходный JSONL. `export` отказывается записывать в тот же файл, что и входной, и пишет через временный файл, который затем переименовывается на место, так что при сбое предыдущий файл остаётся целым.

`inputSchema` и `outputSchema` инструмента, как они объявлены в результате `tools/list`, не затрагиваются `--redact-key` и `--redact-secrets`. Имя внутри схемы — это объявление типа, а не значение; само имя в любом случае остаётся в журнале, а очистка подсхемы под свойством с именем `token` унесла бы с собой и собственные проверки инструмента. Исключение действует только для этой позиции, поэтому аргумент, который по случайности называется `inputSchema`, очищается как любой другой, и оно останавливается на `default`, `const`, `examples` и `enum`, где хранятся данные, а не структура. Используйте `--redact-path`, чтобы указать что-то внутри схемы, или `--redact-value`, который сопоставляет текст где угодно, кроме двух ключевых слов, которые разбирает mcpsnoop, — `type` и `x-mcp-header`.

Область действия каждого флага различается, поэтому проверяйте результат, а не полагайтесь на предположения. Все четыре очищают полезные нагрузки JSON-RPC, а `--redact-key`, `--redact-path` и `--redact-secrets` достигают только их. Только `--redact-value` также очищает stderr, прочий не-JSON текст и внутренности строк. Заголовок `Mcp-Param-*` очищается вместе с тем значением тела, которое он отражает; остальные метаданные конверта, метки сервера, `Mcp-Name`, `Mcp-Method` и HTTP-статус остаются такими, как были записаны. Редактирование выполняется по принципу «лучшее из возможного», поэтому используйте отдельный путь вывода и прочтите результат перед тем, как делиться им.

### Потоковая передача завершённых вызовов в коллектор OTLP

Отправляйте спаны, пока прокси запущен, указав ему конечную точку трасс OTLP/HTTP JSON. Повторяйте `--otlp-header` для заголовков аутентификации коллектора или арендатора.```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

Доставка осуществляется по принципу best-effort и никогда не блокирует проксируемый MCP-трафик. Если коллектор недоступен, mcpsnoop повторяет попытки в фоновом режиме и отбрасывает новые кадры трассировки, когда его ограниченная очередь заполнена. Обычный журнал сеансов JSONL остаётся постоянной записью.

Сравнение сеансов

Сравните два сохранённых сеанса по id или пути JSONL.```bash mcpsnoop diff before-session after-session mcpsnoop diff old.jsonl new.jsonl

Отчет показывает инструменты, которые были добавлены или удалены, изменения описания и `inputSchema`, сопоставленные вызовы инструментов, чей статус изменился, и заметные сдвиги длительности. Вызовы сопоставляются по имени инструмента и аргументам, поэтому переупорядоченные вызовы по-прежнему корректно сравниваются. По умолчанию изменения длительности должны отличаться как минимум на 100 мс и в 2 раза; используйте `--duration-threshold` и `--duration-ratio`, чтобы настроить эти пороговые значения.

Передайте `--exit-code`, чтобы применять гейт CI по регрессиям: он завершается с ненулевым кодом, когда последующая сессия удаляет инструмент, изменяет описание инструмента, заголовок, входную схему, выходную схему или аннотации, имеет вызов, чей статус ухудшился, или замедляется. Улучшения (добавленные инструменты, исправленные вызовы, ускорения) по-прежнему завершаются с нулевым кодом, как и изменение иконки, которое меняет внешний вид инструмента, не меняя его функциональность.

## Проверка сессий в CI

Пропускайте записанный запуск агента через гейт по ошибкам, повреждению потока, предупреждениям протокола, несоответствиям заголовков маршрутизации, вызовам, которые так и не получили ответа, потерянным кадрам, оставляющим захват неполным, дрейфу определений инструментов или использованию устаревших функций протокола.```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 и warn приводят к провалу проверки сами по себе. Остальные — опциональны. Передайте подмножество через запятую, чтобы проверять только то, что важно для задания, опустите сеанс, чтобы проверить самую свежую запись, или используйте -, чтобы читать JSONL из стандартного ввода.

СигналПриводит к провалу при
errorвызове, получившем ответ с ошибкой JSON-RPC, результате с пометкой isError или задаче, завершившейся сбоем
invalidкадре на канале протокола, не являющемся корректным JSON-RPC, обычно когда сервер пишет в stdout
warnкадре, нарушающем какое-либо ожидание, задаваемое спецификацией MCP или JSON-RPC
mismatchзаголовке маршрутизации, расходящемся с телом, передаваемом в составе batch или отсутствующем там, где его требует ревизия
pendingзапросе, всё ещё открытом на момент завершения записи, из-за чего вызывающая сторона так и не дождалась ответа
late-resultответе, пришедшем после отмены своего запроса
driftизменении объявленного определения инструмента после утверждения базовой линии
deprecatedфункции, объявленной спецификацией устаревшей
incompleteкадрах, потерянных выше по потоку, из-за чего все остальные счётчики — это нижняя граница, а не полный итог
schemaобъявленной схеме, использующей конструкцию или диалект, которые плохо переносятся между клиентами

Каждый сигнал подсчитывается независимо от того, влияет ли он на проверку, поэтому прогон сообщает, что он обнаружил, прежде чем вы решите, что должно вызывать провал.``` 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

Счётчик потерянных кадров также передаётся вместе с артефактами, поэтому захват,
который занижает собственные показатели, сообщает об этом везде, где его открывают:
`missing_frames` в JSON-экспорте, `log.comment` в HAR и атрибут ресурса
`mcpsnoop.session.missing_frames` в OTLP.```bash
mcpsnoop check build-agent
mcpsnoop check --fail-on error,invalid artifacts/session.jsonl
mcpsnoop check --fail-on mismatch gateway-run.jsonl

Помимо подсчёта сигналов, проверяйте структуру запуска. Эти опции сочетаются друг с другом и с --fail-on, и любой сбой приводит к ненулевому коду выхода.

ФлагСбой при
--max-duration <dur>один или несколько завершённых вызовов инструментов превысили бюджет; выводит их количество и худший вызов
--expect-tool <name>указанный инструмент ни разу не был вызван (можно указывать несколько раз)
--forbid-tool <name>указанный инструмент был вызван (можно указывать несколько раз)

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

### Сообщайте туда, куда уже смотрит CI

`--format junit` записывает один `<testcase>` на каждый сигнал и сессию, а его сбои
следуют тому же выбору `--fail-on`, что и текстовый вывод.```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 вместо этого записывает журнал SARIF 2.1.0. Там, где junit сообщает один агрегат на сигнал, SARIF сообщает один результат на находку, содержащий сеанс, Seq кадра и собственный текст предупреждения или дрейфа этого кадра, и указывающий на строку журнала, из которой был декодирован кадр. Сигнал, указанный в --fail-on, сообщается на уровне error, а сигнал вне него — на уровне note, так что отчёт и гейт никогда не расходятся.

Результат ссылается на журнал путём, относительным к рабочему каталогу, который code scanning затем разрешает относительно корня репозитория. Оповещение отображается с окружающими строками только тогда, когда этот путь является файлом в анализируемом коммите, поэтому файл захвата, который workflow создал в artifacts/, открывает оповещение с сообщением, правилом и номером строки, но без просмотра исходного кода. Зафиксировать в репозитории файл захвата, который вы хотите отобразить полностью, — единственный способ получить такой просмотр. Журнал, прочитанный из каталога состояния или из stdin, вообще не получает пути.

Code scanning отклоняет файл, чей прогон содержит более 25 000 результатов, и отображает только верхние 5 000 из принятых, поэтому отчёт ограничен 5 000: сначала находки, на которых провалился гейт, затем результат mcpsnoop/report-truncated, сообщающий, сколько было пропущено. Форматы text и junit остаются полными.

Чтобы поместить находки на вкладку Security, передайте журнал SARIF в upload-sarif. Заданию требуется security-events: write, иначе загрузка вернёт 403. check завершается с ненулевым кодом при находке, поэтому шагу загрузки нужен if: always(), чтобы он вообще выполнялся на прогонах, которым есть о чём сообщить; continue-on-error передаёт вердикт проверке code scanning, которая завершается ошибкой на оповещении уровня error и может быть сделана обязательной проверкой. Уберите его, если предпочитаете, чтобы сам шаг проверки делал задание красным.```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
### Обнаружение заголовка маршрутизации, не согласующегося с телом

В транспорте streamable-HTTP шлюз маршрутизирует по `Mcp-Method` и `Mcp-Name`, пока сервер читает тело, поэтому заголовок, не согласующийся с телом, означает, что они видят два разных запроса. Сигнал `mismatch` охватывает и этот случай, и заголовок, следующий с пакетом, который он не может адресовать, и полностью отсутствующий обязательный заголовок.

Начиная с 2026-07-28 отсутствующий заголовок маршрутизации является ошибкой валидации, и соответствующий спецификации сервер отклоняет запрос с `400` и `-32020`. mcpsnoop сообщает о нём только тогда, когда известно, что сеанс работает на этой ревизии или более поздней, поскольку более ранние ревизии вообще не определяют эти заголовки, и их отсутствие там корректно. Собственное отклонение сервера с `-32020` считается тем же сигналом.

Имя или URI ресурса, которые не помещаются в значение HTTP-поля, передаются в Base64 в виде сентинела `=?base64?…?=`, который декодируется перед сравнением, поэтому клиент, кодирующий корректно, никогда не помечается.

Для HTTP-запросов `tools/call` mcpsnoop также показывает каждый заголовок `Mcp-Param-{Name}` и, когда известно соответствующее объявленное определение инструмента, сравнивает его с аннотированным путём аргумента. Вложенные свойства, Base64-сентинел, логические значения и численно эквивалентные безопасные целые числа обрабатываются без ложных срабатываний строкового сравнения. Неизвестные заголовки параметров и сеансы без соответствующего определения инструмента остаются в режиме наблюдения. Маскирование на основе ключей и значений применяется к захваченным значениям заголовков параметров до того, как они попадут в приёмник, и значение, которое mcpsnoop замаскировал сам, никогда не сообщается как расхождение.

### Обнаружение расхождения определений инструментов

Первое полное `tools/list`, наблюдавшееся для метки сервера, становится его доверенным эталоном. Более поздние сеансы сравнивают этот эталон поле за полем: описание, название, схемы ввода и вывода, аннотации и иконки, а также инструменты, которые были добавлены или удалены. Аннотации имеют наибольшее значение, поскольку инструмент, одобренный с `readOnlyHint`, который позже объявляет себя разрушительным, — это тот самый подвох, ради которого существует эта проверка, а спецификация предписывает клиентам относиться к аннотациям как к ненадёжным. Название и иконки отслеживаются, потому что именно их видит пользователь, а спецификация ставит `title` инструмента выше `annotations.title` и его имени. Таблица сеансов и сводка по инструментам помечают расхождение, не блокируя и не изменяя MCP-трафик.

Аннотации сравниваются с учётом значений по умолчанию их спецификации, поэтому сервер, который начинает явно прописывать подсказку, на которую уже полагался, не помечается. Эталон, записанный до того, как mcpsnoop начал отслеживать поле, продолжает работать для полей, которые он записывает, и сообщает, за какие не может поручиться; перезапишите его с помощью `mcpsnoop baseline --accept`, когда доверитесь текущим определениям.

Изменение того, что записывает маскирование, меняет то, что сравнивается при проверке расхождения. Эталон, снятый без `--redact-value`, а затем проверенный по захвату, снятому с этим флагом, помечает замаскированные поля как изменённые, что корректно, поскольку записанное определение действительно изменилось. Перезапишите с флагом `--accept` после изменения настроек маскирования.

Используйте стабильный уникальный флаг `--label` для каждого сервера, у которого имя команды или целевой хост в противном случае совпадали бы. Эталоны хранятся в обычном каталоге состояния mcpsnoop, поэтому `MCPSNOOP_HOME` и `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

В эфемерном CI каталог состояния исходно пуст, поэтому первый запуск лишь фиксирует базовую линию и не сообщает об отклонениях. Базовая линия должна сохраняться между запусками, чтобы последующие запуски могли сверяться с ней. Укажите --baseline на каталог, зафиксированный в репозитории или кэшированный, либо задайте MCPSNOOP_HOME как постоянно сохраняемый путь.```bash mcpsnoop check --fail-on drift --baseline .mcpsnoop/baselines session.jsonl

`drift` является опциональным для `check`; стандартный фильтр `error,invalid,warn` не изменился.

### Помечать устаревшие возможности протокола

Редакция от 2026-07-28 объявляет устаревшими Roots, Sampling и Logging. Они продолжат
работать как минимум год, поэтому mcpsnoop помечает их, а не считает ошибками.
Поток, инспектор возможностей и экспорт помечают их, и каждая пометка
называет замену.

Две из трёх теперь доступны только через запрос с несколькими циклами обмена,
когда имя метода находится внутри карты `inputRequests` сервера, а не в самом
кадре. Они тоже помечаются, так что сервер, перешедший на новый паттерн,
не перестанет молча сообщать о них.```bash
mcpsnoop check --fail-on deprecated session.jsonl

Как и drift, deprecated является opt-in. Обычный запуск сообщает количество и остаётся зелёным, поэтому сеанс, использующий всё ещё допустимую устаревшую возможность, сам по себе никогда не сделает CI красным.

Конструкции схем, с которыми клиенты справляются плохо

Сервер может быть полностью корректным и при этом сложным для использования агентом. Клиенты различаются по тому, насколько полно они реально поддерживают JSON Schema, и инструмент, который модель продолжает вызывать неправильно, часто оказывается инструментом, чья схема требует от клиента больше, чем тот способен предоставить.

Сводка по инструментам, открываемая клавишей s, содержит колонку SCHEMA, где названа самая примечательная особенность схемы каждого представленного инструмента, с завершающим +, когда разновидностей больше одной.

ПоказаноЗначение
no rootinputSchema отсутствует, не является JSON-объектом или имеет корневой тип, отличный от "object"
dialect$schema, называющая диалект, отличный от 2020-12, который ревизия использует по умолчанию
ext ref$ref, указывающая за пределы документа; это как раз тот случай, когда спецификация предупреждает реализации не следовать ссылке вслепую
oneOf, anyOf, allOf, notключевое слово композиции, обрабатываемое клиентами непоследовательно
ref$ref, указывающая внутрь того же документа
untypedсвойство, которое не объявляет тип и никак иначе не сообщает, что оно принимает

Все, кроме первого, — это наблюдения, а не вердикты. Схема, использующая oneOf, не ошибочна: просто разные клиенты, скорее всего, прочтут её по-разному, а схема может объявлять любой диалект, какой пожелает. no root — исключение: определение Tool требует inputSchema и жёстко фиксирует корневой тип как "object", поэтому клиент, проверяющий список, сразу отвергает такой инструмент, и он никогда не становится доступным для вызова, при этом в передаваемых данных нет ничего, что объяснило бы причину. Именно поэтому no root возглавляет колонку; схема, вычищенная собственной функцией редактирования mcpsnoop, никогда не сообщается, поскольку нечитаемая схема — не ошибочная.

Это разделение определяет, как check с ними поступает. no root — это предупреждение в кадре tools/list, поэтому при пороге по умолчанию error,invalid,warn оно проваливает проверку без какого-либо флага, и в этом суть: сервер, поставляющий непригодный инструмент, нормально отвечает на каждое рукопожатие и просто никогда не получает tools/call. Наблюдения учитываются как schema_findings, сообщаются в разделе schema findings: и проваливают запуск, только когда вы добавляете schema в --fail-on. И те и другие попадают в --format junit и --format sarif, а export включает список по каждому инструменту в 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

Столбец несёт предупреждающий цвет и никогда не принимает красный цвет столбца ERR, а
mcpsnoop по-прежнему ничего не меняет в пересылаемом им трафике.

Ничто не разрешается и не извлекается. Внешний `$ref` распознаётся только по своей форме
сам по себе, а схема, на которую он указывает, никогда не читается.

### Смотрите, во что сервер обходится вам в контексте

Определения инструментов попадают в контекст модели при каждом разговоре, а
результаты инструментов — при каждом вызове. Сводка по инструментам (`s`) измеряет и то и другое по сессии,
которую вы фактически захватили.

Строка `definitions` — это фиксированная стоимость: сколько весит `tools/list` этого сервера
до того, как сделан хотя бы один вызов. Столбец `DEF` разбивает это по
каждому инструменту, а `RESULT` — сколько на данный момент стоили ответы каждого инструмента. Таблица остаётся
отсортированной по ошибкам и задержке, так что просматривайте `DEF`, чтобы найти дорогие определения;
экспорт перечисляет их сначала самыми тяжёлыми. Строка под таблицей называет единственный
самый тяжёлый результат, который скрывает итог.

Значения определений — это JSON с удалёнными незначимыми пробелами, поэтому
сервер, который делает pretty-print своего `tools/list`, не считается более дорогим, чем
тот, который этого не делает, и один и тот же сервер при разных захватах измеряется одинаково.
`RESULT` — это байты в том виде, в котором они поступили: результат — это разовая полезная нагрузка, а не
контракт, который стоит нормализовать.```bash
mcpsnoop export -T json | jq '.summary.definitions'

Экспорт содержит те же показатели, по каждому инструменту и с разделением на байты описания и схемы, так что объёмное описание и объёмная схема остаются разделимыми, и каждый из них можно отслеживать между снимками. mcpsnoop diff сообщает, изменилось ли описание или схема между двумя сессиями; экспорт — это то, где хранится размер этого изменения.

Это байты, а не токены. Количество токенов зависит от модели, поэтому его измерение означало бы поставку токенизатора и выбор, чьего. Байты точны, и вы можете применить собственное соотношение. Незавершённый tools/list сообщает увиденное как нижнюю границу и прямо говорит об этом, а не выдаёт частичную сумму за итог.

Обнаружение клиента, который искажает состояние сервера

В рамках шаблона multi round-trip сервер передаёт клиенту непрозрачный requestState, и клиент должен вернуть его без изменений при повторной попытке. Серверу предписано считать его входными данными, контролируемыми атакующим, потому что клиент, который вмешивается в него, может попытаться изменить поведение сервера или обойти проверку авторизации.

Находясь в канале, mcpsnoop видит, как значение уходит и возвращается, поэтому может сказать, когда контракт был нарушен. Есть три способа, которыми это может нарушиться, и каждый сообщается как предупреждение протокола при повторной попытке.

СообщениеЗначение
MRTR retry changed requestStateклиент отправил не то, что выдал сервер
MRTR retry is missing requestStateсервер выдал значение, а повторная попытка его пропустила
MRTR retry invented requestStateповторная попытка содержала значение, которое сервер никогда не выдавал

Это нарушения протокола со стороны клиента, а не наши наблюдения, поэтому они передаются по обычному сигналу предупреждения, и обычный запуск check завершается ошибкой при одном из них. Это намеренно. Клиент, искажающий состояние сервера, заслуживает остановки сборки.

Само значение никогда не отображается и не логируется, и ничто его не декодирует и не разбирает. Это может быть зашифрованный блок, содержащий субъект доступа и токен, и сравнение непрозрачных байтов и есть вся проверка.

Один случай остаётся вне досягаемости. Когда сервер отвечает requestState и не возвращает inputRequests, изменённая повторная попытка не сопоставляется ни с чем и не возвращает ключей, так что ничто не связывает её с исходным запросом, и она читается как несвязанный вызов, а не как нарушение.

Наблюдение с другой машины

Выполняйте захват локально на машине, где происходит трафик, а для сетевого перехода используйте SSH, чтобы mcpsnoop не нуждался в собственном удалённом транспорте.

Просмотр в реальном времени

Запустите TUI на своей рабочей станции и пробросьте сокет mcpsnoop удалённой машины обратно на неё. Живой туннель использует проброс Unix-сокетов по SSH, поэтому обе стороны должны работать под Linux или macOS. В Windows используйте посмертную копию лога ниже.```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

Сокет находится в каталоге состояния удалённой системы, определяемом как `MCPSNOOP_HOME`,
иначе `XDG_STATE_HOME/mcpsnoop`, иначе `~/.local/state/mcpsnoop`. По умолчанию mcpsnoop
предполагает домашний каталог Linux `/home/<user>` из вашего `user@host` и выводит напоминание
в stderr всякий раз, когда прибегает к этому предположению. Если на удалённой системе расположение
определяется иначе, укажите этот единственный нестандартный элемент.```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

Пост-мортем

Транслируйте удалённую сессию прямо в TUI через SSH, локальная копия не нужна.```bash ssh remote-user@remote-host 'cat ~/.local/state/mcpsnoop/sessions/session.jsonl' | mcpsnoop open -

Чтобы вместо этого сохранить локальную копию, скопируйте логи с помощью scp в каталог сеансов и запустите
TUI как обычно.```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

Security

mcpsnoop запускает команду сервера, которую вы оборачиваете, поэтому оборачивайте только доверенные серверы, а ненадежные запускайте в контейнере. Он никогда не выполняет ничего, чего вы не поместили в конфигурацию своего клиента.

Захваченные фреймы могут содержать подсказки, аргументы инструментов, учетные данные и результаты инструментов. Если полезные данные могут нести секреты, включите редактирование (redaction), чтобы вычистить наблюдаемые копии трассировки, в то время как проксируемые байты по-прежнему проходят без изменений.

Редактирование на основе ключей заменяет целые значения под совпадающими ключами JSON-объектов, и тот же набор ключей применяется по мере возможности к аргументам командной строки обернутого сервера, так что --api-key=sk-x и --token sk-x затираются при использовании --redact-secrets. Аргумент, несущий секрет без узнаваемого имени флага, обнаружен быть не может.

Редактирование на основе пути заменяет только значения, выбранные выражением JSONPath, что полезно, когда общее имя ключа чувствительно в одном месте, но безопасно в другом. Повторяйте --redact-path, чтобы вычистить более одного места.

Редактирование на основе значений применяет регулярные выражения к наблюдаемым строковым значениям, тексту stderr и не-JSON текстовым фреймам.

Все три подхода действуют по принципу best effort. Регулярные выражения могут пропускать секреты, избыточно совпадать с безобидным текстом или не видеть преобразованные или закодированные значения.

Редактирование никогда не превращается в обвинение. Каждая проверка, которая сравнивает одно наблюдаемое явление с другим, маршрутизирующий заголовок с телом, значение Mcp-Param с аргументом, который оно отражает, схему инструмента с тем, что требует от него ревизия, знает, когда именно mcpsnoop был стороной, переписавшей байты, и молчит, а не сообщает о сервере из-за настройки приватности самого пользователя. Дрейф определения инструмента — исключение, и намеренно: включение редактирования меняет то, что записывается, а значит и то, что содержит базовая линия. См. Обнаружение дрейфа определения инструмента.```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+'

Для удалённых рабочих процессов используйте SSH-туннелирование или SSH-передачу
файлов, чтобы аутентификация транспорта, шифрование, проверка хоста, ротация
ключей и политика аудита оставались в вашей существующей SSH-инфраструктуре.

## Участие

Приветствуются Issues и pull requests. Подробности см. в
[CONTRIBUTING.md](https://github.com/kerlenton/mcpsnoop/blob/HEAD/CONTRIBUTING.md).

## Лицензия

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

Категории