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

Запуск сервера

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

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

mcp.run() запускает сервер.

Единственное решение, которое нужно принять, — это транспорт: как именно байты перемещаются между сервером и его клиентом.

Выбор транспорта

Транспорт Что это Когда
stdio Хост запускает ваш файл как подпроцесс и общается с ним через его stdin и stdout. Локальные серверы. Вариант по умолчанию.
streamable-http Настоящий HTTP-сервер, слушающий порт. Всё, что вы развёртываете.
sse Старый HTTP-транспорт. Никогда.

Warning

В редакции протокола 2025-03-26 на смену SSE пришёл Streamable HTTP. mcp.run(transport="sse") по-прежнему работает, со своими параметрами sse_path= и message_path=, но существует лишь ради клиентов, которые ещё не перешли. Ничего нового на нём не стройте.

mcp.run()

server.py
from mcp.server import MCPServer

mcp = MCPServer("Bookshop")


@mcp.tool()
def search_books(query: str) -> str:
    """Search the catalog by title or author."""
    return f"Found 3 books matching {query!r}."


if __name__ == "__main__":
    mcp.run()
  • run() синхронный. Он блокирует выполнение на всё время жизни сервера.
  • Без аргументов транспорт — stdio.
  • Вызов стоит под if __name__ == "__main__":, потому что всё, что загружает ваш сервер (mcp dev, mcp run, mcp install, тесты), импортирует этот файл. Эта проверка не даёт импорту превратиться в работающий сервер.

stdio

Настраивать нечего. Хост запускает ваш файл как дочерний процесс, пишет запросы в его stdin и читает ответы из его stdout.

Запустите его сами — и увидите следствие:

python server.py

Ничего не выводится, и управление не возвращается. Процесс ждёт на stdin, пока хост не заговорит первым.

Это также означает, что stdout и есть канал связи. Во время обслуживания SDK переносит этот канал на приватный дескриптор, а вывод, который сбрасывается (flush) в stdout (подпроцесс, пишущий в унаследованный stdout, print() со сбросом буфера), перенаправляет в stderr, где он не может повредить поток. Вывод, сброшенный в stdout до начала обслуживания (echo в скрипте-обёртке, небуферизованный print при импорте), всё равно попадает в канал связи — как и print(), который остаётся в буфере, пока интерпретатор не сбросит его при выходе. Для вывода, который вам действительно нужен, правильный инструмент — модуль logging: его обработчик сбрасывает каждую запись в stderr сразу по мере появления. Подробнее — на странице Логирование.

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

uv run mcp dev server.py

Inspector делает ровно то же, что и настоящий хост: запускает server.py как подпроцесс и подключается к нему через stdio.

Порт вы ему не указывали. Его и нет.

Streamable HTTP

Чтобы вместо этого посадить тот же сервер на порт, укажите транспорт (и его параметры) в run():

server.py
from mcp.server import MCPServer

mcp = MCPServer("Bookshop")


@mcp.tool()
def search_books(query: str) -> str:
    """Search the catalog by title or author."""
    return f"Found 3 books matching {query!r}."


if __name__ == "__main__":
    mcp.run(transport="streamable-http", port=3001)

Эта единственная строка собирает приложение Starlette и обслуживает его через uvicorn. Клиенты подключаются к http://127.0.0.1:3001/mcp.

У каждого транспорта свои именованные аргументы, и все они передаются в run():

  • host / port: где слушать. По умолчанию 127.0.0.1 и 8000.
  • streamable_http_path: где находится конечная точка MCP. По умолчанию /mcp.
  • json_response=True: отвечать на каждый POST одним JSON-телом вместо SSE-потока. В этом теле есть место только для ответа и ничего больше, поэтому инструмент, который обращается к клиенту посреди запроса (ctx.elicit(), сэмплирование (sampling)), на этом участке выбрасывает NoBackChannelError, а уведомления, привязанные к выполняющемуся вызову (ход выполнения от ctx.report_progress(), лог-сообщения отдельного вызова), отбрасываются; отдельный поток GET по-прежнему доставляет не связанные с вызовом уведомления.
  • stateless_http=True: свежий транспорт на каждый запрос, без отслеживания сессий.
  • max_request_body_size: максимальный принимаемый размер тела POST в байтах. По умолчанию 4 МиБ; более крупные запросы получают HTTP 413 ещё до разбора и создания сессии. Увеличивайте его, только если легитимные MCP-сообщения превышают этот размер.
  • event_store, retry_interval, transport_security: возобновляемость и защита от DNS-rebinding. Они могут подождать, пока вы не развернётесь где-то кроме localhost; transport_security разобран на странице Развёртывание и масштабирование.

