1. Host сам стартує процес
Cursor/Claude запускають uv run python -m qauto_mcp і тримають stdin/stdout. Немає окремого сервера: закрив IDE — MCP помер. Не підключиш другий клієнт до того самого процесу.
IT · Model Context Protocol
Від stdio до мережевого transport і зручного логування
Урок 11
Блок 1
Освіжимо transport перед міграцією qauto
Повторення Streamable HTTP
Досі qauto MCP server працював через stdio: Cursor запускав процес і передавав credentials у env.
Перед міграцією освіжимо, що таке Streamable HTTP і чим він відрізняється від локального subprocess.
Повторення Streamable HTTP
Transport layer визначає, як JSON-RPC доходить між Host і Server. Формат повідомлень той самий — змінюється лише доставка.
Streamable HTTP — мережевий transport: сервер як окремий процес з HTTP endpoint. Один процес — багато клієнтів. У stdio кожне зʼєднання — окремий subprocess.
Повторення Streamable HTTP
Cursor/Claude запускають uv run python -m qauto_mcp і тримають stdin/stdout. Немає окремого сервера: закрив IDE — MCP помер. Не підключиш другий клієнт до того самого процесу.
Кожен Host піднімає свій процес. Inspector, Cursor і Claude Desktop — три копії сервера, три сесії, три логіни в QAuto.
У stdin/stdout ходить JSON-RPC. Туди не можна писати логи — зламаєш протокол. Діагностика лише в stderr, і то обережно.
Працює, бо Host інжектить QAUTO_EMAIL / QAUTO_PASSWORD при spawn. Для HTTP Host процес не запускає, тому той самий блок env у mcp.json уже нічого не дає.
stdio — локальна труба. Не вийде покласти MCP на сервер/Docker і підключитись по URL. Для цього якраз Streamable HTTP.
Повторення Streamable HTTP
Раніше в MCP був окремий transport HTTP+SSE з двома endpoint'ами. Streamable HTTP замінив його: тепер один endpoint, наприклад /mcp, і через нього йде все.
«Streamable» означає, що будь-яка відповідь може стати потоком, якщо сервер вирішить, що так краще: простий виклик — POST → JSON, довга задача — POST → SSE. GET потрібен лише для повідомлень, які сервер ініціює сам.
Повторення Streamable HTTP
Host сам стартує процес і інжектить credentials у env:
{
"mcpServers": {
"qauto-course": {
"command": "uv",
"args": ["run", "main.py"],
"env": {
"QAUTO_EMAIL": "user@example.com",
"QAUTO_PASSWORD": "YourPass1"
}
}
}
}
Повторення Streamable HTTP
Host не запускає subprocess — ті самі ключі йдуть у headers:
{
"mcpServers": {
"qauto-course": {
"url": "http://127.0.0.1:8000/mcp",
"headers": {
"QAUTO_EMAIL": "user@example.com",
"QAUTO_PASSWORD": "YourPass1"
}
}
}
}
Повторення Streamable HTTP
Блок 2
Два рівні: transport (FastMCP) і додаток (tools + httpx)
Логування
Після міграції на Streamable HTTP логування — не одне ціле. Це два окремі шари.
Framework: старт HTTP, обраний transport, вхідні запити на /mcp. На stdio stdout зайнятий протоколом. На HTTP задаємо log_level у конструкторі.
mcp = FastMCP(
...,
log_level=os.getenv("MCP_LOG_LEVEL", "INFO"),
)
Ваш код: виклики tools і HTTP до QAuto API. Цього log_level FastMCP не дає — потрібен Python logging.
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
logger.info("tool → %s", name)
Логування
log_levelСтарт і зупинку HTTP-сервера, обраний transport (streamable-http), вхідні MCP-запити на endpoint /mcp (JSON-RPC: initialize, tools/call).
API-запити qauto через httpx у tools. Якщо tool викликав get_cars, у stderr від log_level буде лише факт tools/call — без запиту на /api/cars.
Логування
log_levelРівень — фільтр «від і вище». Локально — DEBUG / INFO, production — WARNING / ERROR.
Максимум деталей: bind, transport, кожен запит на /mcp.
log_level="DEBUG"
# Processing request of type CallToolRequest
# Streamable HTTP session manager started
Робочий режим: сервер стартував і слухає endpoint.
log_level="INFO"
# Uvicorn running on http://127.0.0.1:8000
Лише попередження framework, без шуму INFO.
log_level="WARNING"
# Stateless HTTP is currently experimental
Помилки сервера / transport, коли запит не обробився.
log_level="ERROR"
# Error processing request from 127.0.0.1
Сервер не може продовжувати роботу.
log_level="CRITICAL"
# Failed to bind 127.0.0.1:8000
Логування
log_levelЗадаємо один раз у конструкторі — поруч із host/port:
mcp = FastMCP(
name="qauto-course",
host=MCP_HOST,
port=MCP_PORT,
streamable_http_path=MCP_HTTP_PATH,
log_level=os.getenv("MCP_LOG_LEVEL", "INFO"),
)
Логування
log_level покриває лише transport. Щоб бачити, що робить qauto server — виклики tools, запити до QAuto API, час відповіді — потрібен окремий шар: logging у Python + hooks httpx + wrapper для tools.
Фінал заняття
На цій лекції ми освіжили Streamable HTTP, перевели qauto MCP server зі stdio на мережевий transport і розібрали, як credentials переїжджають з env у headers клієнтського конфігу.
Також розвели два рівні логування: log_level FastMCP (transport) і logging додатку (tools + httpx до QAuto) з маскуванням секретів.
Ключова ідея: host, port і streamable_http_path — у конструкторі FastMCP; у run() лишається вибір transport. Далі — контейнеризація MCP server.