@yawlabs/tailscale-mcp 0.19.0 → 0.19.1
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/dist/index.js +24 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31481,12 +31481,17 @@ function filterTools(groups, options) {
|
|
|
31481
31481
|
// src/tools/acl.ts
|
|
31482
31482
|
var DEFAULT_PRINCIPAL_CAP = 25;
|
|
31483
31483
|
var DIFF_TIME_BUDGET_MS = 6e4;
|
|
31484
|
-
function accessSet(matches) {
|
|
31484
|
+
function accessSet(matches, postureDefs, definitionsAvailable) {
|
|
31485
31485
|
const out = /* @__PURE__ */ new Set();
|
|
31486
31486
|
for (const match of matches) {
|
|
31487
31487
|
const via = [...match.via ?? []].sort();
|
|
31488
31488
|
const postures = [...match.postures ?? []].sort();
|
|
31489
|
-
const
|
|
31489
|
+
const posturePart = postures.length > 0 ? ` posture ${postures.map((name) => {
|
|
31490
|
+
if (!definitionsAvailable) return name;
|
|
31491
|
+
const rules = [...postureDefs?.[name] ?? []].sort();
|
|
31492
|
+
return rules.length > 0 ? `${name}(${rules.join(";")})` : name;
|
|
31493
|
+
}).join(",")}` : "";
|
|
31494
|
+
const qualifier = `${via.length > 0 ? ` via ${via.join(",")}` : ""}${posturePart}`;
|
|
31490
31495
|
for (const port of match.ports ?? []) out.add(`${port}${qualifier}`);
|
|
31491
31496
|
}
|
|
31492
31497
|
return out;
|
|
@@ -31517,7 +31522,13 @@ async function previewAccess(policy, principal) {
|
|
|
31517
31522
|
status: res.status
|
|
31518
31523
|
};
|
|
31519
31524
|
}
|
|
31520
|
-
return {
|
|
31525
|
+
return {
|
|
31526
|
+
ok: true,
|
|
31527
|
+
access: accessSet(parsed.matches, parsed.postures, false),
|
|
31528
|
+
hasPostureDefs: !!parsed.postures,
|
|
31529
|
+
matches: parsed.matches,
|
|
31530
|
+
postureDefs: parsed.postures
|
|
31531
|
+
};
|
|
31521
31532
|
}
|
|
31522
31533
|
var ETAG_FOOTER_MARKER = "// ETag: ";
|
|
31523
31534
|
function stripEtagFooter(body) {
|
|
@@ -31654,7 +31665,7 @@ var aclTools = [
|
|
|
31654
31665
|
},
|
|
31655
31666
|
{
|
|
31656
31667
|
name: "tailscale_diff_acl_access",
|
|
31657
|
-
description: "Answer 'who loses access?' before applying an ACL change. Compares the CURRENT policy against a proposed one and reports, per user, which destinations they gain and lose. Run this before tailscale_update_acl -- validate_acl only checks syntax and the policy's own tests block, so a policy with no tests validates clean while revoking everyone. LIMITS, all reported in the response rather than left to be discovered. It compares USER principals only, so a revocation that runs through a tag or group can show a clean diff
|
|
31668
|
+
description: "Answer 'who loses access?' before applying an ACL change. Compares the CURRENT policy against a proposed one and reports, per user, which destinations they gain and lose. Run this before tailscale_update_acl -- validate_acl only checks syntax and the policy's own tests block, so a policy with no tests validates clean while revoking everyone. LIMITS, all reported in the response rather than left to be discovered. It compares USER principals only, so a revocation that runs through a tag or group can show a clean diff, and an empty result is never proof a change is safe. Posture DEFINITION changes ARE detected: posture names are resolved to their rules, so tightening `posture:corp` shows as a change -- except when a preview omits the definitions map, where it falls back to comparing names. It costs two preview requests per user, so it checks the first 25 by default and stops after 60 seconds regardless; either way it sets `truncated`, reports how many were skipped, and says which limit stopped it. Users whose preview fails are listed in `failed` and excluded from the compared count -- a failure is never reported as lost access, and if nothing could be compared the call fails rather than returning an empty diff.",
|
|
31658
31669
|
annotations: {
|
|
31659
31670
|
title: "Diff ACL access",
|
|
31660
31671
|
readOnlyHint: true,
|
|
@@ -31665,7 +31676,7 @@ var aclTools = [
|
|
|
31665
31676
|
inputSchema: external_exports.object({
|
|
31666
31677
|
policy: external_exports.string().describe("The proposed ACL policy text to compare against the current live policy"),
|
|
31667
31678
|
principals: external_exports.array(external_exports.string().trim().min(1)).optional().describe(
|
|
31668
|
-
"
|
|
31679
|
+
"Principals to check, as they appear in `loginName` from tailscale_list_users. That is often an email, but on a GitHub or SSO tailnet it is not (e.g. 'alice@github') -- pass the loginName verbatim rather than an address you assume. Omit to enumerate the tailnet's users automatically. Pass an explicit list to bound the request count, or to check specific users beyond the cap."
|
|
31669
31680
|
),
|
|
31670
31681
|
maxPrincipals: external_exports.number().int().positive().optional().describe(
|
|
31671
31682
|
`Maximum users to check (default ${DEFAULT_PRINCIPAL_CAP}). Each costs two preview requests. Raising this on a large tailnet can be slow and may hit rate limits.`
|
|
@@ -31742,8 +31753,11 @@ var aclTools = [
|
|
|
31742
31753
|
}
|
|
31743
31754
|
continue;
|
|
31744
31755
|
}
|
|
31745
|
-
const
|
|
31746
|
-
const
|
|
31756
|
+
const definitionsAvailable = before.hasPostureDefs && after.hasPostureDefs;
|
|
31757
|
+
const beforeAccess = accessSet(before.matches, before.postureDefs, definitionsAvailable);
|
|
31758
|
+
const afterAccess = accessSet(after.matches, after.postureDefs, definitionsAvailable);
|
|
31759
|
+
const lost = [...beforeAccess].filter((a) => !afterAccess.has(a)).sort();
|
|
31760
|
+
const gained = [...afterAccess].filter((a) => !beforeAccess.has(a)).sort();
|
|
31747
31761
|
if (lost.length === 0 && gained.length === 0) unchanged.push(principal);
|
|
31748
31762
|
else changed.push({ principal, lost, gained });
|
|
31749
31763
|
}
|
|
@@ -31792,9 +31806,8 @@ var aclTools = [
|
|
|
31792
31806
|
// can come back empty while real access changed.
|
|
31793
31807
|
scope: [
|
|
31794
31808
|
"User principals only.",
|
|
31795
|
-
"Not compared: access granted via tags or groups
|
|
31796
|
-
"changes to a
|
|
31797
|
-
"and a destination whose port list is narrowed appears as a paired loss and gain of the whole entry (`tag:prod:22,80` -> `tag:prod:22`) rather than a clean loss, so `gained` is not a literal list of newly-reachable destinations.",
|
|
31809
|
+
"Not compared: access granted via tags or groups.",
|
|
31810
|
+
"Posture definition changes ARE compared -- names are resolved to their rules -- unless a preview omitted the definitions map, in which case names alone are compared and a redefinition would not show.",
|
|
31798
31811
|
"An empty diff is not proof the change is safe."
|
|
31799
31812
|
].join(" "),
|
|
31800
31813
|
changed,
|
|
@@ -34538,7 +34551,7 @@ function buildMetaTools(state) {
|
|
|
34538
34551
|
}
|
|
34539
34552
|
|
|
34540
34553
|
// src/index.ts
|
|
34541
|
-
var version2 = true ? "0.19.
|
|
34554
|
+
var version2 = true ? "0.19.1" : resolveVersionFallback();
|
|
34542
34555
|
var subcommand = process.argv[2];
|
|
34543
34556
|
var cliSubcommandHandled = false;
|
|
34544
34557
|
if (subcommand === "deploy-acl" || subcommand === "validate-acl") {
|