loading…
Search for a command to run...
loading…
Tailscale MCP server for managing your tailnet from AI assistants
Tailscale MCP server for managing your tailnet from AI assistants
npm version License: MIT GitHub stars CI Release
Ask your agent questions about your tailnet and have it act on the answers. 89 tools + 4 resources covering the full Tailscale v2 API. Backed by 700+ unit tests and an opt-in live-tailnet integration suite.
Built and maintained by Yaw Labs.
One click adds this to your mcp.hosting account so it syncs to every MCP client you use. Or install manually below.
You could curl the Tailscale API. The point isn't replacing curl — it's letting an agent compose multi-endpoint workflows in one turn without writing a script:
lastSeen, filters by keyExpiryDisabled, returns a table. Three endpoints, one question.tag:mobile reach tag:dashboard but not tag:db, preserving my comments" — reads the current HuJSON, proposes a minimal diff, validates it against the API, returns the diff for you to apply.devices:read and dns" — creates a trust credential via tailscale_create_key with keyType=client, returns the credentials once (save them immediately).A curl can do each step. The agent composes them. That's where the lift is, and that's what the tool surface is designed for — every read endpoint is first-class so the agent can synthesize, and every write endpoint is tagged destructiveHint or idempotentHint so your MCP client can gate mutations the way you configured it.
If all you need is one endpoint in a CI job, use curl — we even have a CLI subcommand for the common ACL-from-git case. The MCP is for the interactive, exploratory, "I don't know what I need yet" work.
tailscale CLI?Reasonable question. Both have their place. Where this MCP is better:
tailscale CLI is scoped to the node it runs on. Admin concerns — ACLs, users, invites, webhooks, log streaming, posture integrations, auth keys, OAuth clients, and federated identities — live in the v2 HTTP API. You'd be shelling out to curl anyway.tailscale status --json | jq pipelines that break when the schema evolves.npx — users don't re-author their skill when Tailscale adds an endpoint.readOnlyHint / destructiveHint / idempotentHint so clients can skip confirmation on reads and require it on mutations. A skill that shells out to the CLI can't express that.RUN_INTEGRATION_TESTS=1 + a tailnet API key) for shape-drift detection. Most skills are short markdown prompts without their own test layer — if the vendor changes output format, nothing catches it for you.If you already have a skill that covers your 10% of Tailscale workflows, great — keep it. The MCP is for the other 90%.
Fair critique from Reddit: a new repo claiming "actively maintained" with no visible tests is worth exactly zero trust. Here's what's actually verifiable:
node --test) covering every tool's input validation, API shape, and error handling. Run npm test to see them pass locally.release.yml, manual dispatch); skips gracefully when no test-tailnet secret is configured, so forks aren't blocked.Issues and PRs are triaged. File one if something is off — github.com/YawLabs/tailscale-mcp/issues.
1. Set your API key
Get an API key from Tailscale Admin Console > Settings > Keys and add it to your shell profile (~/.bashrc, ~/.zshrc, or Windows system environment variables):
export TAILSCALE_API_KEY="tskey-api-..."
2. Create .mcp.json in your project root
macOS / Linux / WSL:
{
"mcpServers": {
"tailscale": {
"command": "npx",
"args": ["-y", "@yawlabs/tailscale-mcp"]
}
}
}
Windows:
{
"mcpServers": {
"tailscale": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@yawlabs/tailscale-mcp"]
}
}
}
Why the extra step on Windows? On Windows,
npxis a.cmdfile, and Node 20+ refuses to spawn.cmdfiles directly. Wrapping withcmd /cis the standard workaround.
3. Restart and approve
Restart Claude Code (or your MCP client) and approve the Tailscale MCP server when prompted.
That's it. Now ask your agent:
"List my Tailscale devices that haven't been seen in the last 7 days"
"Summarize every ACL change in the audit log from yesterday"
"Draft an ACL rule that lets
tag:cireachtag:registryon port 5000 only"
89 tools is a lot. If you've already got a dozen MCP servers and your client is feeling heavy, trim what this one exposes. Three knobs, combinable:
TAILSCALE_PROFILE (preset, easiest){
"env": {
"TAILSCALE_API_KEY": "tskey-api-...",
"TAILSCALE_PROFILE": "core"
}
}
minimal (20 tools) — status, devices, audit. Observe the tailnet, read the audit log.core (47 tools) — adds acl, dns, keys, users. The day-to-day admin surface.full (89 tools, default) — everything. Same as omitting the env var.TAILSCALE_TOOLS (explicit group list){
"env": {
"TAILSCALE_API_KEY": "tskey-api-...",
"TAILSCALE_TOOLS": "devices,acl,dns,audit"
}
}
Comma-separated group names. Overrides TAILSCALE_PROFILE when both are set — use this when the presets aren't quite right.
Valid group names: status, devices, acl, dns, keys, users, tailnet, webhooks, posture, audit, invites, services, log-streaming.
TAILSCALE_READONLY (drop mutations){
"env": {
"TAILSCALE_API_KEY": "tskey-api-...",
"TAILSCALE_PROFILE": "core",
"TAILSCALE_READONLY": "1"
}
}
Set to 1 or true to drop every tool without readOnlyHint: true. Stacks with TAILSCALE_PROFILE or TAILSCALE_TOOLS as an intersection — combine for maximum minimalism.
The server logs the active filter to stderr on startup:
@yawlabs/tailscale-mcp v0.9.1 ready (20 tools, profile=minimal, readonly)
If you don't set any filter, startup prints a tip pointing you at the profiles.
If you run this server through mcp.hosting (via the @yawlabs/mcph local agent), the two filtering layers compose cleanly:
TAILSCALE_PROFILE / TAILSCALE_TOOLS / TAILSCALE_READONLY reduce the tool surface before mcph sees it. The unloaded tools aren't registered at all.mcp_connect_activate({ tools: [...] }) filters further for what appears in tools/list. Tools not in that list stay reachable via mcp_connect_dispatch, so you don't lose capability.Recommended pattern for mcph users: set TAILSCALE_PROFILE=core (or narrower) in your mcp.hosting server config, then let mcph handle per-conversation activation on top. The server stays lean by default, and mcp_connect_dispatch covers the long-tail tools for ad-hoc needs.
API key (simplest): Set TAILSCALE_API_KEY in your shell or MCP config.
OAuth (scoped access): For fine-grained permissions, set TAILSCALE_OAUTH_CLIENT_ID and TAILSCALE_OAUTH_CLIENT_SECRET instead. Create an OAuth client at Tailscale Admin Console > Settings > OAuth.
The server checks for an API key first, then falls back to OAuth. If neither is set, tools return a clear error telling you what to configure — the server still starts, so your MCP client doesn't loop restarting.
Tailnet: Uses your default tailnet automatically. Set TAILSCALE_TAILNET to specify one explicitly.
429 retry (built-in). API responses with HTTP 429 are retried up to 3 times, honoring the Retry-After header (both seconds-integer and HTTP-date forms). Falls back to exponential backoff with jitter, capped at 30s per wait. No env var needed — this is on by default. Workflows like "rotate every key older than 90 days" no longer fail mid-loop on Tailscale's per-tenant rate limits.
TAILSCALE_DEBUG=1 — log every HTTP method, URL, status, and elapsed time to stderr. Authorization headers are never logged. Use this when a tool returns an unexpected error and you want to see the actual request that went out. Example:
[tailscale-mcp] GET https://api.tailscale.com/api/v2/tailnet/-/devices
[tailscale-mcp] <- 200 (148ms)
TAILSCALE_MAX_CONCURRENT=N — cap in-flight API requests at N. Default is unlimited (no behavior change for users who don't opt in). Useful when an agent fans out aggressively against a tailnet that has stricter limits than the per-call retry can absorb.
TAILSCALE_REQUEST_BUDGET_MS=N — total wall-clock budget per request, including 429 retries and their sleeps. Default 90000 (90s). When the next retry's predicted wall time would exceed the budget, the call surfaces the 429 immediately instead of holding the line. Tune lower if your MCP client has a tighter outer timeout. 429s on non-idempotent methods (POST, PATCH) are never retried — those return immediately regardless of budget.
Friendlier error messages. JSON error bodies of the form {"message":"..."} or {"error":"..."} are unwrapped before display, so you see the prose explanation instead of raw JSON. 401s still get the full multi-line auth-error formatter (with the Windows env-var hint when applicable).
MCP Resources expose read-only data clients can browse without a tool call.
| Resource | URI | Description |
|---|---|---|
| Tailnet Status | tailscale://tailnet/status |
Device count and tailnet settings |
| Devices | tailscale://tailnet/devices |
All devices with status and IPs |
| ACL Policy | tailscale://tailnet/acl |
Full ACL policy (HuJSON preserved) |
| DNS Config | tailscale://tailnet/dns |
Nameservers, search paths, split DNS, MagicDNS |
| Tool | Description |
|---|---|
tailscale_status |
Verify API connection, see tailnet info and device count |
| Tool | Description |
|---|---|
tailscale_list_devices |
List all devices with status, IPs, OS, and last seen |
tailscale_get_device |
Get detailed info for a specific device |
tailscale_authorize_device |
Authorize a pending device |
tailscale_deauthorize_device |
Deauthorize a device |
tailscale_set_devices_authorized |
Authorize/deauthorize many devices in one call (parallel, per-id error reporting) |
tailscale_delete_device |
Remove a device from the tailnet |
tailscale_rename_device |
Rename a device |
tailscale_expire_device |
Expire a device's key, forcing re-authentication |
tailscale_get_device_routes |
Get advertised and enabled subnet routes |
tailscale_set_device_routes |
Enable or disable subnet routes |
tailscale_get_device_posture_attributes |
Get all posture attributes for a device |
tailscale_set_device_posture_attribute |
Set a custom posture attribute (with optional expiry) |
tailscale_delete_device_posture_attribute |
Delete a custom posture attribute |
tailscale_set_device_tags |
Set ACL tags on a device |
tailscale_set_device_ip |
Set a device's Tailscale IPv4 address |
tailscale_update_device_key |
Update device key settings (e.g. disable key expiry) |
tailscale_batch_update_posture_attributes |
Batch update custom posture attributes across devices |
| Tool | Description |
|---|---|
tailscale_get_acl |
Get ACL policy with formatting preserved (HuJSON) + ETag |
tailscale_update_acl |
Update ACL policy (requires ETag for safe concurrent edits) |
tailscale_validate_acl |
Validate a policy without applying it |
tailscale_preview_acl |
Preview rules that would apply to a user or IP |
| Tool | Description |
|---|---|
tailscale_get_nameservers |
Get DNS nameservers |
tailscale_set_nameservers |
Set DNS nameservers |
tailscale_get_search_paths |
Get DNS search paths |
tailscale_set_search_paths |
Set DNS search paths |
tailscale_get_split_dns |
Get split DNS configuration |
tailscale_set_split_dns |
Set split DNS configuration (full replace) |
tailscale_update_split_dns |
Update split DNS configuration (partial merge) |
tailscale_get_dns_preferences |
Get DNS preferences (MagicDNS) |
tailscale_set_dns_preferences |
Set DNS preferences (MagicDNS) |
tailscale_get_dns_configuration |
Get unified DNS configuration (all settings in one call) |
tailscale_set_dns_configuration |
Set unified DNS configuration (all settings in one call) |
| Tool | Description |
|---|---|
tailscale_list_keys |
List keys (auth keys; pass all=true to include OAuth clients and federated identities) |
tailscale_get_key |
Get details for a key |
tailscale_create_key |
Create an auth key, OAuth client (keyType=client), or federated identity (keyType=federated) |
tailscale_delete_key |
Delete a key |
tailscale_update_key |
Update a key's description, scopes, tags, or federated claim settings |
| Tool | Description |
|---|---|
tailscale_list_users |
List all users in the tailnet |
tailscale_get_user |
Get details for a specific user |
tailscale_approve_user |
Approve a pending user |
tailscale_suspend_user |
Suspend a user, revoking access |
tailscale_restore_user |
Restore a suspended user |
tailscale_update_user_role |
Update a user's role (owner, admin, member, etc.) |
tailscale_delete_user |
Delete a user and all their devices |
| Tool | Description |
|---|---|
tailscale_get_tailnet_settings |
Get tailnet settings (HTTPS, device approval, key expiry, etc.) |
tailscale_update_tailnet_settings |
Update tailnet settings (HTTPS certificates, approval, auto-updates, key expiry, posture, regional routing, network flow logging, external ACL management) |
tailscale_get_contacts |
Get tailnet contacts |
tailscale_set_contacts |
Set tailnet contacts |
tailscale_resend_contact_verification |
Resend verification email for a contact |
| Tool | Description |
|---|---|
tailscale_list_webhooks |
List webhooks |
tailscale_get_webhook |
Get a specific webhook |
tailscale_create_webhook |
Create a webhook |
tailscale_update_webhook |
Update a webhook's endpoint URL and/or subscriptions |
tailscale_delete_webhook |
Delete a webhook |
tailscale_rotate_webhook_secret |
Rotate a webhook's secret |
tailscale_test_webhook |
Send a test event to verify webhook delivery |
| Tool | Description |
|---|---|
tailscale_list_posture_integrations |
List posture integrations |
tailscale_get_posture_integration |
Get a posture integration |
tailscale_create_posture_integration |
Create a posture integration |
tailscale_update_posture_integration |
Update a posture integration |
tailscale_delete_posture_integration |
Delete a posture integration |
| Tool | Description |
|---|---|
tailscale_list_services |
List all Tailscale Services in your tailnet |
tailscale_get_service |
Get details for a specific service |
tailscale_update_service |
Update a service's configuration |
tailscale_delete_service |
Delete a service |
tailscale_list_service_hosts |
List devices hosting a service |
tailscale_get_service_device_approval |
Get approval status of a device for a service |
tailscale_set_service_device_approval |
Approve or reject a device to host a service |
| Tool | Description |
|---|---|
tailscale_list_log_stream_configs |
List log streaming configurations (both audit and network) |
tailscale_get_log_stream_config |
Get log streaming config for a log type |
tailscale_set_log_stream_config |
Set where logs are sent (Axiom, Datadog, Splunk, etc.) |
tailscale_delete_log_stream_config |
Delete a log streaming configuration |
tailscale_get_log_stream_status |
Check if log streaming is delivering successfully |
tailscale_create_aws_external_id |
Create/get AWS external ID for S3 log streaming |
tailscale_validate_aws_trust_policy |
Validate AWS IAM role trust policy for S3 log streaming |
| Tool | Description |
|---|---|
tailscale_list_device_invites |
List device invites for a specific device |
tailscale_create_device_invite |
Create a device invite |
tailscale_get_device_invite |
Get a device invite |
tailscale_delete_device_invite |
Delete a device invite |
tailscale_accept_device_invite |
Accept a device share invitation |
tailscale_resend_device_invite |
Resend a device invite email |
| Tool | Description |
|---|---|
tailscale_list_user_invites |
List user invites |
tailscale_create_user_invite |
Create a user invite |
tailscale_get_user_invite |
Get a user invite |
tailscale_delete_user_invite |
Delete a user invite |
tailscale_resend_user_invite |
Resend a user invite email |
| Tool | Description |
|---|---|
tailscale_get_audit_log |
Get configuration audit log (who changed what, when) |
tailscale_get_network_flow_logs |
Get network traffic flow logs between devices |
For the simple "deploy ACL from git on merge" workflow, you don't need an MCP server or an agent — use the built-in CLI:
npx @yawlabs/tailscale-mcp deploy-acl tailscale/acl.json
Handles ETag fetching, validation, and deployment in one command. Works in any CI system. Set TAILSCALE_API_KEY and TAILSCALE_TAILNET as env vars.
Optional: Lock the Admin Console to prevent manual edits that drift from git. Ask your agent:
"Set aclsExternallyManagedOn to true and aclsExternalLink to our repo URL"
This shows a read-only banner in the Tailscale Admin Console pointing to your repo. Use the MCP for reads and investigations, and let CI handle the deploy.
Contributions welcome. See CONTRIBUTING.md for the PR workflow and AI-agent guidelines. Please open an issue to discuss before a PR for anything beyond a typo fix.
git clone https://github.com/YawLabs/tailscale-mcp.git
cd tailscale-mcp
npm install
npm run lint # Biome check
npm run lint:fix # Auto-fix
npm run build # tsc + esbuild bundle
npm test # node --test (full suite)
For integration testing against your own tailnet: set TAILSCALE_API_KEY and run node dist/index.js.
Found a vulnerability? See SECURITY.md — please use GitHub's private vulnerability reporting, not a public issue.
MIT
Add this to claude_desktop_config.json and restart Claude Desktop.
{
"mcpServers": {
"tailscale-mcp": {
"command": "npx",
"args": [
"-y",
"@yawlabs/tailscale-mcp"
]
}
}
}pro tip
Just installed Tailscale Mcp? Say to Claude: "remember why I installed Tailscale Mcpand what I want to try" — it'll save into your Vault.
how this works →