# MCP-сервер на Linux: запуск по stdio и Streamable HTTP

> Как запускать MCP-сервер на Linux - stdio запускает клиент, Streamable HTTP работает как служба systemd за обратным прокси, и что из этого требует сам протокол

Страница: https://mcpdoc.ru/deployment/ubuntu/
Указатель сайта: https://mcpdoc.ru/llms.txt

Сервер stdio на Linux запускает клиент, а не systemd: у службы без клиента нет собеседника. Службой systemd делают сервер Streamable HTTP. Обычно он слушает `127.0.0.1`, а перед ним стоит обратный прокси, который не задерживает потоковые ответы.

Протокол требует от HTTP-сервера проверять `Origin` и рекомендует локальный адрес. Устройство службы, ограничения systemd и настройка nginx - практика, а не правило MCP.

описана редакция 2026-07-28, сверено со спецификацией и документацией systemd и nginx 4 октября 2026

Совместимость Modern страница описывает редакцию 2026-07-28; отличия старых редакций названы в тексте

- **Страница описывает редакцию:** 2026-07-28

- **Затрагивает устаревшее:** нет

- **Затрагивает удалённое в редакции 2026-07-28:** нет

- **Проверено:** 2026-10-04

- **Источник:** [modelcontextprotocol.io](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports)

Адрес страницы остался от прежней версии сайта. Содержание относится к любому дистрибутиву Linux с systemd.

## Сервер stdio запускает клиент

По странице [stdio](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio) клиент запускает сервер как дочерний процесс и общается с ним через `stdin` и `stdout`. Отсюда три следствия для Linux.

