@yawlabs/tailscale-mcp puts the Tailscale v2 API behind a typed tool surface an agent can compose: devices, ACLs, DNS, keys and trust credentials, users, invites, webhooks, log streaming, posture, services, and organization tailnets. Built and maintained by Yaw Labs, MIT licensed, published on npm.

npx -y @yawlabs/tailscale-mcp@latest

GitHub · npm · Add to Yaw MCP

Who it is for

Anyone who administers a tailnet and would rather ask a question than write a script. You could curl the Tailscale API - the point is not replacing curl, it is letting an agent compose multi-endpoint workflows in one turn:

If all you need is one endpoint in a CI job, use curl - or the ACL CLI subcommands below. The MCP server is for the interactive, exploratory, "I don't know what I need yet" work.

Install

1. Set your API key. Create one in the Tailscale admin console under Settings > Keys, then add it to your shell profile (~/.bashrc, ~/.zshrc, or Windows system environment variables) rather than to your client's JSON config, which is world-readable on most systems and easy to commit by accident.

export TAILSCALE_API_KEY="tskey-api-..."

2. Add the server to your client. On macOS, Linux and WSL, a .mcp.json in your project root:

{ "mcpServers": { "tailscale": { "command": "npx", "args": ["-y", "@yawlabs/tailscale-mcp@latest"] } } }

On Windows, wrap the call in cmd /c: npx is a .cmd file there, and Node 20+ refuses to spawn .cmd files directly.

{ "mcpServers": { "tailscale": { "command": "cmd", "args": ["/c", "npx", "-y", "@yawlabs/tailscale-mcp@latest"] } } }

3. Restart and approve. Restart Claude Code (or your MCP client) and approve the Tailscale server when prompted. Then ask it something: "List my Tailscale devices that haven't been seen in the last 7 days."

Running Yaw MCP? One click adds it to your local config, and it is then available in every Yaw Terminal session.

What it covers

Tools are organized into groups, and the group names are also what you pass when you want to load only some of them:

GroupWhat it covers
statusVerify the API connection, tailnet info, device count
devicesList and inspect devices, authorize, expire, rename, tag, routes, posture attributes, delete
aclGet, validate, preview and update policy, plus an access diff of a proposed policy against the live one
dnsNameservers, search paths, split DNS, MagicDNS preferences, unified get/set
keysAuth keys, OAuth clients, federated identities, OAuth apps
usersList, approve, suspend, restore, change role, delete
tailnetTailnet settings and contacts
webhooksCreate, update, delete, rotate the secret, send a test event
postureDevice-posture integrations
auditConfiguration audit log and network flow logs
invitesDevice invites and user invites
servicesTailscale Services, their hosts, and per-device approval
log-streamingWhere audit and network logs are sent, delivery status, AWS S3 trust-policy helpers
local-cliOpt-in read-only diagnostics from the local tailscale binary

ACL work is the part most worth calling out: tailscale_get_acl returns the policy with its HuJSON formatting and comments preserved plus an ETag, and tailscale_update_acl requires that ETag, so concurrent edits conflict instead of silently overwriting each other. tailscale_diff_acl_access answers the question review actually cares about: who gains and loses access.

Four MCP resources expose read-only data a client can browse without a tool call: tailnet status (tailscale://tailnet/status), devices, the ACL policy with HuJSON preserved, and the DNS configuration.

Load fewer tools

If your client is already carrying a dozen MCP servers, trim what this one exposes. Three knobs, combinable, all set in the env block of your client config:

The server logs the active filter to stderr at startup, so you can confirm what actually loaded. Your agent gets the same answer in-band: tailscale_tool_groups is always registered whatever the filters say, and tells the model whether a tool does not exist at all, exists but is not loaded, or exists with writes withheld - plus the exact environment change that would restore it. Without that, a withheld tool and a tool that was never built look identical from the agent's side, which is how an agent ends up working around a restriction instead of reporting it.

Safety and control

Deploy ACLs from CI without an agent

For the plain "deploy the ACL from git on merge" workflow you do not need an MCP server or a model at all - the same package ships the CLI:

# PR check: validate the proposed policy without touching the tailnet npx -y @yawlabs/tailscale-mcp@latest validate-acl tailscale/acl.json # On merge: ETag fetch + validate + deploy with If-Match, fail-closed at every step npx -y @yawlabs/tailscale-mcp@latest deploy-acl tailscale/acl.json

Works in any CI system - set TAILSCALE_API_KEY and TAILSCALE_TAILNET as environment variables. Both commands exit non-zero on any failure, and deploy-acl refuses to deploy without an ETag, reporting a 412 as the concurrent-edit conflict it is. Hand-rolling this with curl has a trap worth knowing about: Tailscale returns the ETag header on GET but not on HEAD, so a curl -I fetch yields an empty value and an empty If-Match either deploys unguarded or fails the job.

Frequently asked questions

What does the Tailscale MCP server do?

It exposes the Tailscale v2 admin API as MCP tools, so an agent can answer questions about your tailnet and act on the answers in the same turn. Devices, ACLs, DNS, auth keys and trust credentials, users, invites, webhooks, log streaming, posture, services and organization tailnets are all covered, along with read-only resources a client can browse without a tool call.

Which MCP clients does it work with?

Any client that speaks MCP. Unlike a Claude Code skill, which only loads in Claude Code, this server works in Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, and anything else that speaks the protocol. Version bumps ship through npx, so you do not re-author anything when Tailscale adds an endpoint.

How do I authenticate it?

Set TAILSCALE_API_KEY for the simple path, or TAILSCALE_OAUTH_CLIENT_ID and TAILSCALE_OAUTH_CLIENT_SECRET for scoped OAuth access. The server checks for an API key first and falls back to OAuth. If neither is set it still starts, and tools return a clear error telling you what to configure, so your client does not loop restarting. Your default tailnet is used automatically; set TAILSCALE_TAILNET to name one explicitly.

Can I stop the agent from changing anything?

Yes. TAILSCALE_READONLY=1 drops every tool that is not marked read-only, and TAILSCALE_WRITE_GROUPS names the areas writes are allowed in while everything else stays readable. Read the caveat first: those knobs filter the tool list, not your API token. The server still holds one credential with full tailnet authority, so an agent that also has a shell can reach the API directly. Scope the Tailscale OAuth client itself to the areas you actually need.

Do I need an agent to deploy ACLs from CI?

No. The same package ships validate-acl and deploy-acl CLI subcommands for the deploy-ACL-from-git workflow. Both exit non-zero on any failure, and deploy-acl refuses to deploy without an ETag, so a concurrent Admin Console edit can never be silently clobbered. Use the MCP server for interactive, exploratory work and the CLI for the merge hook.

Related MCP servers

Further reading

Published by Yaw Labs.