@yawlabs/tailscale-mcp 0.12.1 → 0.12.3
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 +58 -60
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30958,7 +30958,7 @@ var MAX_429_RETRIES = 3;
|
|
|
30958
30958
|
var DEFAULT_429_DELAY_MS = 1e3;
|
|
30959
30959
|
var MAX_429_DELAY_MS = 3e4;
|
|
30960
30960
|
var MAX_REQUEST_BUDGET_MS = 9e4;
|
|
30961
|
-
var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "
|
|
30961
|
+
var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "PUT", "DELETE"]);
|
|
30962
30962
|
var oauthToken = null;
|
|
30963
30963
|
var oauthRefreshPromise = null;
|
|
30964
30964
|
function getAuthConfig() {
|
|
@@ -31165,7 +31165,8 @@ async function apiRequest(method, path, body, options) {
|
|
|
31165
31165
|
headers["Content-Type"] = "application/json";
|
|
31166
31166
|
fetchBody = JSON.stringify(body);
|
|
31167
31167
|
}
|
|
31168
|
-
const
|
|
31168
|
+
const isAbsolute = path.startsWith("http://") || path.startsWith("https://");
|
|
31169
|
+
const url2 = isAbsolute ? path : `${BASE_URL}${path}`;
|
|
31169
31170
|
const startedAt = Date.now();
|
|
31170
31171
|
debugLog(`${method} ${url2}`);
|
|
31171
31172
|
const isRetryable = RETRYABLE_METHODS.has(method.toUpperCase());
|
|
@@ -31284,6 +31285,9 @@ var PROFILES = {
|
|
|
31284
31285
|
full: []
|
|
31285
31286
|
// empty = all groups
|
|
31286
31287
|
};
|
|
31288
|
+
function parseReadonlyFlag(value) {
|
|
31289
|
+
return value === "1" || value === "true";
|
|
31290
|
+
}
|
|
31287
31291
|
function filterTools(groups, options) {
|
|
31288
31292
|
const validNames = new Set(Object.keys(groups));
|
|
31289
31293
|
let profileGroups;
|
|
@@ -31304,7 +31308,7 @@ function filterTools(groups, options) {
|
|
|
31304
31308
|
const effectiveGroups = explicitTools2 ?? profileGroups ?? null;
|
|
31305
31309
|
const enabledGroups = effectiveGroups ? new Set(effectiveGroups) : null;
|
|
31306
31310
|
const unknownGroups2 = enabledGroups ? [...enabledGroups].filter((g) => !validNames.has(g)) : [];
|
|
31307
|
-
const readonly2 = options.readonly
|
|
31311
|
+
const readonly2 = parseReadonlyFlag(options.readonly);
|
|
31308
31312
|
const out = [];
|
|
31309
31313
|
for (const [name, tools] of Object.entries(groups)) {
|
|
31310
31314
|
if (enabledGroups && !enabledGroups.has(name)) continue;
|
|
@@ -31321,6 +31325,49 @@ function filterTools(groups, options) {
|
|
|
31321
31325
|
return result;
|
|
31322
31326
|
}
|
|
31323
31327
|
|
|
31328
|
+
// src/tools/status.ts
|
|
31329
|
+
function composeTailnetStatusData(devicesRes, settingsRes, extras = {}) {
|
|
31330
|
+
const { deviceCount: _deviceCount, settings: _settings, errors: _errors, ...safeExtras } = extras;
|
|
31331
|
+
const data = {
|
|
31332
|
+
...safeExtras,
|
|
31333
|
+
deviceCount: devicesRes.ok ? devicesRes.data?.devices?.length ?? null : null,
|
|
31334
|
+
settings: settingsRes.ok ? settingsRes.data : null
|
|
31335
|
+
};
|
|
31336
|
+
const errors = {};
|
|
31337
|
+
if (!devicesRes.ok) errors.devices = devicesRes.error ?? `HTTP ${devicesRes.status}`;
|
|
31338
|
+
if (!settingsRes.ok) errors.settings = settingsRes.error ?? `HTTP ${settingsRes.status}`;
|
|
31339
|
+
if (Object.keys(errors).length > 0) data.errors = errors;
|
|
31340
|
+
return data;
|
|
31341
|
+
}
|
|
31342
|
+
var statusTools = [
|
|
31343
|
+
{
|
|
31344
|
+
name: "tailscale_status",
|
|
31345
|
+
description: "Check that the Tailscale API connection is working. Returns your tailnet name, device count, and confirms authentication is valid. Use this to verify setup.",
|
|
31346
|
+
annotations: {
|
|
31347
|
+
title: "Check API status",
|
|
31348
|
+
readOnlyHint: true,
|
|
31349
|
+
destructiveHint: false,
|
|
31350
|
+
idempotentHint: true,
|
|
31351
|
+
openWorldHint: true
|
|
31352
|
+
},
|
|
31353
|
+
inputSchema: external_exports.object({}),
|
|
31354
|
+
handler: async () => {
|
|
31355
|
+
const [devicesRes, settingsRes] = await Promise.all([
|
|
31356
|
+
apiGet(`/tailnet/${getTailnet()}/devices?fields=id`),
|
|
31357
|
+
apiGet(`/tailnet/${getTailnet()}/settings`)
|
|
31358
|
+
]);
|
|
31359
|
+
if (!devicesRes.ok && !settingsRes.ok) {
|
|
31360
|
+
return devicesRes;
|
|
31361
|
+
}
|
|
31362
|
+
const data = composeTailnetStatusData(devicesRes, settingsRes, {
|
|
31363
|
+
connected: true,
|
|
31364
|
+
tailnet: getTailnet()
|
|
31365
|
+
});
|
|
31366
|
+
return { ok: true, status: 200, data };
|
|
31367
|
+
}
|
|
31368
|
+
}
|
|
31369
|
+
];
|
|
31370
|
+
|
|
31324
31371
|
// src/server-wiring.ts
|
|
31325
31372
|
function isLocalCliEnabled(env) {
|
|
31326
31373
|
return env.TAILSCALE_LOCAL_CLI === "1" || env.TAILSCALE_LOCAL_CLI === "true";
|
|
@@ -31370,19 +31417,7 @@ async function tailnetStatusResource(uri) {
|
|
|
31370
31417
|
apiGet(`/tailnet/${getTailnet()}/devices?fields=id`),
|
|
31371
31418
|
apiGet(`/tailnet/${getTailnet()}/settings`)
|
|
31372
31419
|
]);
|
|
31373
|
-
const data = {
|
|
31374
|
-
tailnet: getTailnet(),
|
|
31375
|
-
// `?? null` (not `?? 0`): the request succeeded but the body was missing
|
|
31376
|
-
// a `devices` array (204 / empty content-length / unexpected shape).
|
|
31377
|
-
// Reporting `0` in that case would be confidently wrong; null signals
|
|
31378
|
-
// "unknown" so the caller doesn't conflate it with an actually-empty tailnet.
|
|
31379
|
-
deviceCount: devicesRes.ok ? devicesRes.data?.devices?.length ?? null : null,
|
|
31380
|
-
settings: settingsRes.ok ? settingsRes.data : null
|
|
31381
|
-
};
|
|
31382
|
-
const errors = {};
|
|
31383
|
-
if (!devicesRes.ok) errors.devices = devicesRes.error ?? `HTTP ${devicesRes.status}`;
|
|
31384
|
-
if (!settingsRes.ok) errors.settings = settingsRes.error ?? `HTTP ${settingsRes.status}`;
|
|
31385
|
-
if (Object.keys(errors).length > 0) data.errors = errors;
|
|
31420
|
+
const data = composeTailnetStatusData(devicesRes, settingsRes, { tailnet: getTailnet() });
|
|
31386
31421
|
return { contents: [{ uri: uri.href, text: JSON.stringify(data, null, 2), mimeType: "application/json" }] };
|
|
31387
31422
|
}
|
|
31388
31423
|
async function tailnetDevicesResource(uri) {
|
|
@@ -31615,8 +31650,8 @@ function isCidr(s) {
|
|
|
31615
31650
|
if (slash < 0) return false;
|
|
31616
31651
|
const addr = s.slice(0, slash);
|
|
31617
31652
|
const prefix = s.slice(slash + 1);
|
|
31653
|
+
if (!/^\d+$/.test(prefix)) return false;
|
|
31618
31654
|
const prefixN = Number(prefix);
|
|
31619
|
-
if (!Number.isInteger(prefixN) || prefixN < 0) return false;
|
|
31620
31655
|
if (net.isIPv4(addr)) return prefixN <= 32;
|
|
31621
31656
|
if (net.isIPv6(addr)) return prefixN <= 128;
|
|
31622
31657
|
return false;
|
|
@@ -33196,46 +33231,6 @@ var serviceTools = [
|
|
|
33196
33231
|
}
|
|
33197
33232
|
];
|
|
33198
33233
|
|
|
33199
|
-
// src/tools/status.ts
|
|
33200
|
-
var statusTools = [
|
|
33201
|
-
{
|
|
33202
|
-
name: "tailscale_status",
|
|
33203
|
-
description: "Check that the Tailscale API connection is working. Returns your tailnet name, device count, and confirms authentication is valid. Use this to verify setup.",
|
|
33204
|
-
annotations: {
|
|
33205
|
-
title: "Check API status",
|
|
33206
|
-
readOnlyHint: true,
|
|
33207
|
-
destructiveHint: false,
|
|
33208
|
-
idempotentHint: true,
|
|
33209
|
-
openWorldHint: true
|
|
33210
|
-
},
|
|
33211
|
-
inputSchema: external_exports.object({}),
|
|
33212
|
-
handler: async () => {
|
|
33213
|
-
const [devicesRes, settingsRes] = await Promise.all([
|
|
33214
|
-
apiGet(`/tailnet/${getTailnet()}/devices?fields=id`),
|
|
33215
|
-
apiGet(`/tailnet/${getTailnet()}/settings`)
|
|
33216
|
-
]);
|
|
33217
|
-
if (!devicesRes.ok && !settingsRes.ok) {
|
|
33218
|
-
return devicesRes;
|
|
33219
|
-
}
|
|
33220
|
-
const data = {
|
|
33221
|
-
connected: true,
|
|
33222
|
-
tailnet: getTailnet(),
|
|
33223
|
-
// `?? null` (not `?? 0`): the request succeeded but the body was missing
|
|
33224
|
-
// a `devices` array (204 / empty content-length / unexpected shape).
|
|
33225
|
-
// Reporting `0` in that case would be confidently wrong; null signals
|
|
33226
|
-
// "unknown" so the caller doesn't conflate it with an actually-empty tailnet.
|
|
33227
|
-
deviceCount: devicesRes.ok ? devicesRes.data?.devices?.length ?? null : null,
|
|
33228
|
-
settings: settingsRes.ok ? settingsRes.data : null
|
|
33229
|
-
};
|
|
33230
|
-
const errors = {};
|
|
33231
|
-
if (!devicesRes.ok) errors.devices = devicesRes.error ?? `HTTP ${devicesRes.status}`;
|
|
33232
|
-
if (!settingsRes.ok) errors.settings = settingsRes.error ?? `HTTP ${settingsRes.status}`;
|
|
33233
|
-
if (Object.keys(errors).length > 0) data.errors = errors;
|
|
33234
|
-
return { ok: true, status: 200, data };
|
|
33235
|
-
}
|
|
33236
|
-
}
|
|
33237
|
-
];
|
|
33238
|
-
|
|
33239
33234
|
// src/tools/tailnet.ts
|
|
33240
33235
|
var tailnetTools = [
|
|
33241
33236
|
{
|
|
@@ -33679,7 +33674,7 @@ var webhookTools = [
|
|
|
33679
33674
|
];
|
|
33680
33675
|
|
|
33681
33676
|
// src/index.ts
|
|
33682
|
-
var version2 = true ? "0.12.
|
|
33677
|
+
var version2 = true ? "0.12.3" : (await null).createRequire(import.meta.url)("../package.json").version;
|
|
33683
33678
|
var subcommand = process.argv[2];
|
|
33684
33679
|
if (subcommand === "deploy-acl") {
|
|
33685
33680
|
const filePath = process.argv[3];
|
|
@@ -33773,7 +33768,7 @@ server.resource(
|
|
|
33773
33768
|
);
|
|
33774
33769
|
var transport = new StdioServerTransport();
|
|
33775
33770
|
await server.connect(transport);
|
|
33776
|
-
var readonlyMode = process.env.TAILSCALE_READONLY
|
|
33771
|
+
var readonlyMode = parseReadonlyFlag(process.env.TAILSCALE_READONLY);
|
|
33777
33772
|
var filterSuffix = formatBannerFilterSuffix({
|
|
33778
33773
|
unknownProfile,
|
|
33779
33774
|
explicitTools,
|
|
@@ -33787,8 +33782,11 @@ console.error(
|
|
|
33787
33782
|
);
|
|
33788
33783
|
var hasCreds = !!process.env.TAILSCALE_API_KEY || !!process.env.TAILSCALE_OAUTH_CLIENT_ID && !!process.env.TAILSCALE_OAUTH_CLIENT_SECRET;
|
|
33789
33784
|
if (!filterSuffix && hasCreds) {
|
|
33785
|
+
const profileCount = (groups) => groups.reduce((n, g) => n + (toolGroups[g]?.length ?? 0), 0);
|
|
33786
|
+
const coreCount = profileCount(PROFILES.core);
|
|
33787
|
+
const minimalCount = profileCount(PROFILES.minimal);
|
|
33790
33788
|
console.error(
|
|
33791
|
-
|
|
33789
|
+
`@yawlabs/tailscale-mcp: tip \u2014 set TAILSCALE_PROFILE=core (${coreCount} tools) or =minimal (${minimalCount}) to load a smaller tool surface. See README.`
|
|
33792
33790
|
);
|
|
33793
33791
|
}
|
|
33794
33792
|
//# sourceMappingURL=index.js.map
|