@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 +8 -8
- package/dist/index.js +7 -3
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/YawLabs/tailscale-mcp/stargazers)
|
|
6
6
|
[](./release.sh)
|
|
7
7
|
|
|
8
|
-
**Ask your agent questions about your tailnet and have it act on the answers.**
|
|
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.**
|
|
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
|
-
- **
|
|
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
|
-
|
|
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`** (
|
|
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 (
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
}
|