Что записывать в журнал при каждом вызове инструмента MCP

Клиент видит удалённый MCP-сервер как один непрозрачный спан, а дашборд каталога видит итоговые цифры. Сам вызов видел только сервер. Какую строку MCP-серверы Tajo пишут для каждого вызова, поле за полем, что из неё вычищается и что пока не построено.

логирование mcp сервера

Что MCP-сервер должен записывать при каждом вызове инструмента?

Записывайте одну строку на каждый вызов инструмента, на сервере, потому что только сервер его видел. MCP-серверы Tajo фиксируют, кто вызывал (идентификатор ключа, но никогда не его секрет), какой сервер и коннектор, инструмент, если он существует, его класс воздействия, ok или короткий код ошибки, длительность, размер результата, идентификатор трассировки, записи, которых коснулся вызов, а также аргументы и ответ, очищенные от секретов и персональных данных. Строки читаются через logs.list и logs.get и удаляются через 30 дней. Они хранятся отдельно от журнала аудита с хеш-цепочкой, где лежат одобрения и дайджесты их входных данных. Не построено: оценка работоспособности, оповещение о частоте ошибок, поиск по инструменту или идентификатору трассировки и экспорт в вашу собственную систему трассировки.

Узнать больше
На этой странице

8 июня 2026 года Anthropic дала владельцам коннекторов из каталога Claude дашборд. Он показывает активных пользователей, общее число вызовов инструментов и место в каталоге. Он показывает «оценку работоспособности, частоту ошибок и задержку с первого взгляда, с разбивкой ошибок по инструментам, чтобы точно найти, что сбоит».

Годом ранее Alex Volkov из Weights & Biases описал ту же проблему с другого конца. Как только его агент стал вызывать инструменты через MCP, «вся та наблюдаемость, к которой я привык, когда мой собственный код выполняется от начала до конца, немного погасла». Его соавтор доклада, Benjamin Eckel из Dylibso, показал, почему так. Удалённый MCP-сервер «выглядит для нас в трассировке как один спан сервиса», потому что он работает вне контроля вызывающей стороны.

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

Вот что записывают MCP-серверы Tajo, прочитанное в коде, который их запускает, и изложенное в документации. Статья Что такое MCP-сервер описывает протокол. Здесь речь о строке журнала.

Одна строка на вызов

Дашборд показывает взгляд начальника порта: какие суда пришли, какие ушли, какие опоздали. Журнал запросов представляет собой судовой журнал, который ведут на борту в тот же момент.

Каждый tools/call на сервере рабочего пространства Tajo и на каждом сервере коннектора заканчивается одной строкой. Строка пишется после того, как ответ сформирован, каким бы он ни был. Вызов, отклонённый потому, что ключу запрещена запись, или потому, что эндпоинт доступен только для чтения, получает строку, как и любой другой. Как и вызов инструмента, которого не существует.

Вот что содержит строка и чего она хранить отказывается.

ПолеХранитсяНе хранится
КтоИдентификатор API-ключа, который вызывал. Токен, полученный при входе через OAuth, тоже считается ключом.Секрет ключа или его префикс.
ГдеСервер рабочего пространства или сервер коннектора, с указанием коннектора и профиля.
ИнструментИмя, если у сервера есть инструмент с таким именем.Любое другое имя, присланное клиентом: оно сохраняется как (unknown).
Воздействиеread, write, send или destructive: самое сильное воздействие, которое мог иметь вызов.
Результатok или короткий код ошибки в snake_case, например unknown_tool.Текст ошибки: всё, что не является кодом, сохраняется как tool_error.
Длительность и размерМиллисекунды от поступления до ответа и байты, которые получила вызывающая сторона.
Идентификатор трассировкиИдентификатор запроса из заголовка x-request-id, x-trace-id или W3C traceparent, иначе новый.Значение заголовка, которое не является шестнадцатеричным числом или UUID.
ЗаписиДо 20 идентификаторов записей, которые вызов назвал или создал: контакт, сообщение, одобрение.Сами записи.
ТелаАргументы и ответ, очищенные, не более 4 КБ каждое.Секреты, свободный текст и персональные данные, перечисленные ниже.

Три из этих отказов касаются клиента, а не данных. Имя инструмента, код ошибки и идентификатор трассировки приходят извне. Каждое из них хранится только в той форме, за которую сервер может поручиться. Журнал, который сохраняет всё, что прислала вызывающая сторона, можно заполнить чем угодно, включая адрес email клиента в столбце с именем инструмента.

