Skip to main content
Glama
README.md
# mcp-tailscale

**Secure MCP access for private infrastructure over Tailscale**

[![npm](https://img.shields.io/npm/v/tailscale-mcp?style=flat-square)](https://www.npmjs.com/package/tailscale-mcp)
[![GitHub release](https://img.shields.io/github/v/release/itunified-io/mcp-tailscale?style=flat-square)](https://github.com/itunified-io/mcp-tailscale/releases)
[![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue?style=flat-square)](LICENSE)
[![CalVer](https://img.shields.io/badge/calver-YYYY.0M.DD.MICRO-22bfae?style=flat-square)](https://calver.org)
[![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen?style=flat-square)](https://nodejs.org)
[![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square)](https://www.typescriptlang.org/)
[![mcp-tailscale MCP server](https://glama.ai/mcp/servers/itunified-io/mcp-tailscale/badges/card.svg)](https://glama.ai/mcp/servers/itunified-io/mcp-tailscale)

## The Problem

AI agents need access to internal tools, services, and infrastructure — but exposing private systems to the internet creates unacceptable security risks. VPNs are complex, SSH tunnels are fragile, and API gateways add latency and maintenance overhead.

**mcp-tailscale** bridges this gap: a lightweight MCP server that gives AI agents secure, authenticated access to your Tailscale-connected infrastructure — without exposing anything to the public internet.

## What It Does

mcp-tailscale is an MCP Gateway Runtime that connects AI agents (Claude, GPT, custom) to your private infrastructure through Tailscale's zero-trust network. It provides **48 tools across 9 domains** for managing devices, DNS, ACL policies, auth keys, users, webhooks, posture integrations, and tailnet settings — all through the Tailscale API v2.

**No SSH. No shell execution. API-only. 4 runtime dependencies.**

## Use Cases

- **DevOps Automation** — Let AI agents manage device authorization, subnet routes, and ACL policies across your tailnet
- **DNS Management** — Configure split DNS, global nameservers, and MagicDNS through natural language
- **Security Auditing** — Automated ACL policy validation, posture compliance checks, and key rotation
- **Fleet Management** — Monitor device status, manage tags, and onboard new devices at scale
- **Infrastructure as Conversation** — Query and modify your private network configuration through AI-driven workflows

## Quick Start

### Install from npm

```bash
npm install -g tailscale-mcp
```

### Or clone and build from source

```bash
git clone https://github.com/itunified-io/mcp-tailscale.git
cd mcp-tailscale
npm install
cp .env.example .env   # Edit with your Tailscale API key and tailnet name
npm run build
node dist/index.js     # stdio transport for MCP
```

## HashiCorp Vault Integration (Optional)

`mcp-tailscale` supports **opportunistic secret loading from HashiCorp Vault** via AppRole authentication. This lets you store your Tailscale credentials centrally in Vault and avoid passing sensitive values through MCP config files or shell environment variables.

### How It Works

At startup, the server checks whether `NAS_VAULT_ADDR` is set. If it is, it authenticates to Vault using AppRole credentials, reads the KV v2 secret at `<mount>/data/tailscale/api`, and injects the values into the process environment before the Tailscale client is initialized.

- **Opportunistic loading** — if `NAS_VAULT_ADDR` is unset, the Vault loader is a silent no-op. The server behaves exactly as without Vault.
- **Silent fallback** — if Vault is unreachable, authentication fails, or the secret path is missing, a single-line warning is written to stderr and the server falls back to whatever environment variables are already set.
- **No new runtime dependencies** — the loader uses the global `fetch` available in Node.js 20+ (no extra packages).
- **Secret values are never logged** — only the KV path name and a populated-count appear in stderr diagnostics.

### Precedence

```
Explicit env vars  >  Vault  >  error (missing credentials)
```

If `TAILSCALE_API_KEY` is already set in the environment, Vault is still contacted (if configured) but the explicit value wins. This lets you override Vault values per-session without touching the Vault secret.

### Vault Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `NAS_VAULT_ADDR` | Yes* | Vault server address (e.g., `https://vault.example.com:8200`) |
| `NAS_VAULT_ROLE_ID` | Yes* | AppRole role ID for this server |
| `NAS_VAULT_SECRET_ID` | Yes* | AppRole secret ID for this server |
| `NAS_VAULT_KV_MOUNT` | No | KV v2 mount path (default: `kv`) |

\* Only required if using Vault. When `NAS_VAULT_ADDR` is unset, none of these are read.

### KV v2 Secret Structure

The loader reads the secret at path `<mount>/data/tailscale/api` (default: `kv/data/tailscale/api`) and maps keys as follows:

```
# Path: kv/tailscale/api
{
  "api_key": "tskey-api-your-key",
  "tailnet": "your-tailnet.ts.net"
}
```

| Vault Key | Maps To |
|-----------|---------|
| `api_key` | `TAILSCALE_API_KEY` |
| `tailnet` | `TAILSCALE_TAILNET` |

> **OAuth note:** the Vault loader only handles the API-key path. If you use OAuth (`TAILSCALE_OAUTH_CLIENT_ID` / `TAILSCALE_OAUTH_CLIENT_SECRET`), set those through the normal environment — they are not currently read from Vault.

### Vault Setup Steps

**1. Write the Tailscale credentials to KV v2:**

```bash
vault kv put kv/tailscale/api \
  api_key="tskey-api-your-key" \
  tailnet="your-tailnet.ts.net"
```

**2. Create a Vault policy:**

```hcl
# tailscale-mcp-policy.hcl
path "kv/data/tailscale/api" {
  capabilities = ["read"]
}
```

```bash
vault policy write tailscale-mcp tailscale-mcp-policy.hcl
```

**3. Enable AppRole auth and create a role:**

```bash
vault auth enable approle

vault write auth/approle/role/tailscale-mcp \
  token_policies="tailscale-mcp" \
  token_ttl="1h" \
  token_max_ttl="4h"
```

**4. Retrieve the role ID and generate a secret ID:**

```bash
vault read auth/approle/role/tailscale-mcp/role-id
vault write -f auth/approle/role/tailscale-mcp/secret-id
```

### MCP Config Example (with Vault)

```json
{
  "mcpServers": {
    "tailscale": {
      "command": "npx",
      "args": ["@itunified.io/mcp-tailscale"],
      "env": {
        "NAS_VAULT_ADDR": "https://vault.example.com:8200",
        "NAS_VAULT_ROLE_ID": "your-role-id",
        "NAS_VAULT_SECRET_ID": "your-secret-id"
      }
    }
  }
}
```

With this configuration, no Tailscale credentials appear in the MCP config — they are fetched from Vault at startup.

---

## Claude Code Integration

Add to `.mcp.json` in your project root:

```json
{
  "mcpServers": {
    "tailscale": {
      "command": "node",
      "args": ["/path/to/mcp-tailscale/dist/index.js"],
      "env": {
        "TAILSCALE_API_KEY": "your-api-key-here",
        "TAILSCALE_TAILNET": "your-tailnet-name"
      },
      "comment": "Or use OAuth: TAILSCALE_OAUTH_CLIENT_ID + TAILSCALE_OAUTH_CLIENT_SECRET instead of TAILSCALE_API_KEY"
    }
  }
}
```

## Features

48 tools across 9 domains:

- **Devices** — List, get, delete, authorize, expire, rename devices; manage routes, tags, and posture attributes
- **DNS** — Global nameservers, search paths, split DNS configuration, MagicDNS preferences
- **ACL** — Get, set, preview, validate, and test ACL policies
- **Keys** — List, get, create, and revoke auth keys
- **Tailnet** — Settings (read/write), contacts, Tailnet Lock status
- **Users** — List and get tailnet users with role/type filtering
- **Webhooks** — Create, list, get, and delete webhook endpoints
- **Posture Integrations** — List, get, create, and delete third-party posture provider integrations
- **Diagnostics** — Tailnet status summary, API connectivity check, log streaming, DERP map

**Authentication:** API key or OAuth client credentials (auto-refresh)

## Skills

Claude Code skills compose MCP tools into higher-level workflows. See [`.claude/skills/README.md`](.claude/skills/README.md) for detailed documentation.

| Skill | Slash Command | Description |
|-------|--------------|-------------|
| tailscale-health | `/ts-health` | Tailnet health dashboard — devices, DNS, ACL, keys, connectivity |
| tailscale-live-test | `/ts-test` | Live integration test — read + safe writes with cleanup |
| tailscale-acl-management | — | ACL policy management — view, edit, validate, test, drift detection |
| tailscale-device-management | — | Device management — list, authorize, routes, tags, posture |
| tailscale-dns-management | — | DNS management — split DNS, nameservers, search paths, MagicDNS |
| tailscale-key-management | — | Auth key management — create, list, rotate, revoke |
| tailscale-onboarding | — | New device onboarding — auth key, authorize, tags, routes, verify |

## SSE Transport

By default, mcp-tailscale uses stdio transport. To enable HTTP/SSE:

```bash
export TAILSCALE_MCP_TRANSPORT=sse
export TAILSCALE_MCP_AUTH_TOKEN=your-secret-token
export TAILSCALE_MCP_PORT=3000      # optional, default: 3000
export TAILSCALE_MCP_HOST=localhost  # optional, default: localhost
node dist/index.js
```

All requests require `Authorization: Bearer <token>`. The server will not start without `TAILSCALE_MCP_AUTH_TOKEN`.

## Configuration

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `TAILSCALE_API_KEY` | Yes* | — | Tailscale API key (from admin console > Settings > Keys) |
| `TAILSCALE_OAUTH_CLIENT_ID` | Yes* | — | OAuth client ID (from admin console > Settings > OAuth) |
| `TAILSCALE_OAUTH_CLIENT_SECRET` | Yes* | — | OAuth client secret |
| `TAILSCALE_TAILNET` | Yes | — | Tailnet name (e.g., `example.com` or your org name) |
| `TAILSCALE_API_URL` | No | `https://api.tailscale.com` | API base URL (override for testing) |
| `TAILSCALE_TIMEOUT` | No | `30000` | Request timeout in milliseconds |
| `NAS_VAULT_ADDR` | No | — | HashiCorp Vault URL, enables Vault AppRole loading (see below) |
| `NAS_VAULT_ROLE_ID` | No | — | Vault AppRole role_id |
| `NAS_VAULT_SECRET_ID` | No | — | Vault AppRole secret_id |
| `NAS_VAULT_KV_MOUNT` | No | `kv` | Vault KV v2 mount path |

*\*Either `TAILSCALE_API_KEY` or both `TAILSCALE_OAUTH_CLIENT_ID` + `TAILSCALE_OAUTH_CLIENT_SECRET` must be set. OAuth takes priority when both are configured.*

### Loading Secrets from HashiCorp Vault (AppRole)

If you run a central Vault instance, `mcp-tailscale` can fetch its credentials
at startup via AppRole instead of passing them through the MCP config:

```sh
export NAS_VAULT_ADDR=https://vault.example.com
export NAS_VAULT_ROLE_ID=<role-id>
export NAS_VAULT_SECRET_ID=<secret-id>
# optional — defaults to "kv"
export NAS_VAULT_KV_MOUNT=kv
```

The loader reads KV v2 at `<mount>/data/tailscale/api` and expects two keys:
`api_key` and `tailnet`. Example Vault write:

```sh
vault kv put kv/tailscale/api \
  api_key=tskey-api-test \
  tailnet=your-tailnet-name
```

**Precedence:** `process.env` (explicit) > Vault. If `NAS_VAULT_ADDR` is unset
the loader is a silent no-op — the server behaves exactly as before. On any
Vault error (network, auth, missing path), a single-line warning is written
to stderr and the server falls back to whatever env vars are already set.

**OAuth note:** the Vault loader only handles the API-key path. If you use
OAuth (`TAILSCALE_OAUTH_CLIENT_ID` / `TAILSCALE_OAUTH_CLIENT_SECRET`), set
those through the normal environment — they are not currently read from
Vault.

**Security:** secret values are never logged. Only the KV path name and a
populated-count appear in stderr diagnostics. Uses the global `fetch`
(Node 20+) — no new runtime dependencies.

### Authentication

**API Key:** Create at `login.tailscale.com/admin/settings/keys`. The key needs read/write access to the resources you want to manage.

**OAuth Client Credentials:** Create at `login.tailscale.com/admin/settings/oauth`. OAuth tokens auto-refresh before expiry. Recommended for automated/service integrations.

## Tools

### Devices (11 tools)

| Tool | Description |
|------|-------------|
| `tailscale_device_list` | List all devices in the tailnet |
| `tailscale_device_get` | Get device details by ID |
| `tailscale_device_delete` | Delete a device (requires `confirm: true`) |
| `tailscale_device_authorize` | Authorize a pending device |
| `tailscale_device_routes_get` | Get advertised and enabled routes |
| `tailscale_device_routes_set` | Set enabled subnet routes |
| `tailscale_device_tags_set` | Set ACL tags on a device |
| `tailscale_device_posture_get` | Get custom posture attributes |
| `tailscale_device_posture_set` | Set a custom posture attribute |
| `tailscale_device_expire` | Expire a device key (requires `confirm: true`) |
| `tailscale_device_rename` | Set a custom display name for a device |

### DNS (8 tools)

| Tool | Description |
|------|-------------|
| `tailscale_dns_nameservers_get` | Get global DNS nameservers |
| `tailscale_dns_nameservers_set` | Set global DNS nameservers |
| `tailscale_dns_searchpaths_get` | Get DNS search paths |
| `tailscale_dns_searchpaths_set` | Set DNS search paths |
| `tailscale_dns_splitdns_get` | Get split DNS configuration |
| `tailscale_dns_splitdns_set` | Update split DNS configuration (PATCH) |
| `tailscale_dns_preferences_get` | Get DNS preferences (MagicDNS) |
| `tailscale_dns_preferences_set` | Set DNS preferences |

### ACL (5 tools)

| Tool | Description |
|------|-------------|
| `tailscale_acl_get` | Get the current ACL policy |
| `tailscale_acl_set` | Replace the ACL policy (requires `confirm: true`) |
| `tailscale_acl_preview` | Preview ACL policy for a user or IP |
| `tailscale_acl_validate` | Validate an ACL policy without applying |
| `tailscale_acl_test` | Run ACL tests defined in the policy |

### Keys (4 tools)

| Tool | Description |
|------|-------------|
| `tailscale_key_list` | List all auth keys |
| `tailscale_key_get` | Get auth key details |
| `tailscale_key_create` | Create a new auth key |
| `tailscale_key_delete` | Delete an auth key (requires `confirm: true`) |

### Tailnet (5 tools)

| Tool | Description |
|------|-------------|
| `tailscale_tailnet_settings_get` | Get tailnet settings |
| `tailscale_tailnet_settings_update` | Update tailnet settings (requires `confirm: true`) |
| `tailscale_tailnet_contacts_get` | Get tailnet contact emails |
| `tailscale_tailnet_contacts_set` | Update tailnet contacts (requires `confirm: true`) |
| `tailscale_tailnet_lock_status` | Get Tailnet Lock status |

### Users (2 tools)

| Tool | Description |
|------|-------------|
| `tailscale_user_list` | List all users (filter by type/role) |
| `tailscale_user_get` | Get user details by ID |

### Webhooks (4 tools)

| Tool | Description |
|------|-------------|
| `tailscale_webhook_list` | List all webhook endpoints |
| `tailscale_webhook_create` | Create a webhook endpoint |
| `tailscale_webhook_get` | Get webhook details by ID |
| `tailscale_webhook_delete` | Delete a webhook (requires `confirm: true`) |

### Posture Integrations (4 tools)

| Tool | Description |
|------|-------------|
| `tailscale_posture_integration_list` | List all posture provider integrations |
| `tailscale_posture_integration_get` | Get posture integration details by ID |
| `tailscale_posture_integration_create` | Create a posture provider integration |
| `tailscale_posture_integration_delete` | Delete a posture integration (requires `confirm: true`) |

### Diagnostics (5 tools)

| Tool | Description |
|------|-------------|
| `tailscale_status` | Tailnet status summary (device counts, online/offline) |
| `tailscale_api_verify` | Verify API connectivity and authentication |
| `tailscale_log_stream_get` | Get log streaming configuration |
| `tailscale_log_stream_set` | Set log streaming configuration (requires `confirm: true`) |
| `tailscale_derp_map` | Get DERP relay map |

## Architecture

See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed architecture diagrams and component descriptions.

## Roadmap

See [ROADMAP.md](ROADMAP.md) for the product development roadmap.

## Development

```bash
npm run build      # Compile TypeScript
npm test           # Run unit tests (vitest)
npm run typecheck  # Type check only (no emit)
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
See [docs/api-reference.md](docs/api-reference.md) for the Tailscale API v2 endpoint mapping.

## Open Source

mcp-tailscale is the community edition — a fully functional MCP Gateway Runtime under AGPL-3.0. Self-host it, contribute to it, build on it.

What you get with the open-source edition:

- Complete Tailscale API v2 coverage (48 tools, 9 domains)
- stdio and SSE transport
- API key and OAuth authentication
- Zod-validated inputs, structured error handling
- Claude Code skills for common workflows
- Full test suite (vitest)

## Commercial

For organizations that need governance, compliance, and multi-tenant capabilities on top of the open-source runtime, we offer commercial editions with enterprise features.

**Planned enterprise capabilities:**

- Role-based access control (RBAC)
- OIDC/SAML single sign-on
- Audit event logging
- Policy engine for tool access control
- Multi-tenant isolation
- Commercial license (no AGPL obligations)
- Priority support and SLA

See [PRODUCT_PACKAGING.md](PRODUCT_PACKAGING.md) for tier details.

Contact us: [GitHub Sponsors](https://github.com/sponsors/itunified-io)

## License

This project is dual-licensed:

- **Open Source**: [GNU Affero General Public License v3.0 (AGPL-3.0)](LICENSE) — free for open-source and non-commercial use
- **Commercial**: Available for proprietary integrations — see [COMMERCIAL_LICENSE.md](COMMERCIAL_LICENSE.md)

If you use mcp-tailscale in a proprietary product or SaaS offering, a commercial license is required. Support development by [sponsoring us on GitHub](https://github.com/sponsors/itunified-io).

TDQS

A3.6/5.0

Scored across 49 tools

Disambiguation4/5

Most tools have clearly distinct resource+action pairs, and the names make selection straightforward. Minor overlap exists between tailscale_dns_nameservers_get and tailscale_dns_preferences_get (both surface MagicDNS status) and between tailscale_acl_validate and tailscale_acl_test.

Naming Consistency4/5

The dominant pattern is tailscale_<resource>_<action>, which is applied consistently across most tools. A few outliers break the pattern: tailscale_derp_map and tailscale_status lack an action verb, and tailscale_api_verify places the verb at the end.

Tool Count2/5

At 49 tools, this is well beyond the 25+ threshold and makes tool selection a heavy burden for agents. The count reflects the broad Tailscale admin API surface rather than redundant duplicates, but the sheer volume still hurts usability.

Completeness4/5

The surface covers nearly all major Tailscale admin resources: devices, DNS, ACL, auth keys, webhooks, users, posture integrations, tailnet settings, log streaming, and DERP maps. Minor gaps exist (no webhook update, no general-purpose device update), but core workflows are fully supported.

Maintenance

ActivityInactive
ResponsivenessSyncing