سرورهای MCP خودمیزبان زیاد میشوند. اول یکی برای مستندات داخلی، بعد یکی برای پایگاه داده، بعد کسی یک سرور کوچک برای پایپلاین استقرار مینویسد. هر کدام پورت، URL و رمز خودش را دارد و هر نفر در تیم هر سه را در پیکربندی کلاینتش میچسباند. عوض کردن یک توکن یعنی به همه پیام دادن.
درگاه همهٔ اینها را به یک آدرس و یک توکن تبدیل میکند. کلاینتها به https://mcp.example.com/docs/mcp، .../db/mcp و مانند آن وصل میشوند. درگاه توکن را بررسی میکند، هر مسیر را به سرور درست روی localhost میفرستد و خود سرورها هرگز بیرون در معرض نیستند. خیلی کم لازم است: یک VPS کوچک با IPv4 اختصاصی و Caddy.
پیکربندی زیر را روی یک سرور آزمایشی ساختیم و پیش از نوشتن با کلاینتهای واقعی بررسی کردیم.
چه لازم دارید
- یک VPS با IPv4 عمومی تا کلاینتها به ۴۴۳ برسند. اگر سرورهای MCP شما خودشان کار سنگین نمیکنند، Nano-IP کافی است.
- یک دامنه با رکورد A که به سرور اشاره کند، اینجا
mcp.example.com. - Ubuntu 24.04 یا Debian 12/13. Caddy در مخزنهای استاندارد هر دو هست.
۱. سرورهای 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، و خیلیهای دیگر هم همینطورند. گام ۳ این را حل میکند.
۲. 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
Caddy گواهی mcp.example.com را خودش میگیرد.
بلوک route تزئینی نیست. نسخهٔ اول ما بررسی توکن و بلوکهای handle_path را کنار هم در سطح بالا داشت و درخواستهای بدون توکن با 200 رد میشدند. Caddy دستورها را با ترتیب خودش مرتب میکند و handle_path پیش از respond اجرا میشود. داخل route به همان ترتیبی که نوشته شدهاند اجرا میشوند. اگر این پیکربندی را تغییر میدهید، اول حالت «بدون توکن» را آزمایش کنید.
handle_path پیشوند را حذف میکند، پس /everything/mcp بهصورت /mcp به سرور میرسد. flush_interval -1 رویدادهای استریم را همان لحظه که میرسند عبور میدهد.
۳. همه چیز جز SSH و HTTPS را ببندید
apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
بعد از این، در آزمایش ما پورتهای 3101 و 3102 از بیرون سرور در دسترس نبودند، در حالی که ۲۲ و ۴۴۳ پاسخ میدادند. پورت ۸۰ فقط برای این باز میماند که Caddy گواهی بگیرد و تمدید کند و به HTTPS هدایت کند.
۴. بررسی
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 را دوباره بارگذاری کنید. پیکربندی هیچکس تغییر نمیکند، جز یک URL تازه زیر همان میزبان و با همان توکن.
محدودیتهایی که باید بدانید
- یک توکن برای همه ساده است و عوض کردنش یک فایل است، اما نمیتوانید دسترسی یک نفر را بدون تغییر برای همه لغو کنید. برای دسترسی جداگانهٔ کاربران، یک پراکسی احراز هویت جلویش بگذارید یا به هر نفر مسیر و توکن خودش را بدهید.
- درگاه سرور را امن نمیکند. سرور MCP که دستور shell اجرا میکند، برای هر کسی که توکن دارد همچنان یک shell است. توکنها را از پیکربندیهای مشترک و git دور نگه دارید.
- اندازه را بر اساس سرورها انتخاب کنید، نه درگاه. خود Caddy چند ده مگابایت مصرف میکند. اگر یکی از سرورها مدل محلی اجرا میکند یا مخزن بزرگی را ایندکس میکند، پلن را او تعیین میکند، نه پراکسی.
فقط یک سرور MCP دارید؟ VPS برای سرورهای MCP آن حالت سادهتر را پوشش میدهد. میخواهید ایجنتها سروری را هم که رویش زندگی میکنند اداره کنند؟ این کار را سرور MCP شرکت EQVPS انجام میدهد (یا REST API اگر اسکریپت را ترجیح میدهید) و پلنهای دارای IPv4 اختصاصی از ماهی $8 شروع میشوند.
نظرات
هنوز نظری نیست. اولین نفر باشید.