—
type: article
title: \”Tailscale + MCP: Sovereign Remote Access\”
date: 2026-08-17
status: draft
task: t_fa48131f
—
# Tailscale + MCP: Sovereign Remote Access
When you run AI agents that need to touch infrastructure — deploy containers, query databases, restart services — the network layer becomes the bottleneck. VPNs add latency, SSH tunnels break, and exposing ports to the internet is a non-starter for sovereign stacks.
Tailscale solved the connectivity problem years ago: a mesh VPN built on WireGuard that just works. But until recently, there was no standard way for an AI agent to *use* that connectivity programmatically. Enter the Model Context Protocol (MCP).
This article walks through the current landscape of Tailscale MCP servers, what they actually enable, and the one gap you still have to handle yourself: certificate provisioning.
—
## Why MCP Changes the Game
MCP gives LLMs a standardized way to call tools. Instead of hand-crafting API wrappers for every service, you expose a server that speaks MCP — the model discovers available tools at runtime and invokes them with structured arguments.
For Tailscale, this means an agent can:
– List all nodes in your tailnet and their status
– Read and update ACLs (the firewall rules governing traffic flow)
– Manage DNS records and MagicDNS entries
– Create and revoke auth keys for new devices
– Query tailnet settings and audit logs
All without you writing a single line of glue code.
—
## The Landscape: Three Production-Grade Servers
After evaluating seven active projects, three stand out for production use.
### 1. YawLabs @yawlabs/tailscale-mcp (Primary Recommendation)
**Best API coverage, best test discipline.**
– 89 Admin API tools + 4 local CLI diagnostics (`tailscale_local_status`, `tailscale_ping`, `tailscale_netcheck`, `tailscale_local_version`)
– 700+ unit tests; every tool verified against the live API
– Tool subsetting via env: `TAILSCALE_PROFILE=minimal|core|full` (20/47/89 tools)
– Read-only mode: `TAILSCALE_READONLY=1` drops all mutating tools
– Local CLI group requires `TAILSCALE_LOCAL_CLI=1` and the `tailscale` binary in PATH
“`yaml
mcp_servers:
tailscale:
command: npx
args: [\”-y\”, \”@yawlabs/tailscale-mcp@latest\”]
env:
TAILSCALE_API_KEY: \”tskey-api-XXXXXXXX\”
# TAILSCALE_PROFILE: \”core\” # 47 tools; omit = full 89
# TAILSCALE_READONLY: \”1\” # drop all mutating tools
# TAILSCALE_LOCAL_CLI: \”1\” # add local status/ping/netcheck diag
“`
Restart Hermes and tools appear as `mcp_tailscale_*` — e.g., `mcp_tailscale_list_devices`, `mcp_tailscale_get_acl`, `mcp_tailscale_set_dns_configuration`.
### 2. jaxxstorm/tailscale-mcp (Native Tailnet Endpoint)
**Tailnet-native serving with per-user OAuth Grants.**
– Written by Lee Briggs (Tailscale engineer, featured on the Tailscale blog)
– Embeds **tsnet** — the server becomes its own tailnet node, reachable at `http://ts-mcp.
– Full OpenAPI parity: 90/90 operations implemented
– **OAuth Grants** authorization (`jaxxstorm.com/cap/mcp`) for fine-grained per-user tool/resource ACLs — stronger than a shared API key
– Homebrew: `brew install jaxxstorm/tap/tailscale-mcp`
“`bash
brew install jaxxstorm/tap/tailscale-mcp
export TAILSCALE_OAUTH_TOKEN='{\”type\”:\”oauth\”,\”clientId\”:\”kXXX\”,\”clientSecret\”:\”tskey-client-XXX\”,\”scopes\”:[\”all\”]}’
export TAILSCALE_TAILNET=\”your-tailnet.ts.net\”
export TS_ADVERTISE_TAGS=\”tag:mcp-server\”
./ts-mcp # serves http://ts-mcp.
“`
Hermes HTTP transport:
“`yaml
mcp_servers:
tailscale:
url: \”http://ts-mcp.
headers:
Authorization: \”Bearer
“`
### 3. HexSleeves @hexsleeves/tailscale-mcp-server (Docker-First, Risk-Gated)
**Most popular (116★), Docker-ready, risk-gated tool access.**
– TypeScript + Zod, npm + Docker Hub (`hexsleeves/tailscale-mcp-server`)
– Tool surface: device lifecycle, network ops, ACL read/validate/update, DNS, auth keys, policy file, network lock
– **Risk gating**: `TAILSCALE_ALLOWED_TOOL_RISK=read|write|admin` — defaults to read-only
– HTTP transport with bearer token + `GET /health`; expose via `tailscale serve` (never Funnel)
“`yaml
mcp_servers:
tailscale:
command: npx
args: [\”-y\”, \”@hexsleeves/tailscale-mcp-server\”]
env:
TAILSCALE_API_KEY: \”tskey-api-XXXXXXXX\”
TAILSCALE_TAILNET: \”-\”
TAILSCALE_ALLOWED_TOOL_RISK: \”write\”
“`
—
## The Critical Gap: Certificate Provisioning
Here’s what no MCP server can do: **spawn tailnet TLS certificates.**
The Tailscale Admin API has no certificate-provisioning endpoint. Verified against the current OpenAPI v2 spec (58 paths) — the only cert-related entry is the `httpsCertificates` *setting* on the tailnet settings endpoint (enable/disable). No POST/GET to issue certs.
Certs are issued **on-node** by `tailscale cert
**Workable paths:**
1. **Zero-code (recommended)**: Hermes terminal tool → `docker exec
2. **Thin wrapper (only if certs must be first-class MCP)**: A ~50-line FastMCP/Python server exposing `tailscale_cert(name)` that shells into the Docker tailscale container. Build later if the CLI path is inconvenient.
—
## Security Posture
– API key/OAuth client must have **Owner/Admin** role on the tailnet. Prefer OAuth client credentials with scoped permissions over long-lived `tskey-api-*`.
– Start read-only (`TAILSCALE_ALLOWED_TOOL_RISK=read` or `TAILSCALE_READONLY=1` / `TAILSCALE_PROFILE=core`), escalate writes deliberately.
– HTTP transport: bearer token ≥32 chars, bind 127.0.0.1, expose via `tailscale serve` (never Funnel).
– jaxxstorm ts-mcp adds **OAuth Grants** — strongest option if multiple users will query via the tailnet endpoint.
—
## Hermes Integration (Verified on This Machine)
Hermes has a **native MCP client** — add servers under `mcp_servers` in `~/.hermes/config.yaml`, restart, and tools appear as `mcp_{server}_{tool}`. The config already has a working example (`novamira-visual-localhost`), so the pattern is proven.
Key mechanics:
– stdio transport: `command` + `args` + `env`. HTTP: `url` + `headers`.
– **Env filtering**: Hermes does NOT pass the full shell env to stdio MCP subprocesses — the API key MUST be declared explicitly in the `env:` block.
– Prereqs: `mcp` Python package (install into the Hermes venv: `pip install mcp`), Node/npx (v22.22.3 at `~/.local/bin`).
– No hot-reload: adding a server requires restarting Hermes.
– Tool names: hyphens/dots → underscores; server `tailscale`, tool `list_devices` → `mcp_tailscale_list_devices`.
—
## Recommendation
**Adopt — do not build from scratch.** There is no gap an MCP server must fill except certs, and that gap is in the Tailscale API itself.
| Need | Tool |
|——|——|
| Nodes, ACLs, DNS, keys, tailnet settings, status (Admin API) | **YawLabs @yawlabs/tailscale-mcp** — primary |
| Tailnet-native MCP endpoint w/ MagicDNS + per-user grants | **jaxxstorm ts-mcp** — secondary/native |
| Docker-first, risk-gated, stdio+HTTP | **HexSleeves @hexsleeves/tailscale-mcp-server** — solid fallback |
| Spawn tailnet certs | **Hermes terminal** → `docker exec … tailscale cert
—
## Next Steps
1. Install `mcp` into the Hermes venv: `pip install mcp`
2. Generate an API key (Admin Console → Settings → Keys) or OAuth client (Settings → OAuth clients) with Owner/Admin role
3. Add the YawLabs server to `~/.hermes/config.yaml` (see config block above)
4. Restart Hermes → verify tools appear with `hermes tools list | grep tailscale`
5. For certs: enable HTTPS in Admin Console → DNS → HTTPS Certificates, then `docker exec
—
*Grounded in: `tailscale-mcp-research-2026-08-05.md` (research brief t_7b6ae051, recommendation: adopt).*



