−25%

untuk Windows tahunan, hingga 31 Okt. Lihat paket

EQVPS
Mulai

VPS untuk gateway MCP: beberapa server MCP di balik satu alamat dan satu token

Tim Anda memakai tiga server MCP yang di-host sendiri dan setiap orang punya tiga URL dan tiga rahasia. Taruh semuanya di balik satu alamat HTTPS di VPS kecil: Caddy memeriksa bearer token, merutekan per path, dan menjauhkan server itu sendiri dari internet. Konfigurasi yang sudah diuji.

Server MCP yang di-host sendiri terus bertambah. Mula-mula ada satu untuk dokumentasi internal, lalu satu untuk database, lalu seseorang menulis yang kecil untuk pipeline deploy. Masing-masing punya port, URL, dan rahasianya sendiri, dan setiap orang di tim menempelkan ketiganya ke konfigurasi klien. Mengganti token berarti mengabari semua orang.

Gateway mengubahnya menjadi satu alamat dan satu token. Klien terhubung ke https://mcp.example.com/docs/mcp, .../db/mcp, dan seterusnya. Gateway memeriksa token, mengirim setiap path ke server yang tepat di localhost, dan server itu sendiri tidak pernah terbuka. Yang dibutuhkan sangat sedikit: VPS kecil dengan IPv4 sendiri dan Caddy.

Kami membangun pengaturan di bawah ini di server uji dan memeriksanya dengan klien sungguhan sebelum menulisnya.

Yang Anda butuhkan

  • VPS dengan IPv4 publik, agar klien bisa menjangkau 443. Nano-IP sudah cukup, kecuali server MCP Anda mengerjakan tugas berat sendiri.
  • Domain dengan record A yang mengarah ke server, di sini mcp.example.com.
  • Ubuntu 24.04 atau Debian 12/13. Caddy ada di repositori standar keduanya.

1. Jalankan server MCP di localhost

Setiap server berjalan sebagai layanan systemd. Di sini kami memakai server referensi server-everything dua kali agar contohnya lengkap; ganti dengan milik Anda. Butuh Node.js 20 atau lebih baru:

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

Salinan kedua memakai port 3102. Lalu:

systemctl daemon-reload
systemctl enable --now mcp-everything-1 mcp-everything-2

Satu hal yang kami lihat saat pengujian: server ini mendengarkan di semua antarmuka, bukan hanya 127.0.0.1, dan banyak server lain juga begitu. Langkah 3 menangani hal itu.

2. Caddy: periksa token dulu, lalu rutekan per path

Buat token dan berikan ke Caddy lewat file environment:

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-nya:

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 mengambil sertifikat untuk mcp.example.com sendiri.

Blok route bukan hiasan. Versi pertama kami menaruh pemeriksaan token dan blok handle_path berdampingan di tingkat atas, dan permintaan tanpa token langsung lolos dengan 200. Caddy mengurutkan direktif menurut urutannya sendiri, dan handle_path dijalankan sebelum respond. Di dalam route, direktif dijalankan sesuai urutan tulisan. Jika Anda mengadaptasi konfigurasi ini, uji kasus „tanpa token“ lebih dulu.

handle_path membuang prefiks, jadi /everything/mcp sampai ke server sebagai /mcp. flush_interval -1 meneruskan event streaming begitu tiba.

3. Tutup semuanya kecuali SSH dan HTTPS

apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable

Setelah itu, dalam pengujian kami port 3101 dan 3102 tidak bisa dijangkau dari luar server, sedangkan 22 dan 443 menjawab. Port 80 tetap terbuka hanya agar Caddy bisa mengambil dan memperbarui sertifikat serta mengalihkan ke HTTPS.

4. Periksa

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"

Permintaan pertama harus mencetak 401 dan yang kedua 200. Dalam uji kami, token yang salah dan token yang terpotong sama-sama mendapat 401, dan path yang tidak dikenal mendapat 404 bahkan dengan token yang benar.

Lalu dari klien, misalnya Claude Code:

claude mcp add --transport http everything https://mcp.example.com/everything/mcp \
  --header "Authorization: Bearer $TOKEN"
claude mcp list

Kedua server menampilkan ✔ Connected. Klien yang sama dengan token salah menampilkan ✘ Failed to connect. Lebih lanjut tentang pengaturan klien di Hubungkan EQVPS MCP ke Claude Code dan Codex.

Menambah server berikutnya

Jalankan di port localhost baru, tambahkan satu blok handle_path, muat ulang Caddy. Konfigurasi siapa pun tidak berubah, kecuali URL baru di bawah host yang sama dengan token yang sama.

Batasan yang perlu diketahui

  • Satu token untuk semua itu sederhana dan menggantinya cukup satu file, tetapi Anda tidak bisa mencabut akses satu orang tanpa menggantinya untuk semua. Untuk akses per pengguna, pasang proxy autentikasi di depan atau beri setiap orang route dan token sendiri.
  • Gateway tidak membuat server aman. Server MCP yang menjalankan perintah shell tetap merupakan shell bagi siapa pun yang memegang token. Jauhkan token dari konfigurasi bersama dan git.
  • Tentukan ukuran berdasarkan server, bukan gateway. Caddy sendiri hanya memakai beberapa puluh megabyte. Jika salah satu server menjalankan model lokal atau mengindeks repositori besar, server itulah yang menentukan paket, bukan proxy.

Hanya punya satu server MCP? VPS untuk server MCP membahas kasus yang lebih sederhana itu. Ingin agen Anda juga mengurus server tempat mereka tinggal? Server MCP EQVPS bisa melakukannya (atau REST API jika Anda lebih suka skrip), dan paket dengan IPv4 sendiri mulai $8 per bulan.

Pertanyaan umum

Kenapa server MCP perlu ditaruh di balik gateway?

Satu alamat dan satu token per tim alih-alih satu per server, TLS di satu tempat, dan server itu sendiri hanya mendengarkan di localhost. Menambah server keempat cukup dengan mengubah tiga baris di gateway, bukan rahasia baru untuk semua orang.

Paket apa yang saya perlukan?

Paket dengan IPv4 sendiri, karena klien harus menjangkau port 80 dan 443. Nano-IP ($8/bln, 2 vCPU, 1 GB) cukup untuk gateway dan beberapa server MCP ringan. Jika ada server yang mengerjakan tugas berat sendiri, sesuaikan ukurannya dengan server itu.

Apakah streaming jalan lewat Caddy?

Ya. Streamable HTTP memakai permintaan POST biasa plus server-sent events. Atur flush_interval -1 di reverse proxy agar event langsung diteruskan, tidak ditahan di buffer.

Apakah satu token bersama cukup aman?

Untuk tim kecil, ini awal yang masuk akal: token tidak pernah keluar dari HTTPS, dan Anda menggantinya dengan mengubah satu file. Saat butuh akses per orang atau pencabutan, pasang lapisan autentikasi sungguhan di depannya. Token bersama adalah batas bawah, bukan batas atas.

Bisakah ini dijalankan di paket NAT?

Tidak sebagai gateway publik. Paket NAT hanya punya satu port yang diteruskan untuk SSH dan tidak ada yang masuk selain itu, jadi klien tidak bisa menjangkau 443. Ini hanya berjalan jika semua klien ada di server yang sama.

Komentar

Belum ada komentar. Jadilah yang pertama.

Tinggalkan komentar

Komentar dimoderasi sebelum muncul.