@yawlabs/tailscale-mcp 0.12.7 → 0.12.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![GitHub stars](https://img.shields.io/github/stars/YawLabs/tailscale-mcp)](https://github.com/YawLabs/tailscale-mcp/stargazers)
6
6
  [![Release](https://img.shields.io/badge/release-local-blue)](./release.sh)
7
7
 
8
- **Ask your agent questions about your tailnet and have it act on the answers.** 93 admin-API tools + 4 optional local-CLI diagnostics + 4 resources covering the full [Tailscale v2 API](https://tailscale.com/api). Backed by 1000+ unit tests and an opt-in live-tailnet integration suite.
8
+ **Ask your agent questions about your tailnet and have it act on the answers.** 89 admin-API tools + 4 optional local-CLI diagnostics + 4 resources covering the full [Tailscale v2 API](https://tailscale.com/api). Backed by 700+ unit tests and an opt-in live-tailnet integration suite.
9
9
 
10
10
  Built and maintained by [Yaw Labs](https://yaw.sh).
11
11
 
@@ -35,7 +35,7 @@ Reasonable question. Both have their place. Where this MCP is better:
35
35
  - **Typed tool surface, not string parsing.** Every tool has a Zod-validated input schema and a structured response. No brittle `tailscale status --json | jq` pipelines that break when the schema evolves.
36
36
  - **Cross-client, no user rewriting.** A Claude Code skill only loads in Claude Code. An MCP server works in Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, and anything else that speaks MCP. Version bumps ship through `npx` — users don't re-author their skill when Tailscale adds an endpoint.
37
37
  - **Safe-by-default writes.** Every tool declares `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.
38
- - **Real tests.** 1000+ unit tests covering every tool's input validation, API shape, and error handling. Plus an opt-in live-tailnet integration suite (`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.
38
+ - **Real tests.** 700+ unit tests covering every tool's input validation, API shape, and error handling. Plus an opt-in live-tailnet integration suite (`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.
39
39
 
40
40
  If you already have a skill that covers your 10% of Tailscale workflows, great — keep it. The MCP is for the other 90%.
41
41
 
@@ -43,7 +43,7 @@ If you already have a skill that covers your 10% of Tailscale workflows, great
43
43
 
44
44
  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:
45
45
 
46
- - **1000+ tests** (`node --test`) covering every tool's input validation, API shape, and error handling. Run `npm test` to see them pass locally.
46
+ - **700+ tests** (`node --test`) covering every tool's input validation, API shape, and error handling. Run `npm test` to see them pass locally.
47
47
  - **Local release flow** via [`release.sh`](./release.sh): lint + test + bump + tag + push + npm publish + MCP Registry publish, all from the workstation. No CI workflow to babysit.
48
48
  - **Dependabot alerts** surface on this repo and get fixed, not ignored.
49
49
  - **Every tool verified against the live API.** If it's in the tool list, it calls a real endpoint that exists in the current v2 API. No placeholder 404 tools.
@@ -104,7 +104,7 @@ That's it. Now ask your agent:
104
104
 
105
105
  ## Too many tools? Subset them.
106
106
 
107
- 93 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:
107
+ 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:
108
108
 
109
109
  ### Option 1: `TAILSCALE_PROFILE` (preset, easiest)
110
110
 
@@ -119,7 +119,7 @@ That's it. Now ask your agent:
119
119
 
120
120
  - **`minimal`** (20 tools) — `status`, `devices`, `audit`. Observe the tailnet, read the audit log.
121
121
  - **`core`** (47 tools) — adds `acl`, `dns`, `keys`, `users`. The day-to-day admin surface.
122
- - **`full`** (93 tools, default) — everything. Same as omitting the env var.
122
+ - **`full`** (89 tools, default) — everything. Same as omitting the env var.
123
123
 
124
124
  ### Option 2: `TAILSCALE_TOOLS` (explicit group list)
125
125
 
@@ -221,7 +221,7 @@ Set `TAILSCALE_LOCAL_CLI=1` (in your shell or `.mcp.json` `env` block) to add fo
221
221
 
222
222
  Requirements: the `tailscale` binary must be in `PATH`. If it's installed somewhere unusual, set `TAILSCALE_BINARY` to its absolute path. The MCP server doesn't need root to run these — they're all diagnostic, not state-mutating. Operations that would need elevation (`tailscale up`, `set --advertise-routes`, `lock sign`) are deliberately not exposed.
223
223
 
224
- When opt-in is on, the startup banner reflects it: `@yawlabs/tailscale-mcp v0.10.9 ready (93 tools, local-cli=on)`.
224
+ When opt-in is on, the startup banner reflects it: `@yawlabs/tailscale-mcp v0.10.9 ready (89 tools, local-cli=on)`.
225
225
 
226
226
  ## Resources (4)
227
227
 
@@ -234,7 +234,7 @@ MCP Resources expose read-only data clients can browse without a tool call.
234
234
  | ACL Policy | `tailscale://tailnet/acl` | Full ACL policy (HuJSON preserved) |
235
235
  | DNS Config | `tailscale://tailnet/dns` | Nameservers, search paths, split DNS, MagicDNS |
236
236
 
237
- ## Tools (93 + 4 opt-in)
237
+ ## Tools (89 + 4 opt-in)
238
238
 
239
239
  <details>
240
240
  <summary><strong>Status</strong> (1 tool)</summary>
@@ -467,7 +467,7 @@ This shows a read-only banner in the Tailscale Admin Console pointing to your re
467
467
 
468
468
  ## Requirements
469
469
 
470
- - Node.js 20+
470
+ - Node.js 20+ to run the server (22+ to develop — the test script passes a glob to `node --test`, supported from Node 21)
471
471
  - A Tailscale API key or OAuth client credentials
472
472
 
473
473
  ## Contributing
package/dist/index.js CHANGED
@@ -31959,7 +31959,7 @@ var deviceTools = [
31959
31959
  },
31960
31960
  inputSchema: external_exports.object({
31961
31961
  deviceId: external_exports.string().describe("The device ID"),
31962
- ipv4: external_exports.string().ipv4().describe("The new Tailscale IPv4 address for the device (e.g. '100.64.0.1')")
31962
+ ipv4: external_exports.ipv4().describe("The new Tailscale IPv4 address for the device (e.g. '100.64.0.1')")
31963
31963
  }),
31964
31964
  handler: async (input) => {
31965
31965
  return apiPost(`/device/${encPath(input.deviceId)}/ip`, { ipv4: input.ipv4 });
@@ -33551,7 +33551,7 @@ function getAllowedWebhookEvents() {
33551
33551
  const extras = raw.split(",").map((s) => s.trim()).filter(Boolean);
33552
33552
  return /* @__PURE__ */ new Set([...STATIC_WEBHOOK_EVENT_TYPES, ...extras]);
33553
33553
  }
33554
- var endpointUrlSchema = external_exports.string().url().refine((u) => u.startsWith("https://"), "endpointUrl must use https://");
33554
+ var endpointUrlSchema = external_exports.url().refine((u) => u.startsWith("https://"), "endpointUrl must use https://");
33555
33555
  var webhookSubscriptionsSchema = external_exports.array(external_exports.string()).min(1).superRefine((arr, ctx) => {
33556
33556
  const allowed = getAllowedWebhookEvents();
33557
33557
  let knownEventsList = null;
@@ -33706,7 +33706,7 @@ var webhookTools = [
33706
33706
  ];
33707
33707
 
33708
33708
  // src/index.ts
33709
- var version2 = true ? "0.12.7" : resolveVersionFallback();
33709
+ var version2 = true ? "0.12.9" : resolveVersionFallback();
33710
33710
  var subcommand = process.argv[2];
33711
33711
  if (subcommand === "deploy-acl") {
33712
33712
  const filePath = process.argv[3];
@@ -33722,6 +33722,10 @@ if (subcommand === "deploy-acl") {
33722
33722
  } else if (subcommand === "version" || subcommand === "--version") {
33723
33723
  console.log(version2);
33724
33724
  process.exit(0);
33725
+ } else if (subcommand !== void 0) {
33726
+ console.error(
33727
+ `@yawlabs/tailscale-mcp: unrecognized argument "${subcommand}" -- known subcommands: deploy-acl, version. Starting the MCP server.`
33728
+ );
33725
33729
  }
33726
33730
  var toolGroups = {
33727
33731
  status: statusTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/tailscale-mcp",
3
- "version": "0.12.7",
3
+ "version": "0.12.9",
4
4
  "mcpName": "io.github.YawLabs/tailscale-mcp",
5
5
  "description": "Tailscale MCP server for managing your tailnet from AI assistants",
6
6
  "license": "MIT",
@@ -32,7 +32,7 @@
32
32
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
33
33
  "dev": "tsc --watch",
34
34
  "start": "node dist/index.js",
35
- "test": "npm run build && node --test dist/**/*.test.js",
35
+ "test": "npm run build && node --test \"dist/**/*.test.js\"",
36
36
  "test:ci": "npm run test",
37
37
  "lint": "biome check src/",
38
38
  "lint:fix": "biome check --write src/",
@@ -40,10 +40,11 @@
40
40
  },
41
41
  "dependencies": {},
42
42
  "overrides": {
43
- "hono": "^4.12.18",
43
+ "hono": "^4.12.21",
44
44
  "@hono/node-server": "^1.19.13",
45
45
  "fast-uri": "^3.1.2",
46
- "ip-address": "^10.1.1"
46
+ "ip-address": "^10.1.1",
47
+ "qs": "^6.15.2"
47
48
  },
48
49
  "devDependencies": {
49
50
  "@biomejs/biome": "^2.4.12",
@@ -55,5 +56,12 @@
55
56
  },
56
57
  "engines": {
57
58
  "node": ">=20"
59
+ },
60
+ "devEngines": {
61
+ "runtime": {
62
+ "name": "node",
63
+ "version": ">=22",
64
+ "onFail": "error"
65
+ }
58
66
  }
59
67
  }