- Службу systemd для сервера stdio создавать не нужно. Запущенный без клиента, такой процесс ждёт сообщений на `stdin`, которых никто не пришлёт.
- Команду запуска вы указываете в настройках клиента. Где они лежат, смотрите на странице [Клиенты MCP](https://mcpdoc.ru/reference/clients/).
- Журнал сервера идёт в `stderr`. В `stdout` сервер не должен писать ничего, кроме сообщений MCP.

Завершение тоже ведёт клиент. Ему следует закрыть `stdin` сервера, подождать выхода и только потом завершить процесс принудительно. Спецификация отмечает, что в POSIX это обычно `SIGTERM`, затем `SIGKILL`. Серверу следует выходить сразу, как только `stdin` закрыт.

## Что протокол требует от сервера Streamable HTTP

По странице [Streamable HTTP](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http):

| Требование | Сила |
|---|---|
| Один адрес, принимающий POST | обязан |
| Проверка заголовка `Origin` у всех входящих соединений | обязан |
| Ответ `403 Forbidden`, если `Origin` есть и он недопустим | обязан |
| При локальном запуске слушать только `127.0.0.1`, а не `0.0.0.0` | следует |
| Проверка подлинности всех соединений | следует |
| Заголовок `X-Accel-Buffering: no` при открытии потока SSE | следует |

Спецификация объясняет первые меры так: без них сайт в браузере может через подмену DNS обратиться к MCP-серверу на вашей машине.

Проверку `Origin` выполняет сам сервер. Обратный прокси её не отменяет: для сервера за прокси требование остаётся тем же. Подробнее - на странице [Безопасность сервера](https://mcpdoc.ru/security/server/).

### Что изменилось по сравнению с Legacy

- В Modern нет сессий протокола и нет потока по GET. Серверу только для Modern следует отвечать на GET и DELETE кодом `405 Method Not Allowed`, а заголовок `Mcp-Session-Id` не читать.
- Ответ на запрос приходит либо одним объектом JSON, либо потоком SSE, привязанным к этому запросу. Долгий поток остаётся только у запроса `subscriptions/listen`.
- Транспорта на WebSocket в спецификации нет. Заголовки `Upgrade` в настройке прокси для MCP не нужны.

## Практика, не правило MCP

Серверу Streamable HTTP нужна машина с постоянным адресом. Виртуальный сервер для него можно арендовать у [FirstVDS](https://mcpdoc.ru/go/firstvds/) или [Aéza](https://mcpdoc.ru/go/aeza/).

Всё ниже взято из документации systemd и nginx. Спецификация MCP этого не требует.

### Служба systemd

```ini title="/etc/systemd/system/mcp-server.service, схема"
[Unit]
Description=MCP server (Streamable HTTP)
After=network-online.target
Wants=network-online.target

[Service]
# Команду запуска и рабочий каталог подставьте свои
ExecStart=/opt/mcp-server/bin/start
WorkingDirectory=/opt/mcp-server
User=mcp
Group=mcp
Restart=on-failure

# Ограничения из systemd.exec
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

Пример схематичный, составлен по справочным страницам systemd и здесь не запускался. Путь `/opt/mcp-server/bin/start`, имя службы и пользователь `mcp` условные.

Что означают директивы, по [справочной странице systemd.exec](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html) и [странице systemd.service](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html):

| Директива | Что делает |
|---|---|
| `User=`, `Group=` | задают пользователя и группу, от имени которых работает процесс |
| `Restart=` | определяет, запускать ли службу заново, когда процесс вышел, был убит или истекло время ожидания |
| `NoNewPrivileges=true` | процесс и его потомки не могут получить новые привилегии через `execve()` |
| `ProtectSystem=strict` | вся файловая система доступна только для чтения, кроме `/dev/`, `/proc/` и `/sys/` |
| `ProtectHome=true` | каталоги `/home/`, `/root` и `/run/user` недоступны и выглядят пустыми |
| `PrivateTmp=true` | у службы свои `/tmp/` и `/var/tmp/`, не общие с другими процессами |

Справочная страница systemd.exec предупреждает: многие из этих ограничений молча отключаются там, где ядро или окружение не дают нужного механизма. Например, `ProtectSystem=` не действует, если служба запущена внутри контейнера без пространств имён файловой системы.

При `ProtectSystem=strict` сервер не сможет писать на диск нигде, кроме своих закрытых каталогов `/tmp` и `/var/tmp` (их даёт `PrivateTmp=true`). Если ему нужен каталог для данных, его придётся разрешить отдельно, например директивой `ReadWritePaths=` с той же страницы.

Пользователя можно не создавать заранее: `DynamicUser=` выделяет пару пользователя и группы на время работы службы. Подходит ли это вашему серверу, зависит от того, какие файлы ему нужны.

### Журнал

По странице systemd.exec стандартный вывод службы по умолчанию направляется туда, куда указывает `DefaultStandardOutput=`, а его значение по умолчанию - `journal`. Отдельно настраивать файлы журнала не нужно.

Читает журнал команда `journalctl`. По её [справочной странице](https://www.freedesktop.org/software/systemd/man/latest/journalctl.html) параметр `-u` показывает сообщения одной службы:

```bash title="Журнал службы"
journalctl -u mcp-server.service
```

Команда взята из справочной страницы journalctl и здесь не запускалась.

Протокольное журналирование через `notifications/message` в редакции `2026-07-28` [объявлено устаревшим](https://modelcontextprotocol.io/specification/2026-07-28/server/utilities/logging). Журнал процесса в journal от него не зависит.

### Обратный прокси

Сервер слушает `127.0.0.1`, а снаружи его видит прокси, который принимает TLS. Главное требование к прокси - не задерживать потоковые ответы.

По [документации модуля proxy в nginx](https://nginx.org/en/docs/http/ngx_http_proxy_module.html):

- `proxy_buffering` по умолчанию включена. При включённой буферизации nginx сначала собирает ответ сервера в буферы. При выключенной ответ передаётся клиенту сразу по мере получения.
- Буферизацию можно включить или выключить для одного ответа, передав `yes` или `no` в заголовке `X-Accel-Buffering`. Именно этот заголовок спецификация MCP рекомендует серверу отправлять. Способность можно отключить директивой `proxy_ignore_headers`, поэтому проверьте, что в вашей настройке её нет для этого заголовка.
- `proxy_read_timeout` действует между двумя последовательными чтениями, а не на весь ответ. Если сервер ничего не передал за это время, соединение закрывается.

```nginx title="Фрагмент настройки nginx, схема"
location /mcp {
    proxy_pass http://127.0.0.1:ВАШ_ПОРТ;
    proxy_buffering off;
}
```

Пример схематичный, составлен по документации nginx и здесь не запускался. Блок `server`, TLS и остальные директивы опущены.

Последний пункт связан с примечанием в спецификации: для долгих потоков, прежде всего у `subscriptions/listen`, серверу предлагается время от времени отправлять строку-комментарий SSE. Это не требование, а совет, и он как раз не даёт посреднику закрыть тихое соединение. Сколько именно ждать, задаёт ваша настройка прокси. Спецификация чисел не называет.

## Чего здесь нет и что неизвестно

- Правила межсетевого экрана, AppArmor, SELinux и Fail2ban не приводятся: готовых проверенных образцов для MCP в первоисточниках нет.
- Пути к файлам настроек клиентов на Linux здесь не повторяются, они на страницах клиентов.
- Числа - размеры буферов, пределы соединений, время ожидания - не приводятся. Их нет в спецификации, и подбирать их нужно по измерениям на вашем сервере.
- Какой обратный прокси лучше, страница не оценивает. nginx взят потому, что его прямо называет спецификация в рекомендации про `X-Accel-Buffering`.

## Что читать дальше

- [Streamable HTTP и переход с HTTP+SSE](https://mcpdoc.ru/deployment/http-sse/)
- [Docker](https://mcpdoc.ru/deployment/docker/)
- [Производительность](https://mcpdoc.ru/optimization/performance/)
- [Безопасность сервера](https://mcpdoc.ru/security/server/)
