MCP Server Hosting
How to host MCP servers behind TSLink with zero-trust identity verification, encryption, and access control
Why MCP Servers Need Zero-Trust
The Model Context Protocol connects AI assistants to external tools, databases, and APIs. MCP servers typically run on localhost, which is secure for single-machine use. The security challenge arises when you need remote access -- accessing your MCP servers from another device, sharing them with team members, or connecting them to AI workflows running on different machines.
Remote access requires a reachable transport and the right authorization. A public HTTP endpoint can be appropriate with application-level controls; a private tailnet endpoint is useful when every client can join the network. This guide uses an already-running HTTP MCP server and preserves its application authorization.
For a detailed analysis of MCP security risks and why network-layer security matters, see Why Your MCP Server Should Not Be on the Public Internet.
TSLink provides an alternative: host MCP servers on your private Tailscale network with per-service tailnet identity, WireGuard transport, and optional --allow access control for proxy HTTP services. No public exposure by default. No DNS records created by TSLink; tailnet connectivity still depends on Tailscale control-plane and DERP behavior.
This page covers reaching a third-party MCP server across your tailnet. TSLink also ships its own MCP server, so an agent can drive TSLink directly -- that is TSLink as an MCP Server.
Quick Setup
First confirm the backend already serves MCP over HTTP and note its actual endpoint path, such as http://localhost:8080/mcp. TSLink proxies that HTTP transport; it does not convert a stdio server into HTTP. Choose any required --allow restriction before registering the service:
tslink add mcp-tavily --proxy localhost:8080 --jsonDefault add ensures the background gateway is running. Inspect its result: if data.status is needs_login, a human must open data.auth_url and complete enrollment and any tailnet approval. Then poll the existing gateway for the exact service origin:
tslink url mcp-tavily --waitInspect tslink status --json as well; daemon liveness alone is not service readiness. Configure the client with the exact runtime origin plus the backend's actual MCP path: for a backend at /mcp, append /mcp to that origin. Keep another path if your backend uses one. The MCP transport specification defines a complete endpoint path, not merely a host.
Use an HTTP MCP client whose connection originates on a device inside the tailnet, such as a local CLI client with HTTP transport support. Claude's account-level remote connectors originate from Anthropic's cloud, including when configured in Desktop, and cannot reach this private endpoint merely because your laptop can. Desktop's local MCP configuration is a separate mechanism; local stdio requires a client that can launch the process and is not a claude.ai fallback. See Claude's network requirements.
Do not run serve after default add. A deliberate manual setup uses --no-daemon-install and the manual gateway prerequisites. The tailnet leg uses WireGuard, and proxy HTTP can carry best-effort WhoIs identity headers; keep the backend's MCP authorization.
Access Control
Restrict which users or groups can reach a specific MCP server with the --allow flag:
tslink add mcp-tavily --proxy localhost:8080 --allow user@example.comOnly the specified user can reach this MCP server. All other requests receive a 403 Forbidden response.
For team scenarios, use Tailscale tags:
tslink add mcp-tavily --proxy localhost:8080 --allow tag:ai-teamTags are defined in your Tailscale ACL policy. This lets you manage MCP server access through your existing Tailscale identity infrastructure without additional configuration in TSLink.
For proxy HTTP services, TSLink resolves Tailscale identity via WhoIs and can enforce --allow on each request. There are no TSLink session tokens, cookies, or credentials stored in the MCP client configuration. Keep MCP application-layer authorization where the MCP server needs tool-level policy.
Multiple MCP Servers
Each MCP server gets its own TSLink service with its own identity and access controls:
# Web search tool -- available to the whole team
tslink add mcp-tavily --proxy localhost:8080 --allow tag:ai-team
# GitHub integration -- restricted to developers
tslink add mcp-github --proxy localhost:8081 --allow tag:developers
# Database access -- restricted to data team
tslink add mcp-postgres --proxy localhost:8082 --allow tag:data-team
# Internal knowledge base -- restricted to a specific user
tslink add mcp-docs --proxy localhost:8083 --allow admin@example.comEach MCP server runs on its own WireGuard node with a distinct tailnet identity — network segmentation, not host isolation. Compromising one server does not by itself grant tailnet access to another server's node, though a compromised process can still reach other resources on the same host through other paths. This per-service microsegmentation reflects a core principle of NIST SP 800-207 zero-trust architecture (Tenet 1: all computing services are treated as independent resources).
Each add ensures the gateway is running. Complete any needs_login / auth_url handoff, then poll tslink url <name> --wait for each exact runtime origin and preserve that backend's MCP path in the client endpoint. Do not start another serve.
Add or remove MCP servers while the gateway is running. TSLink watches the service registry and applies changes through hot reload without interrupting other active services.
Monitoring: Access Logs for MCP Audit
Use tslink access log --app my-mcp --since 24h for bounded local HTTP access history with WhoIs-attested identity when available. It records gateway access, not third-party MCP tool semantics. Identity can be absent; records can drop or have gaps. See access history. Backend identity propagation remains separate. tslink logs reads daemon diagnostics on stderr, not this complete query surface.
Inspect recent daemon logs with the shipped log viewer:
tslink logs --source err --last 100
tslink logs --level errorDaemon log files are stored under ~/.config/tslink/logs/ (tslink.out.log and tslink.err.log). TSLink does not ship a serve logging format switch; use tslink logs or read the log files directly for aggregation.
What TSLink Provides vs. What It Does Not
TSLink secures MCP servers at the network and transport layer:
| TSLink provides | TSLink does not provide |
|---|---|
| WireGuard-encrypted tailnet transport (backend hop may be plaintext) | Application-layer authorization within the MCP protocol |
Best-effort identity checks (WhoIs, cached 60s), enforced with --allow | Input validation for MCP tool calls |
| Per-service tailnet identity (network segmentation, not host isolation) | Content filtering for MCP responses |
Access control via --allow | Rate limiting per MCP tool (roadmap) |
| Bounded local access history | MCP-specific protocol inspection |
TSLink is complementary to application-layer MCP security features such as OAuth authorization. Network-layer security (encryption, identity, segmentation) and application-layer security (authorization, input validation) are both necessary. Neither alone is sufficient.