Warning

Параметры транспорта передаются в run(), а не в MCPServer(...). Конструктор описывает, что ваш сервер собой представляет: имя, версию, инструкции. run() описывает, как он обслуживается. Перепутайте — и Python ответит раньше, чем дело вообще дойдёт до MCP:

TypeError: MCPServer.__init__() got an unexpected keyword argument 'port'

run() — короткий путь. Как только нужно больше (сервер, смонтированный внутри существующего приложения, два сервера в одном процессе, CORS для браузерных клиентов), вы собираете ASGI-приложение сами и отдаёте его любому ASGI-хосту. Об этом — Добавление в существующее приложение.

Настройки сервера

Пара вещей, связанных с запуском, к транспорту не относится. Это аргументы конструктора:

server.py
from mcp.server import MCPServer

mcp = MCPServer("Bookshop", log_level="DEBUG")


@mcp.tool()
def search_books(query: str) -> str:
    """Search the catalog by title or author."""
    return f"Found 3 books matching {query!r}."


if __name__ == "__main__":
    mcp.run()
  • log_level: передаётся в logging.basicConfig() в момент создания MCPServer(...). Это настраивает корневой логгер, так что уровень задаётся и для ваших собственных логгеров, а не только для логгеров SDK. По умолчанию "INFO".
  • debug: пробрасывается в приложение Starlette, которое собирают HTTP-транспорты. По умолчанию False.

Оба попадают в mcp.settings, откуда их можно прочитать во время выполнения.

Команда mcp

Дополнение [cli] устанавливает небольшую утилиту командной строки поверх всего этого.

mcp dev запускает ваш сервер под MCP Inspector:

uv run mcp dev server.py
uv run mcp dev server.py --with pandas --with numpy
uv run mcp dev server.py --with-editable .

--with добавляет пакеты в создаваемое окружение; --with-editable устанавливает в него ваш собственный пакет. Нужен npx в PATH: Inspector — это приложение на Node.js.

mcp run импортирует файл, находит объект сервера (mcp, server или app на уровне модуля) и вызывает у него run():

uv run mcp run server.py
uv run mcp run server.py:bookshop

Суффикс после : указывает имя объекта, если он называется не mcp, server и не app.

Блок if __name__ == "__main__": здесь никогда не выполняется: mcp run вызывает run() сам, и единственный параметр, который он пробрасывает, — --transport.

mcp install регистрирует сервер в Claude Desktop, чтобы приложение запускало его за вас:

uv run mcp install server.py --name "Bookshop"
uv run mcp install server.py -v API_KEY=abc123 -f .env

-v KEY=VALUE и -f .env записывают в эту запись переменные окружения. Claude Desktop запускает ваш сервер в отдельном процессе. Окружения вашей оболочки там нет.

Claude Desktop — единственный хост, который знает mcp install. Все остальные хосты (Claude Code, Cursor, VS Code) принимают ту же команду запуска в собственном конфигурационном файле, и каждый из них описан на странице Подключение к настоящему хосту.

mcp version выводит версию установленного SDK.

Tip

mcp dev и mcp run понимают только MCPServer. Если вы строите сервер на низкоуровневом Server, запускать его придётся самим. См. Низкоуровневый Server.

Итоги

  • Транспорт — это то, как байты добираются до сервера: stdio для локального подпроцесса, streamable-http для порта. SSE вытеснен.
  • Транспорт выбирает mcp.run(). Без аргументов это stdio, и вызов блокирует выполнение.
  • Любой параметр транспорта (host, port, streamable_http_path, ...) — это аргумент run() и никогда не MCPServer(...).
  • Держите run() под if __name__ == "__main__":. Всё, что загружает ваш сервер, сначала импортирует файл.
  • log_level= и debug= — аргументы конструктора; они попадают в mcp.settings.
  • mcp dev — для Inspector, mcp run — чтобы выполнить файл, mcp install — для Claude Desktop, mcp version — узнать версию.
  • Транспорт никогда не меняет того, что ваш сервер собой представляет: все три файла на этой странице предоставляют один и тот же инструмент.

Когда ограничением становится сам run() (сервер внутри уже существующего приложения), нужна страница Добавление в существующее приложение. Настоящее имя хоста и больше одного воркера — это Развёртывание и масштабирование. А если часть ваших клиентов всё ещё на версии спецификации 2025-11-25 или более ранней, хорошие новости ждут на странице Обслуживание клиентов старого поколения.