An MCP server that gives AI agents structured access to Tailscale tailnets. Built for the Model Context Protocol with security visibility and token efficiency as first-class design goals.
Tailscale exposes a clean REST API (v2) for managing devices, ACL policies, DNS, auth keys, users, and audit logs. This MCP wraps it with the guardrails that automated agents need:
- Security-first tool set — 19 tools focused on what network security agents actually need: device inventory, key expiry auditing, ACL review + apply, route approval, DNS hygiene. Not a thin wrapper around every endpoint.
- Token-efficient output — compact pipe-delimited format. A 20-device tailnet in ~40 tokens per device. Devices flagged with
KEY_EXPIRY_OFF,KEY_EXPIRED,UPDATE_AVAILABLE,UNAUTHORIZED,OFFLINEat a glance. - Write-gated mutations — device authorization, tagging, key management, route approval, and ACL apply require explicit opt-in via
TAILSCALE_WRITE_ENABLED=true. Destructive operations (delete device, revoke key) additionally require per-callconfirm=true; ACL apply defaults to optimistic-concurrency (If-Match) so it won't silently clobber a concurrent edit. - SecOps visibility — ACL policy summary shows groups, rules, SSH rules, and tag owners. Audit log shows who changed what. Key listing flags reusable keys and expiry status.
| tailscale-blade-mcp | HexSleeves/tailscale-mcp | jaxxstorm/tailscale-mcp | |
|---|---|---|---|
| Focus | Monitoring + security (19 tools) | Management (~15 tools) | Read-only (~5 tools) |
| Design for | LLM agents (token-efficient) | Claude Code | General MCP |
| Output | Pipe-delimited, compact | Full JSON | Full JSON |
| Write safety | Dual-gated (env + confirm) | Direct writes | Read-only |
| Audit log | Yes | No | No |
| ACL summary | Parsed groups/rules/SSH/tags | Raw JSON | Raw JSON |
| Key hygiene | Flags reusable, expiry status | Basic listing | No |
| Marketplace | Stallari certified | Standalone | Standalone |
Published on PyPI — run it without a checkout via uvx:
# Configure
export TAILSCALE_API_KEY="tskey-api-..."
# Run the latest published release (no install step)
uvx tailscale-blade-mcpOr install it as a tool / into an environment:
uv tool install tailscale-blade-mcp # or: pip install tailscale-blade-mcp
tailscale-blade-mcpFrom a local checkout (development):
uv pip install -e .
tailscale-blade-mcp| Tool | Purpose | Token cost |
|---|---|---|
ts_info |
Health check — device counts, online/offline, key expiry warnings, settings, write gate | ~100 |
| Tool | Purpose | Token cost |
|---|---|---|
ts_devices |
All devices — hostname, OS, IP, online/offline, key expiry, tags, updates | ~40/device |
ts_device |
Full detail — addresses, client version, key status, tags, user | ~120 |
ts_device_routes |
Routes — advertised subnets, approved/unapproved status | ~30/route |
| Tool | Purpose | Token cost |
|---|---|---|
ts_dns |
DNS — nameservers, MagicDNS, search paths, split DNS | ~50 |
ts_acl |
ACL policy — groups, rules, SSH rules, tag owners | ~30/rule |
ts_acl_validate |
Validate a policy without applying it (write-gated) | ~20 |
ts_acl_set |
Apply a full ACL policy — validates first, optimistic-concurrency If-Match guard (write-gated; needs policy-file write scope) |
~30 |
| Tool | Purpose | Token cost |
|---|---|---|
ts_keys |
Auth keys — ID, reusable/ephemeral/preauth flags, tags, expiry | ~25/key |
ts_users |
Users — name, role, status, device count, online/last seen | ~25/user |
ts_webhooks |
Webhooks — endpoint URL, event subscriptions | ~25/webhook |
| Tool | Purpose | Token cost |
|---|---|---|
ts_audit_log |
Configuration changes — who, what, when | ~25/entry |
ACL apply (
ts_acl_set) is also write-gated; it's listed under Network with the other ACL tools.
| Tool | Gate | Purpose |
|---|---|---|
ts_authorize_device |
write | Authorize or deauthorize a device |
ts_set_tags |
write | Set ACL tags on a device |
ts_expire_device |
write | Force key expiry — device must re-authenticate |
ts_approve_routes |
write | Approve advertised subnet routes |
ts_create_key |
write | Create an auth key (reusable/ephemeral/preauth) |
ts_delete_key |
write + confirm | Revoke an auth key permanently |
ts_delete_device |
write + confirm | Remove a device from the tailnet |
macbook | os=mac | ip=100.100.1.1 | online | expires=2026-07-11 | id=n1234567890
nas | os=linux | ip=100.100.1.2 | online | KEY_EXPIRY_OFF | UPDATE_AVAILABLE | tags=server,infra | id=n9876543210
phone | os=iOS | ip=100.100.1.3 | OFFLINE | last=2d ago | expires=2026-05-01 | id=n5555555555
Tailscale supports two auth methods:
| Method | Token prefix | Best for |
|---|---|---|
| API access token | tskey-api- |
Personal use, quick setup |
| OAuth client | Bearer token from client_credentials flow | Automation, scoped permissions |
Both are passed via TAILSCALE_API_KEY. For OAuth, obtain a Bearer token first and pass that.
ACL write scope. Read tools work with any valid token. Applying an ACL (ts_acl_set) additionally requires the token to carry policy-file write scope — an OAuth client with the acl scope, or an API access token from an account that can edit the tailnet policy file. A read-only token will validate fine but return a 403 on apply, with a message pointing at the scope requirement.
| Layer | Mechanism |
|---|---|
| Write gate | TAILSCALE_WRITE_ENABLED=true required for any mutation |
| Destructive confirm | ts_delete_key and ts_delete_device require confirm=true |
| ACL validate-before-apply | ts_acl_set always validates the policy and refuses to apply on failure |
| ACL optimistic concurrency | ts_acl_set sends If-Match (the current ETag) by default; a concurrent edit fails with a re-fetch message rather than clobbering. Bypass only via allow_overwrite_concurrent=true |
| Credential scrubbing | API keys, Bearer tokens, Authorization headers stripped from errors |
| Bearer auth | Optional TAILSCALE_MCP_API_TOKEN for HTTP transport |
| Tailnet auto-detect | Uses - shorthand by default — no tailnet name in config |
From the published package (recommended — pin a version with tailscale-blade-mcp@0.7.0):
{
"mcpServers": {
"tailscale": {
"type": "stdio",
"command": "uvx",
"args": ["tailscale-blade-mcp"],
"env": {
"TAILSCALE_API_KEY": "tskey-api-...",
"TAILSCALE_WRITE_ENABLED": "false"
}
}
}
}From a local checkout (development):
{
"mcpServers": {
"tailscale": {
"type": "stdio",
"command": "uv",
"args": ["--directory", "~/src/tailscale-blade-mcp", "run", "tailscale-blade-mcp"],
"env": {
"TAILSCALE_API_KEY": "tskey-api-...",
"TAILSCALE_WRITE_ENABLED": "false"
}
}
}
}- Key expiry approaching —
ts_devicesflagsKEY_EXPIRY_OFFand expired keys for proactive rotation - Unauthorized devices —
ts_devicesflagsUNAUTHORIZEDfor approval workflows - Route approval —
ts_device_routesshows unapproved subnets for security review - ACL changes —
ts_audit_logtracks policy updates for compliance auditing - Stale devices —
ts_devicesshowsOFFLINEwith last-seen time for cleanup workflows
make install-dev # Install with dev + test dependencies
make test # Unit tests (mocked, no API access needed)
make check # Lint + format + type-check
make run # Start MCP server (stdio)src/tailscale_blade_mcp/
├── server.py — FastMCP server, 19 @mcp.tool decorators
├── client.py — TailscaleClient wrapping httpx async, credential scrubbing
├── formatters.py — Token-efficient output (pipe-delimited, null omission, human units)
├── models.py — TailscaleConfig, write gate, constants
└── auth.py — Bearer token middleware for HTTP transport
MIT