Перейти к содержанию

Логирование

Машинный перевод

Эта страница переведена с английской документации автоматически, и основной версией остаётся английская страница. Если что-то читается неправильно, на странице Переводы объясняется, как об этом сообщить.

Пишите в лог из инструмента так же, как из любой другой функции Python: средствами стандартной библиотеки.

В MCP есть возможность логирования на уровне протокола: сервер мог отправлять свои сообщения лога клиенту в виде уведомлений через методы объекта Context. Ревизия спецификации 2026-07-28 объявляет эту возможность устаревшей и ничем её не заменяет, поэтому в этой документации она не описывается. Полный список того, что устарело и что делать взамен, — на странице Устаревшие возможности.

Взамен делайте то же, что в любой другой программе на Python: используйте стандартную библиотеку.

Инструмент, который пишет в лог

server.py
import logging

from mcp.server import MCPServer

logger = logging.getLogger(__name__)

mcp = MCPServer("Bookshop")


@mcp.tool()
def search_books(query: str) -> str:
    """Search the catalog by title or author."""
    logger.info("Searching for %r", query)
    return f"Found 3 books matching {query!r}."
  • logging.getLogger(__name__) возвращает логгер, названный по имени модуля. Создайте его один раз, в начале файла.
  • Внутри инструмента вызывайте logger.info(...), как в любой другой функции. Ничего не нужно внедрять, ничего не нужно ждать через await, ничего специфичного для MCP.

Check

Вызовите инструмент и посмотрите на результат целиком:

result.content             # [TextContent(text="Found 3 books matching 'dune'.")]
result.structured_content  # {'result': "Found 3 books matching 'dune'."}

Строки лога в нём нет нигде. Логи — для вас, того, кто эксплуатирует сервер. Модель их никогда не видит. Если модель должна что-то прочитать, верните это через return.

Куда попадает вывод

Для сервера на stdio этот вопрос важнее обычного. Хост запустил ваш сервер как подпроцесс и читает MCP-сообщения из его stdout. Стандартный поток ошибок — ваш.

Стандартная библиотека уже поступает правильно: по умолчанию вывод логов идёт в sys.stderr. Строки из logger.info(...) попадают в терминал (или туда, куда хост собирает stderr подпроцесса), а поток протокола остаётся чистым.

Tip

Не используйте print() в stdio-сервере. print пишет в stdout, а stdout принадлежит протоколу. Пока сервер работает, SDK перенаправляет в stderr то, что действительно сброшено из буфера stdout, так что испортить передаваемые данные это не может. Но в процессе с блочной буферизацией вывод print() обычно лежит несброшенным в буфере sys.stdout, пока интерпретатор не опустошит его при выходе — прямо в поток протокола. И даже когда строка перенаправлена, она попадает в вывод логов как есть: без уровня, без имени логгера и без возможности её отфильтровать.

logger.debug("got here") — те же усилия на одну строку, и попадает она куда нужно.

Уровень

Вызывать logging.basicConfig() самостоятельно не нужно. Конструктор MCPServer уже сделал это: с обработчиком, направленным в стандартный поток ошибок, и с уровнем, который вы передаёте в log_level=. Так что MCPServer("Bookshop", log_level="DEBUG") — всё, что нужно, чтобы увидеть строки из logger.debug(...).

По умолчанию — "INFO".

logging.basicConfig() никогда не заменяет уже существующие обработчики. Если настроить логирование самостоятельно до создания сервера, ваша конфигурация имеет приоритет.

Попробуйте сами

Запустите сервер через MCP Inspector:

uv run mcp dev server.py

Вызовите search_books на вкладке Tools. Inspector показывает результат: только возвращённое значение. Строка

Searching for 'dune'

ушла в стандартный поток ошибок: в терминал, а не в передаваемые данные.

Info

Если на самом деле нужна трассировка (каждый запрос, сколько он занял, завершился ли ошибкой), нужны не строки лога, а спаны. Сервер уже их выдаёт: SDK по умолчанию трассирует каждое сообщение с помощью OpenTelemetry. См. OpenTelemetry.

Итоги

  • Возможность логирования в протоколе MCP объявлена устаревшей в спецификации 2026-07-28 и ничем не заменена. Не стройте на ней ничего.
  • logger = logging.getLogger(__name__) на уровне модуля, logger.info(...) в инструменте. Вот и весь паттерн.
  • Вывод логов никогда не доходит до модели. Доходит только значение, которое вы возвращаете через return.
  • Стандартный поток ошибок — ваш; stdout принадлежит протоколу. Пока сервер работает, SDK перенаправляет сброшенный посторонний вывод из stdout в stderr, но несброшенный print() всё равно может вылиться в поток протокола при выходе, а перенаправленные строки приходят без меток. Используйте logging — его обработчик сбрасывает буфер после каждой записи.
  • MCPServer(..., log_level="DEBUG") задаёт уровень, а конфигурацию логирования, которую вы сделали раньше, не трогает.

О том, как сообщить подключённым клиентам, что на сервере что-то изменилось (список инструментов, ресурс), — на странице Подписки.