Собствените MCP сървъри се множат. Първо се появява един за вътрешната документация, после за базата данни, после някой пише малък за пайплайна на деплоя. Всеки има свой порт, свой адрес и своя тайна, и всеки човек в екипа слага и трите в конфигурацията на клиента си. Да смениш токен означава да пишеш на всички.
Шлюзът превръща това в един адрес и един токен. Клиентите се свързват с https://mcp.example.com/docs/mcp, .../db/mcp и така нататък. Шлюзът проверява токена, праща всеки път към правилния сървър на localhost, а самите сървъри никога не са отворени навън. Трябва съвсем малко: малък VPS със собствен IPv4 и Caddy.
Конфигурацията по-долу сглобихме на тестов сървър и проверихме с истински клиенти, преди да я опишем.
Какво е нужно
- VPS с публичен IPv4, за да стигат клиентите до 443. Nano-IP стига, ако вашите MCP сървъри сами не вършат тежка работа.
- Домейн с A запис към сървъра, тук
mcp.example.com. - Ubuntu 24.04 или Debian 12/13. Caddy е в стандартните хранилища и на двете.
1. Пуснете MCP сървърите на localhost
Всеки сървър работи като systemd услуга. Тук два пъти ползваме еталонния server-everything, за да е примерът самостоятелен; сложете своите. Нужен е Node.js 20 или по-нов:
mkdir -p /opt/mcp && cd /opt/mcp
npm install @modelcontextprotocol/server-everything
# /etc/systemd/system/mcp-everything-1.service
[Unit]
Description=MCP server everything #1
After=network.target
[Service]
WorkingDirectory=/opt/mcp
Environment=PORT=3101
ExecStart=/opt/mcp/node_modules/.bin/mcp-server-everything streamableHttp
Restart=always
User=nobody
[Install]
WantedBy=multi-user.target
Второто копие ползва порт 3102. После:
systemctl daemon-reload
systemctl enable --now mcp-everything-1 mcp-everything-2
Какво забелязахме при тестовете: този сървър слуша на всички интерфейси, а не само на 127.0.0.1, и много други правят същото. Стъпка 3 решава това.
2. Caddy: първо проверка на токена, после маршрут по път
Генерирайте токен и го подайте на Caddy чрез файл с променливи:
apt install -y caddy
echo "MCP_GATEWAY_TOKEN=$(openssl rand -hex 32)" > /etc/caddy/gateway.env
chmod 600 /etc/caddy/gateway.env
mkdir -p /etc/systemd/system/caddy.service.d
printf '[Service]\nEnvironmentFile=/etc/caddy/gateway.env\n' > /etc/systemd/system/caddy.service.d/env.conf
systemctl daemon-reload
Caddyfile:
mcp.example.com {
route {
@unauthorized not header Authorization "Bearer {$MCP_GATEWAY_TOKEN}"
respond @unauthorized 401
handle_path /everything/* {
reverse_proxy 127.0.0.1:3101 {
flush_interval -1
}
}
handle_path /tools2/* {
reverse_proxy 127.0.0.1:3102 {
flush_interval -1
}
}
respond 404
}
}
caddy validate --config /etc/caddy/Caddyfile && systemctl restart caddy
Сертификата за mcp.example.com Caddy взима сам.
Блокът route не е за красота. В първата версия проверката на токена и блоковете handle_path стояха един до друг на горното ниво, и заявките без токен минаваха с 200. Caddy подрежда директивите по свой ред и handle_path се изпълнява преди respond. Вътре в route те се изпълняват в написания ред. Ако адаптирате тази конфигурация, първо проверете случая „без токен“.
handle_path маха префикса, така че /everything/mcp стига до сървъра като /mcp. flush_interval -1 пропуска поточните събития веднага щом пристигнат.
3. Затворете всичко освен SSH и HTTPS
apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
След това при нашия тест портовете 3101 и 3102 бяха недостъпни отвън, а 22 и 443 отговаряха. Порт 80 остава отворен само за да може Caddy да взима и подновява сертификати и да пренасочва към HTTPS.
4. Проверете
TOKEN=$(cut -d= -f2 /etc/caddy/gateway.env)
INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"1"}}}'
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.example.com/everything/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d "$INIT"
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.example.com/everything/mcp \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d "$INIT"
Първата заявка трябва да изведе 401, втората — 200. При нашия тест грешен и отрязан токен получиха 401, а непознат път — 404 дори с правилен токен.
После от клиент, например от Claude Code:
claude mcp add --transport http everything https://mcp.example.com/everything/mcp \
--header "Authorization: Bearer $TOKEN"
claude mcp list
И двата сървъра показаха ✔ Connected. Същият клиент с грешен токен показа ✘ Failed to connect. Повече за настройката на клиенти — в Свързване на EQVPS MCP с Claude Code и Codex.
Добавяне на следващ сървър
Пуснете го на нов localhost порт, добавете един блок handle_path, презаредете Caddy. Ничия конфигурация не се променя, освен нов адрес под същия хост и със същия токен.
Ограничения, които е добре да знаете
- Един токен за всички е просто и се сменя с един файл, но не можете да отнемете достъпа на един човек, без да го смените за всички. За достъп по потребител сложете auth прокси пред шлюза или дайте на всеки свой маршрут и токен.
- Шлюзът не прави сървъра безопасен. MCP сървър, който изпълнява shell команди, остава shell за всеки с токена. Не дръжте токени в общи конфигурации и в git.
- Размерът се избира според сървърите, не според шлюза. Самият Caddy заема няколко десетки мегабайта. Ако някой сървър пуска локален модел или индексира голямо хранилище, планът се определя от него, не от проксито.
Само един MCP сървър? По-простият случай е описан във VPS за MCP сървъри. Искате агентите и да управляват сървъра, на който живеят? Това може MCP сървърът на EQVPS (или REST API, ако предпочитате скриптове), а плановете със собствен IPv4 са от $8 на месец.
Коментари
Още няма коментари. Бъди първият.