How to Build a Zero-Trust Tailscale MCP
This architecture solves the "Cursor Cloud Agent Bootstrap Problem". Cursor's UI requires MCP servers to be publicly reachable to validate the URL. However, we cannot expose production AWS/Sentry tools to the public internet.
The Solution: One local server, two Tailscale proxy ports, and Header Validation.
1. The Infrastructure Setup
Run your Node.js MCP server bound strictly to localhost. Then expose it twice via Tailscale:
# 1. Public Funnel (Port 443) - For Cursor UI validation
tailscale funnel --bg --https=443 http://localhost:3000
# 2. Private Serve (Port 8443) - For actual Agent/Dev execution
tailscale serve --bg --https=8443 http://localhost:3000
2. The Authentication Logic (Copy to Agent)
When Tailscale proxies requests to your server, it injects headers. Public traffic gets nothing. Private human traffic gets Tailscale-User-Login. Private bot traffic (tagged devices) doesn't get login headers, but the X-Forwarded-Host proves they hit the secure port.
// In your MCP server's request handler:
const isTrustedSession = (req) => {
// 1. Did a human on the Tailnet make this request?
const tailnetUser = req.headers['tailscale-user-login'];
// 2. Did a tagged bot on the Tailnet hit the protected 8443 port?
// Because we bind to 127.0.0.1, X-Forwarded-Host cannot be spoofed by the public.
const isSecurePort = req.headers['x-forwarded-host']?.includes(':8443');
return !!tailnetUser || !!isSecurePort;
};
// Tool Registration Logic:
if (isTrustedSession(req)) {
return [sentryTool, awsTool, newRelicTool]; // Return full arsenal
} else {
return []; // Return 0 tools. Cursor UI validates successfully but can't do damage.
}
3. The Cloud Agent VM Setup
Since Cloud Agent VMs don't have /dev/net/tun, they cannot run standard Tailscale. Boot them using userspace networking and an auth key.
# Cloud Agent Startup Script:
tailscaled --tun=userspace-networking --socks5-server=localhost:1055 &
tailscale up --authkey=$TS_AUTHKEY --hostname=cursor-cloud-vm --accept-routes
# Set proxies so the agent routes traffic into the tailnet
export ALL_PROXY=socks5h://localhost:1055/