Открытый вопрос Benjamin Eckel в конце доклада звучал так: как сборщик узнаёт, «что это вызов инструмента MCP, в котором произошла ошибка». На одном сервере ответом служит столбец. ok равно false, а код говорит, какая это ошибка.

Аргументы: правило, от которого мы отказались

Возражение справедливое, и оно наше. Записывайте аргументы, и вы создадите вторую копию данных своих клиентов, недоступную ни для одного запроса на удаление. До 18 октября 2026 года это было нашим собственным правилом. Журнал не хранил ни аргументы, ни тело результата, потому что и то и другое может содержать персональные данные.

Это был неверный компромисс. Журнал запросов, который не может показать, что было отправлено и что вернулось, не способен ответить на единственный вопрос, ради которого он существует: почему вызов моего агента не удался?

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

  • Ни один секрет не сохраняется. Поле, имя которого указывает на token, password, secret, signature, cookie, session или примерно двадцать похожих слов, хранит только [secret]. Так же обрабатывается значение известного формата учётных данных, где бы оно ни встретилось. Сюда входят bearer-токены, JWT, закрытые ключи, URL с паролем и любая непрозрачная последовательность из 40 и более символов ключа.
  • Прозаический текст не хранится вовсе. Поле с именем html, text, body, markdown или content хранит только [redacted]. В прозе встречаются имена и почтовые адреса, которые не найдёт ни один шаблон.
  • Персональные данные маскируются навсегда. Поле, имя которого обозначает персональные данные, например email, телефон, имя, адрес или заметку, хранит только [redacted]. Адрес email, номер телефона, номер карты или IBAN внутри любой другой строки маскируется.
  • Тела остаются небольшими. Строки обрезаются до 512 символов, списки до 20 элементов, объекты до 50 ключей, а вложенность до 6 уровней. Всё тело обрезается до 4 КБ и помечается как усечённое.

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

Теперь о цене. Маскирование необратимо, это не шифрование с ключом, который у кого-то хранится. Журнал не может показать вам адрес email, на который письмо не дошло. Он показывает идентификатор контакта, а сам контакт, по своим собственным правилам, находится на расстоянии одного вызова. Журнал не входит в процедуру удаления данных рабочего пространства, поэтому маскирование не может её дожидаться.

Почему журнал не является журналом аудита

В Tajo уже есть журнал аудита, в который можно только добавлять. Каждая строка содержит хеш предыдущей строки, поэтому правка в любом месте разрывает цепочку. Почему бы не писать вызовы инструментов туда?

Потому что у цепочки есть цена. Каждое добавление берёт блокировку на уровне рабочего пространства и читает предыдущую строку, чтобы продлить цепочку. Если пропускать через неё каждое чтение, вызовы рабочего пространства выстроятся в очередь за одной блокировкой. А изменения состояния, ради показа которых журнал аудита существует, утонут под тысячами чтений.

Поэтому две записи разделены по вопросам. Журнал запросов говорит, о чём попросил агент и что произошло, в течение 30 дней. Журнал аудита говорит, что изменилось, кто это разрешил и правил ли кто-нибудь запись с тех пор.

Одобрения находятся на стороне аудита. В профилях коннекторов Tajo инструмент propose подготавливает объявленное действие, а инструмент apply выполняет его после одобрения. Ничто не попадает к провайдеру, пока предложение не одобрено человеком или правилом, которое включило рабочее пространство. Строка вызова propose называет среди своих записей одобрение, которое он создал. Само одобрение привязано к дайджесту SHA-256 точных входных данных. Одобрение распространяется на те самые входные данные, которые вы видели: измените одно поле, и оно больше не совпадёт. Когда запись выполняется, её строка аудита хранит дайджест входных данных и дайджест ответа провайдера, но никогда не значения из ответа.

Сколько стоит строка

Строка означает запись в базу данных, а у записи есть коммит. До 8 октября 2026 года каждый вызов ждал собственного коммита перед ответом, около 2,6 мс на нашем сервере. Для одного клиента это составляло четверть небольшого вызова инструмента.

Теперь строка формируется, когда вызов завершается, и ставится в очередь. Сброс записывает очередь не более чем через 100 мс, одной транзакцией на рабочее пространство, поэтому многие вызовы делят один коммит. В самой строке ничего не меняется, включая её метку времени. Если в очереди ждут 5 000 строк, следующий вызов ждёт сброса, а не теряет строку.

