Máy chủ MCP tự host ngày một nhiều. Đầu tiên có một cái cho tài liệu nội bộ, rồi một cái cho cơ sở dữ liệu, rồi ai đó viết một cái nhỏ cho pipeline triển khai. Mỗi cái có cổng, URL và bí mật riêng, và mọi người trong nhóm dán cả ba vào cấu hình client của mình. Đổi một token nghĩa là phải nhắn cho tất cả.
Gateway biến tất cả thành một địa chỉ và một token. Client kết nối tới https://mcp.example.com/docs/mcp, .../db/mcp và tương tự. Gateway kiểm tra token, gửi mỗi đường dẫn tới đúng máy chủ trên localhost, và bản thân các máy chủ không bao giờ lộ ra ngoài. Cần rất ít: một VPS nhỏ có IPv4 riêng và Caddy.
Chúng tôi dựng cấu hình dưới đây trên một máy chủ thử nghiệm và kiểm tra bằng client thật trước khi viết.
Bạn cần gì
- Một VPS có IPv4 công khai để client tới được 443. Nano-IP là đủ, trừ khi máy chủ MCP của bạn tự làm việc nặng.
- Một tên miền có bản ghi A trỏ tới máy chủ, ở đây là
mcp.example.com. - Ubuntu 24.04 hoặc Debian 12/13. Caddy có sẵn trong kho chuẩn của cả hai.
1. Chạy các máy chủ MCP trên localhost
Mỗi máy chủ chạy như một dịch vụ systemd. Ở đây chúng tôi dùng máy chủ mẫu server-everything hai lần để ví dụ tự đủ; hãy thay bằng máy chủ của bạn. Nó cần Node.js 20 trở lên:
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
Bản thứ hai dùng cổng 3102. Sau đó:
systemctl daemon-reload
systemctl enable --now mcp-everything-1 mcp-everything-2
Một điều chúng tôi nhận thấy khi thử: máy chủ này lắng nghe trên mọi giao diện, không chỉ 127.0.0.1, và nhiều máy chủ khác cũng vậy. Bước 3 xử lý việc đó.
2. Caddy: kiểm tra token trước, rồi định tuyến theo đường dẫn
Tạo token và đưa cho Caddy qua một tệp môi trường:
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 tự lấy chứng chỉ cho mcp.example.com.
Khối route không phải để trang trí. Phiên bản đầu của chúng tôi đặt phần kiểm tra token và các khối handle_path cạnh nhau ở cấp cao nhất, và yêu cầu không có token đi thẳng qua với 200. Caddy sắp xếp các chỉ thị theo thứ tự riêng, và handle_path chạy trước respond. Bên trong route, chúng chạy theo thứ tự đã viết. Nếu bạn chỉnh cấu hình này, hãy thử trường hợp “không có token” trước tiên.
handle_path bỏ tiền tố, nên /everything/mcp tới máy chủ dưới dạng /mcp. flush_interval -1 chuyển các sự kiện streaming ngay khi chúng tới.
3. Đóng mọi thứ trừ SSH và HTTPS
apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
Sau đó, trong thử nghiệm của chúng tôi, cổng 3101 và 3102 không truy cập được từ bên ngoài máy chủ, còn 22 và 443 vẫn trả lời. Cổng 80 chỉ mở để Caddy lấy và gia hạn chứng chỉ, cũng như chuyển hướng sang HTTPS.
4. Kiểm tra
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"
Yêu cầu đầu phải in ra 401 và yêu cầu thứ hai in ra 200. Trong lần chạy của chúng tôi, token sai và token bị cắt đều nhận 401, còn đường dẫn lạ nhận 404 ngay cả với token đúng.
Rồi từ một client, ví dụ Claude Code:
claude mcp add --transport http everything https://mcp.example.com/everything/mcp \
--header "Authorization: Bearer $TOKEN"
claude mcp list
Cả hai máy chủ đều hiện ✔ Connected. Cùng client đó với token sai hiện ✘ Failed to connect. Thêm về cài đặt client trong Kết nối EQVPS MCP với Claude Code và Codex.
Thêm máy chủ tiếp theo
Khởi động nó trên một cổng localhost mới, thêm một khối handle_path, tải lại Caddy. Cấu hình của không ai thay đổi, ngoài một URL mới dưới cùng host với cùng token.
Những giới hạn nên biết
- Một token cho mọi người đơn giản và đổi chỉ cần một tệp, nhưng bạn không thể thu hồi quyền của một người mà không đổi cho tất cả. Để có quyền theo từng người dùng, đặt một proxy xác thực phía trước hoặc cấp cho mỗi người route và token riêng.
- Gateway không làm máy chủ an toàn. Một máy chủ MCP chạy lệnh shell vẫn là một shell với bất kỳ ai giữ token. Đừng để token trong cấu hình dùng chung hay trong git.
- Chọn cấu hình theo máy chủ, không theo gateway. Bản thân Caddy chỉ dùng vài chục megabyte. Nếu một máy chủ chạy mô hình cục bộ hoặc lập chỉ mục một kho mã lớn, chính nó quyết định gói, không phải proxy.
Chỉ có một máy chủ MCP? VPS cho máy chủ MCP nói về trường hợp đơn giản hơn đó. Muốn tác tử của bạn quản lý luôn máy chủ mà chúng đang sống? Máy chủ MCP của EQVPS làm được (hoặc REST API nếu bạn thích viết script), và các gói có IPv4 riêng bắt đầu từ $8 mỗi tháng.
Bình luận
Chưa có bình luận nào. Hãy là người đầu tiên.