Запуск сервера
Машинный перевод
Эта страница переведена с английской документации автоматически, и основной версией остаётся английская страница. Если что-то читается неправильно, на странице Переводы объясняется, как об этом сообщить.
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()
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():
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-хосту. Об этом — Добавление в существующее приложение.
Настройки сервера
Пара вещей, связанных с запуском, к транспорту не относится. Это аргументы конструктора:
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 или более ранней, хорошие новости ждут на странице Обслуживание клиентов старого поколения.