Запись в журнал никогда не приводит к сбою вызова. Неудавшаяся запись повторяется построчно, затем фиксируется в логе и отбрасывается. Жёсткое завершение процесса теряет то, что лежало в очереди: не более последних 100 мс вызовов.

Как прочитать журнал

logs.list возвращает вызовы рабочего пространства, начиная с самых новых, без тел. Он фильтрует по времени, по результату или HTTP-статусу, по источнику (MCP или REST API, который использует ту же таблицу) и по API-ключу. По умолчанию он отдаёт по 50 строк на страницу, максимум 100. logs.get возвращает один вызов с текущим именем ключа, его статусом, длительностью, очищенными запросом и ответом и записями, которых он коснулся.

Оба читают только рабочее пространство вызывающей стороны. Идентификатор строки из другого рабочего пространства не находится.

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

Что построено и что нет

Построено на момент написания:

Не построено, и это стоит знать, прежде чем на него полагаться:

  • Нет оценки работоспособности, нет графика по инструментам и нет оповещения о растущей частоте ошибок. Сводка по инструментам с вызовами, ошибками и самыми долгими длительностями есть у операторов Tajo, но ни на одной поверхности, которую можете открыть вы.
  • logs.list не умеет фильтровать по имени инструмента или по идентификатору трассировки. Чтобы найти строку по идентификатору запроса, сузьте поиск по времени и ключу.
  • Строки не экспортируются в систему трассировки, которой управляете вы. Идентификатор трассировки читается только из HTTP-заголовков, а не из поля MCP _meta, которое в докладе использовалось для передачи контекста трассировки.
  • Через 30 дней строка удаляется. Архива или экспорта нет.
  • То, что было замаскировано, восстановить из журнала нельзя.
  • Жёсткое завершение может потерять строки за последние 100 мс.

Доклад завершился мыслью, что наблюдаемость в MCP «распределена неравномерно». Дашборд ниже по цепочке этого не исправит, как и трассировка, которая обрывается у дверей сервера. Синхронизации, которая сбоит, нужна собственная запись; как и каждому вызову, который о чём-то попросил.

Сервер видел вызов. Больше никто. Там его и записывайте.

Часто Задаваемые Вопросы

Что MCP-сервер должен записывать в журнал при каждом вызове инструмента?

Одну строку на вызов, записанную сервером: какой API-ключ вызывал, какой сервер и коннектор, имя инструмента, если такой инструмент существует, его класс воздействия, ok или короткий код ошибки, длительность, размер результата, идентификатор трассировки запроса, идентификаторы записей, которые вызов назвал или создал, а также аргументы и ответ, из которых вычищены секреты и персональные данные. MCP-серверы Tajo пишут такую строку для каждого tools/call, включая отклонённые вызовы, и хранят её 30 дней.

Должен ли журнал запросов MCP хранить аргументы инструмента?

Да, в очищенном виде. Журнал без аргументов не может объяснить, почему вызов не удался. До 18 октября 2026 года Tajo не хранил тела запросов и ответов, а теперь хранит и то и другое после одного детерминированного прохода: секреты превращаются в [secret], свободный текст и персональные поля в [redacted], адреса email, телефоны, номера карт и IBAN маскируются, а каждое тело обрезается до 4 КБ. Маскирование необратимо, поэтому журнал указывает на запись по идентификатору, а не копирует её.

Зачем держать журнал вызовов инструментов MCP отдельно от журнала аудита?

Потому что они отвечают на разные вопросы и стоят по-разному. Журнал аудита Tajo представляет собой хеш-цепочку изменений состояния, и каждое добавление берёт блокировку на уровне рабочего пространства, чтобы продлить цепочку. Если пропускать через него каждое чтение, вызовы рабочего пространства выстроятся в очередь за этой блокировкой, а изменения утонут среди чтений. Вызовы инструментов попадают в отдельную таблицу без цепочки, которая хранится 30 дней; записи, которые подготавливает вызов, по-прежнему оставляют собственные строки аудита.

Подайте заявку на ранний доступ

Укажите имя и адрес электронной почты. Мы отправим ссылку для подтверждения запроса на доступ к Tajo.

Получить Brevo

Эта страница в Markdown: what-to-log-for-every-mcp-tool-call.md

Загрузка Markdown…
Открыть в Claude Открыть в ChatGPT