{
  "markdown": "# tailscale-rmcp\n\nTailscale device, route, DNS, key, user, and ACL management over MCP and CLI.\n\nIt exposes one MCP tool, `tailscale`, plus the `rtailscale` CLI. Agents can\nlist devices, inspect routes, read API keys, ACL policy, DNS settings, and\nusers, authorize devices, and delete devices when the destructive gate is\nexplicitly enabled.\n\n**30-second path:** set `TAILSCALE_API_KEY`, then run\n`npx -y @dinglebear/rtailscale devices --json` -> start loopback HTTP with\n`TAILSCALE_MCP_HOST=127.0.0.1 npx -y @dinglebear/rtailscale serve` -> call `tools/call`\nwith `{\"action\":\"devices\"}`.\n\n**Status:** operational RMCP upstream-client server. Write-capable for device\nauthorization; destructive device deletion requires both server opt-in and\ncaller confirmation. HTTP MCP supports loopback dev mode, static bearer tokens,\nand Google OAuth through `lab-auth`.\n\n**Not for:** replacing the Tailscale admin console, bypassing Tailscale account\npermissions, operating multiple unrelated tailnets from one trust boundary,\nstoring API keys for callers, arbitrary WireGuard control, or passing Tailscale\nAPI keys through MCP tool arguments.\n\n## Contents\n\n- [Naming](#naming)\n- [Capabilities And Boundaries](#capabilities-and-boundaries)\n- [Install](#install)\n- [Quickstart](#quickstart)\n- [Client Configuration](#client-configuration)\n- [Runtime Surfaces](#runtime-surfaces)\n- [MCP Tool Reference](#mcp-tool-reference)\n- [CLI Reference](#cli-reference)\n- [Configuration](#configuration)\n- [Authentication](#authentication)\n- [Safety And Trust Model](#safety-and-trust-model)\n- [Architecture](#architecture)\n- [Distribution Contract](#distribution-contract)\n- [Development](#development)\n- [Verification](#verification)\n- [Deployment](#deployment)\n- [Troubleshooting](#troubleshooting)\n- [Related Servers](#related-servers)\n- [Documentation](#documentation)\n- [License](#license)\n\n## Naming\n\n| Surface | This repo |\n|---|---|\n| Repository | `tailscale-rmcp` |\n| Rust crate | `tailscale-rmcp` |\n| Binary / CLI | `rtailscale` |\n| npm package | `@dinglebear/rtailscale` |\n| npm binary aliases | `tailscale-rmcp`, `rtailscale` |\n| MCP tool | `tailscale` |\n| Config home | `~/.tailscale-mcp` on hosts, `/data` in containers |\n| Env prefixes | `TAILSCALE_*`, `TAILSCALE_MCP_*`, `TAILSCALE_RMCP_*` for npm launcher controls |\n\nThe repo, crate, and npm package use the RMCP family name. The shipped binary is\n`rtailscale` to avoid shadowing the official `tailscale` CLI.\n\n## Capabilities And Boundaries\n\n- List devices and inspect a single device by node ID or legacy numeric device\n  ID.\n- Read subnet routes, API keys, ACL policy, DNS/MagicDNS settings, and tailnet\n  users.\n- Authorize a device for the tailnet.\n- Delete a device only when `TAILSCALE_ALLOW_DESTRUCTIVE=true` and the caller\n  also passes explicit confirmation.\n- Provide setup and doctor commands for local plugin/runtime checks.\n\n| This repo owns | Tailscale owns | Explicitly out of scope |\n|---|---|---|\n| MCP/CLI projection, request validation, HTTP MCP auth policy, response shaping, setup checks, and destructive gates. | Tailnet state, device identities, ACL semantics, DNS behavior, API key issuance, user membership, and upstream authorization. | Replacing the admin console, storing caller credentials, multi-tailnet tenancy, arbitrary WireGuard control, policy editing beyond exposed actions, and local Tailscale daemon management. |\n\n## Install\n\n| Path | Command | Best for | Notes |\n|---|---|---|---|\n| npm / npx | `npx -y @dinglebear/rtailscale --help` | Local MCP clients and quick trials. | Downloads the matching `rtailscale` binary from GitHub Releases. |\n| Release installer | `curl -fsSL https://raw.githubusercontent.com/dinglebear-ai/rtailscale/main/scripts/install.sh \\| bash` | Host installs without Node. | Installs `rtailscale` for the current Linux host. |\n| Docker / Compose | `docker compose up -d` | Shared HTTP MCP deployments. | Reads `.env` and exposes container port `40040`. |\n| Build from source | `cargo build --release` | Development and audits. | Produces `target/release/rtailscale`. |\n| Plugin | `claude plugin install plugins/tailscale` | Claude Code local plugin setup from this checkout. | Ships the skill, `.mcp.json`, and a bundled binary. No hooks — run `rtailscale setup check` yourself if you want the environment verified. |\n\n### npm / npx\n\nRun the stdio MCP server or CLI without a manual binary install:\n\n```bash\nnpx -y @dinglebear/rtailscale --help\nnpx -y @dinglebear/rtailscale mcp\nnpx -y @dinglebear/rtailscale devices --json\n```\n\nThe npm package downloads `rtailscale` during `postinstall`. Override download\nbehavior only when testing packaging:\n\n| Variable | Purpose |\n|---|---|\n| `TAILSCALE_RMCP_SKIP_DOWNLOAD=1` | Skip postinstall binary download. |\n| `TAILSCALE_RMCP_VERSION` or `TAILSCALE_RMCP_BINARY_VERSION` | Select the GitHub Release tag. |\n| `TAILSCALE_RMCP_REPO` | Select the GitHub repo used for release downloads. |\n| `TAILSCALE_RMCP_RELEASE_BASE_URL` | Select a custom release base URL. |\n\n### Build From Source\n\n```bash\ngit clone https://github.com/dinglebear-ai/rtailscale\ncd rtailscale\ncargo build --release\n./target/release/rtailscale --help\n```\n\nMinimum supported Rust version: 1.86.\n\n## Quickstart\n\n### 1. Get A Tailscale API Key\n\nCreate an API key at\n<https://login.tailscale.com/admin/settings/keys>. Use the minimum capability\nneeded for the actions you plan to expose.\n\n### 2. Configure The Tailnet\n\n```bash\nexport TAILSCALE_API_KEY=\"tskey-api-...\"\nexport TAILSCALE_TAILNET=\"-\"          # personal, or \"example.com\" for orgs\n```\n\nEvery Tailscale account belongs to a tailnet. `TAILSCALE_TAILNET=-` targets the\ndefault personal tailnet; organization tailnets usually use the org domain.\n\n### 3. Run A Safe CLI Call\n\n```bash\nnpx -y @dinglebear/rtailscale devices --json\n```\n\n### 4. Start Loopback HTTP MCP\n\n```bash\nTAILSCALE_MCP_HOST=127.0.0.1 npx -y @dinglebear/rtailscale serve\n```\n\nIn another shell:\n\n```bash\ncurl -sf http://127.0.0.1:40040/health\n```\n\n### 5. Make A First MCP Call\n\n```bash\ncurl -s -X POST http://127.0.0.1:40040/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json, text/event-stream\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"tailscale\",\"arguments\":{\"action\":\"devices\"}}}'\n```\n\n## Client Configuration\n\n### Claude Code Stdio\n\n```json\n{\n  \"mcpServers\": {\n    \"tailscale\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"tailscale-rmcp\", \"mcp\"],\n      \"env\": {\n        \"TAILSCALE_API_KEY\": \"tskey-api-...\",\n        \"TAILSCALE_TAILNET\": \"-\"\n      }\n    }\n  }\n}\n```\n\n### Claude Code HTTP\n\n```json\n{\n  \"mcpServers\": {\n    \"tailscale\": {\n      \"type\": \"http\",\n      \"url\": \"http://127.0.0.1:40040/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer ${TAILSCALE_MCP_TOKEN}\"\n      }\n    }\n  }\n}\n```\n\n### Codex / Labby Gateway\n\nRegister Tailscale through Labby as an HTTP upstream when sharing one\nlong-running server, or run it directly as stdio for local-only use.\n\n```toml\n[mcp_servers.tailscale]\ncommand = \"npx\"\nargs = [\"-y\", \"tailscale-rmcp\", \"mcp\"]\n```\n\n### Generic MCP JSON\n\n```json\n{\n  \"command\": \"rtailscale\",\n  \"args\": [\"mcp\"],\n  \"env\": {\n    \"TAILSCALE_API_KEY\": \"tskey-api-...\",\n    \"TAILSCALE_TAILNET\": \"-\"\n  }\n}\n```\n\nDo not put `TAILSCALE_API_KEY`, OAuth secrets, passwords, SSH keys, or upstream\nbearer tokens in MCP tool arguments. Use env, config files, or the MCP client's\nsecret storage. MCP callers never provide credentials, tokens, keys, or secrets\nas action arguments.\n\n## Runtime Surfaces\n\n| Surface | Status | Entry point | Purpose |\n|---|---:|---|---|\n| MCP stdio | Supported | `rtailscale mcp`, `npx -y @dinglebear/rtailscale mcp` | Local child-process MCP clients. |\n| MCP HTTP | Supported | `rtailscale serve`, `POST /mcp` | Streamable HTTP MCP for local or shared server deployments. |\n| CLI | Supported | `rtailscale <command>` | Scriptable parity and debugging. |\n| REST API | Not shipped | N/A | Tailscale already owns the REST API. |\n| Web UI | Not shipped | N/A | Tailscale already owns the admin console. |\n\n## MCP Tool Reference\n\nOne MCP tool is exposed: `tailscale`. Pass the required `action` argument to\nselect the operation.\n\n### Read Actions\n\n| Action | Description | Required params | Optional params |\n|---|---|---|---|\n| `devices` | List all devices in the tailnet. | none | none |\n| `device` | Return one device. | `id` | none |\n| `device_routes` | Return subnet routes for one device. | `id` | none |\n| `keys` | List API keys in the tailnet. | none | none |\n| `acl` | Return ACL policy JSON. | none | none |\n| `dns` | Return DNS nameservers, search paths, and MagicDNS preferences. | none | none |\n| `users` | List tailnet users. | none | none |\n| `help` | Return built-in action documentation. | none | none |\n\n### Write Actions\n\n| Action | Description | Required params | Optional params |\n|---|---|---|---|\n| `authorize_device` | Approve a device for the tailnet. | `id` | none |\n\n### Destructive Actions\n\n| Action | Description | Required params | Optional params |\n|---|---|---|---|\n| `delete_device` | Permanently remove a device. | `id`, `confirm=true` | none |\n\nDevice IDs may be stable node IDs such as `n1234abc` or legacy numeric device\nIDs. Use `action=devices` first to discover IDs.\n\n## CLI Reference\n\nThe binary calls the same service layer as the MCP tool:\n\n```bash\nrtailscale devices [--json]\nrtailscale device <id> [--json]\nrtailscale routes <device-id> [--json]\nrtailscale keys [--json]\nrtailscale acl [--json]\nrtailscale dns [--json]\nrtailscale users [--json]\nrtailscale authorize <device-id> [--json]\nrtailscale delete-device <device-id> --confirm [--json]\nrtailscale doctor [--json]\nrtailscale setup check [--json]\nrtailscale setup repair [--json]\nrtailscale setup plugin-hook [--no-repair] [--json]\nrtailscale serve            # HTTP MCP (also the no-argument default)\nrtailscale mcp              # stdio MCP\n```\n\nAll commands currently print JSON. `--json` is accepted for parity with the rest\nof the RMCP family.\n\n## Configuration\n\nHost installs read `~/.tailscale-mcp/.env` before loading config. Containers\nread `/data/.env`. Process environment overrides both.\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `TAILSCALE_API_KEY` | unset | Tailscale API key. |\n| `TAILSCALE_TAILNET` | `-` | Tailnet: org domain or `-` for personal. |\n| `TAILSCALE_ALLOW_DESTRUCTIVE` | `false` | Enable `delete_device` server-side. |\n| `TAILSCALE_MCP_HOST` | `0.0.0.0` | HTTP bind host. |\n| `TAILSCALE_MCP_PORT` | `40040` | HTTP bind port. |\n| `TAILSCALE_MCP_SERVER_NAME` | `tailscale-rmcp` | Advertised MCP server name. |\n| `TAILSCALE_MCP_NO_AUTH` | `false` | Disable auth only for loopback development. |\n| `TAILSCALE_MCP_TOKEN` | unset | Static bearer token for HTTP MCP. |\n| `TAILSCALE_NOAUTH` | `false` | Trust an upstream gateway to enforce auth. |\n| `TAILSCALE_MCP_AUTH_MODE` | `bearer` | `bearer` or `oauth`. |\n| `TAILSCALE_MCP_PUBLIC_URL` | unset | Public URL for OAuth discovery. |\n| `TAILSCALE_MCP_GOOGLE_CLIENT_ID` | unset | Google OAuth client ID. |\n| `TAILSCALE_MCP_GOOGLE_CLIENT_SECRET` | unset | Google OAuth client secret. |\n| `TAILSCALE_MCP_AUTH_ADMIN_EMAIL` | unset | Admin email for OAuth bootstrap. |\n| `TAILSCALE_MCP_ALLOWED_HOSTS` | unset | Extra accepted Host header values. |\n| `TAILSCALE_MCP_ALLOWED_ORIGINS` | unset | Extra accepted CORS origins. |\n| `TAILSCALE_MCP_HOME` | platform appdata | Override the config/log home used by `setup` and file logging. |\n\nThe Tailscale API base URL is not configurable; it is fixed at\n`https://api.tailscale.com/api/v2` in `src/tailscale.rs`.\n\n## Authentication\n\nStdio MCP runs as a local trusted child process and does not use HTTP auth.\n\nHTTP MCP auth policy:\n\n| State | Condition | Behavior |\n|---|---|---|\n| Loopback dev | `TAILSCALE_MCP_HOST` starts with `127.` or auth is explicitly disabled on loopback | Local unauthenticated development is allowed. |\n| Mounted bearer | Non-loopback with `TAILSCALE_MCP_TOKEN` | Requires `Authorization: Bearer <token>` and action scopes. |\n| Mounted OAuth | `TAILSCALE_MCP_AUTH_MODE=oauth` | Uses Google OAuth/JWT through `lab-auth`. |\n| Trusted gateway | `TAILSCALE_NOAUTH=true` | Assumes a reverse proxy or gateway already enforced auth. |\n\nNon-loopback HTTP startup is rejected unless bearer auth, OAuth, or\n`TAILSCALE_NOAUTH=true` is configured.\n\n## Safety And Trust Model\n\n- Tailscale API keys are loaded from config/env only.\n- MCP callers select actions and IDs, not upstream credentials.\n- `delete_device` has a two-key interlock:\n  `TAILSCALE_ALLOW_DESTRUCTIVE=true` on the server and caller-provided\n  `confirm=true`.\n- Missing IDs and unknown actions fail before upstream calls.\n- Non-loopback HTTP deployments must use bearer auth, OAuth, or a trusted\n  authenticated gateway.\n- This bridge does not sandbox Tailscale itself. Tailscale remains responsible\n  for API permissions and tailnet state changes.\n\n## Architecture\n\n```text\nTailscaleClient  (src/tailscale.rs)  REST transport and API error handling\n       |\nTailscaleService (src/app.rs)        action behavior and destructive gates\n       |\nMCP shim         (src/mcp.rs)        JSON args -> service -> Value\nCLI shim         (src/cli.rs)        argv -> service -> stdout\n```\n\n## Distribution Contract\n\n- `Cargo.toml`, `Cargo.lock`, `packages/tailscale-rmcp/package.json`,\n  `.release-please-manifest.json`, and `server.json` must agree on the released\n  version.\n- GitHub Releases publish the `rtailscale` binary consumed by the npm launcher.\n- The npm package name is `@dinglebear/rtailscale`; binary aliases are\n  `tailscale-rmcp` and `rtailscale`.\n- Docker/OCI metadata uses `ghcr.io/dinglebear-ai/rtailscale:<version>` (see\n  `docker-compose.prod.yml`). The image path still uses the pre-transfer owner\n  namespace even though the repo now lives at `dinglebear-ai/rtailscale`.\n- `plugins/tailscale/.mcp.json` must launch `npx -y @dinglebear/rtailscale mcp` so\n  stdio clients start the MCP transport rather than the HTTP server.\n- `plugins/tailscale/` ships no Claude Code hooks; `scripts/validate-plugin-layout.sh`\n  fails if a `hooks/` directory reappears.\n- The root README is curated. Source of truth for action behavior and config\n  defaults is `src/`, plus the package, plugin, and registry manifests.\n\n## Development\n\n```bash\ncargo fmt --check\ncargo test\ncargo clippy -- -D warnings\ncargo build --release\nnpm --prefix packages/tailscale-rmcp run check\n```\n\n## Verification\n\n```bash\njust validate-plugin\nnpm --prefix packages/tailscale-rmcp run check\ncargo check\ncargo test\ngit diff --check\n```\n\nRuntime smoke:\n\n```bash\nTAILSCALE_API_KEY=tskey-api-... \\\nTAILSCALE_TAILNET=- \\\nrtailscale devices --json\n```\n\nHTTP smoke:\n\n```bash\nTAILSCALE_MCP_HOST=127.0.0.1 rtailscale serve\ncurl -sf http://127.0.0.1:40040/health\n```\n\n## Deployment\n\nUse loopback for local development:\n\n```bash\nTAILSCALE_MCP_HOST=127.0.0.1 rtailscale serve\n```\n\nUse Docker Compose for shared HTTP deployment:\n\n```bash\ncp .env.example .env\ndocker compose up -d\n```\n\nWhen binding to a non-loopback address, configure `TAILSCALE_MCP_TOKEN`,\n`TAILSCALE_MCP_AUTH_MODE=oauth`, or `TAILSCALE_NOAUTH=true` behind an\nauthenticated gateway.\n\n## Troubleshooting\n\n| Symptom | Check |\n|---|---|\n| `TAILSCALE_API_KEY` is missing | Set it in env or `~/.tailscale-mcp/.env`. |\n| Device calls return unauthorized | Refresh the API key in Tailscale admin settings. |\n| HTTP `/mcp` returns unauthorized | Set `TAILSCALE_MCP_TOKEN` and send `Authorization: Bearer <token>`. |\n| Stdio client hangs or logs JSON errors | Ensure client config runs `tailscale-rmcp mcp`, not the default HTTP server mode. |\n| `delete_device` is rejected | Set `TAILSCALE_ALLOW_DESTRUCTIVE=true` server-side and pass `confirm=true` after verifying the target. |\n| Port conflict | Set `TAILSCALE_MCP_PORT` or stop the process already using `40040`. |\n\n## Related Servers\n\n- [soma](https://github.com/dinglebear-ai/soma) - RMCP runtime and scaffold for provider-backed MCP servers.\n- [unifi-rmcp](https://github.com/dinglebear-ai/runifi) - UniFi controller REST API bridge.\n- [unraid](https://github.com/dinglebear-ai/unraid) - Unraid monorepo; `unraid-rs/` is the GraphQL bridge for NAS and server management.\n- [apprise-rmcp](https://github.com/dinglebear-ai/rapprise) - Apprise notification fan-out bridge for many delivery backends.\n- [gotify-rmcp](https://github.com/dinglebear-ai/rgotify) - Gotify push notification bridge for sends, messages, apps, and clients.\n- [arcane-rmcp](https://github.com/dinglebear-ai/rarcane) - Arcane Docker management bridge for containers and related resources.\n- [yarr](https://github.com/dinglebear-ai/yarr) - Media-stack bridge for Sonarr, Radarr, Prowlarr, Plex, and related services.\n- [ytdl-rmcp](https://github.com/dinglebear-ai/rytdl) - Media download and metadata workflow server.\n- [synapse-rmcp](https://github.com/dinglebear-ai/synapse) - Local Synapse workflow server for scout and flux actions.\n- [cortex](https://github.com/dinglebear-ai/cortex) - Syslog and homelab log aggregation MCP server.\n- [axon](https://github.com/dinglebear-ai/axon) - RAG, crawl, scrape, extract, and semantic search project.\n- [labby](https://github.com/dinglebear-ai/labby) - Homelab control plane and MCP gateway project.\n- [lumen](https://github.com/dinglebear-ai/lumen) - Local semantic code search MCP server.\n\n## Documentation\n\n- `CLAUDE.md` is the curated local operating guide for contributors and agents.\n- `docs/SETUP.md` is curated plugin/setup guidance.\n- `docs/OAUTH.md` is curated OAuth setup guidance.\n- `plugins/tailscale/skills/tailscale/SKILL.md` is the agent usage guide.\n- `src/` is the source of truth for current actions, config defaults, auth\n  behavior, and CLI parsing.\n- Package, plugin, Docker, and registry manifests are curated distribution\n  contracts and should be checked with the verification commands above.\n\n## License\n\nOriginal Dinglebear-authored portions of this project are licensed under [AGPL-3.0-only](LICENSE). Separate commercial licensing is available for organizations that need terms outside the AGPL. Third-party material remains under its original license. See [LICENSING.md](https://github.com/dinglebear-ai/rtailscale/blob/main/LICENSING.md).\n",
  "bytes": 18197,
  "sha": "ea6430e50bd3db105527d11265f14a3af160f5511ba047e0e15fc94393a30dc6",
  "repo_slug": "jmagar/tailscale-rmcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_ai_dinglebear_tailscale_rmcp_bb0803f8/readme"
}