@yawlabs/tailscale-mcp 0.1.2 → 0.1.4

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
@@ -4,7 +4,7 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
  [![GitHub stars](https://img.shields.io/github/stars/YawLabs/tailscale-mcp)](https://github.com/YawLabs/tailscale-mcp/stargazers)
6
6
 
7
- **Manage your Tailscale tailnet from Claude Code, Cursor, and any MCP client.** 43 tools. One env var. Works on first try.
7
+ **Manage your Tailscale tailnet from Claude Code, Cursor, and any MCP client.** 52 tools. One env var. Works on first try.
8
8
 
9
9
  Built and maintained by [YawLabs](https://yaw.sh).
10
10
 
@@ -72,7 +72,7 @@ The server checks for an API key first, then falls back to OAuth. If neither is
72
72
 
73
73
  **Tailnet:** Uses your default tailnet automatically. Set `TAILSCALE_TAILNET` to specify one explicitly.
74
74
 
75
- ## Tools (43)
75
+ ## Tools (52)
76
76
 
77
77
  ### Status
78
78
 
@@ -81,7 +81,7 @@ The server checks for an API key first, then falls back to OAuth. If neither is
81
81
  | `tailscale_status` | Verify API connection, see tailnet info and device count |
82
82
 
83
83
  <details>
84
- <summary><strong>Devices</strong> (10 tools)</summary>
84
+ <summary><strong>Devices</strong> (13 tools)</summary>
85
85
 
86
86
  | Tool | Description |
87
87
  |------|-------------|
@@ -94,6 +94,9 @@ The server checks for an API key first, then falls back to OAuth. If neither is
94
94
  | `tailscale_expire_device` | Expire a device's key, forcing re-authentication |
95
95
  | `tailscale_get_device_routes` | Get advertised and enabled subnet routes |
96
96
  | `tailscale_set_device_routes` | Enable or disable subnet routes |
97
+ | `tailscale_get_device_posture_attributes` | Get all posture attributes for a device |
98
+ | `tailscale_set_device_posture_attribute` | Set a custom posture attribute (with optional expiry) |
99
+ | `tailscale_delete_device_posture_attribute` | Delete a custom posture attribute |
97
100
  | `tailscale_set_device_tags` | Set ACL tags on a device |
98
101
 
99
102
  </details>
@@ -139,17 +142,21 @@ The server checks for an API key first, then falls back to OAuth. If neither is
139
142
  </details>
140
143
 
141
144
  <details>
142
- <summary><strong>Users</strong> (2 tools)</summary>
145
+ <summary><strong>Users</strong> (6 tools)</summary>
143
146
 
144
147
  | Tool | Description |
145
148
  |------|-------------|
146
149
  | `tailscale_list_users` | List all users in the tailnet |
147
150
  | `tailscale_get_user` | Get details for a specific user |
151
+ | `tailscale_approve_user` | Approve a pending user |
152
+ | `tailscale_suspend_user` | Suspend a user, revoking access |
153
+ | `tailscale_restore_user` | Restore a suspended user |
154
+ | `tailscale_update_user_role` | Update a user's role (owner, admin, member, etc.) |
148
155
 
149
156
  </details>
150
157
 
151
158
  <details>
152
- <summary><strong>Tailnet Settings</strong> (5 tools)</summary>
159
+ <summary><strong>Tailnet Settings</strong> (4 tools)</summary>
153
160
 
154
161
  | Tool | Description |
155
162
  |------|-------------|
@@ -157,18 +164,27 @@ The server checks for an API key first, then falls back to OAuth. If neither is
157
164
  | `tailscale_update_tailnet_settings` | Update tailnet settings |
158
165
  | `tailscale_get_contacts` | Get tailnet contacts |
159
166
  | `tailscale_set_contacts` | Set tailnet contacts |
160
- | `tailscale_get_tailnet_keys` | Get auth keys and tailnet lock signing key info |
161
167
 
162
168
  </details>
163
169
 
164
170
  <details>
165
- <summary><strong>Webhooks</strong> (4 tools)</summary>
171
+ <summary><strong>Network Lock</strong> (1 tool)</summary>
172
+
173
+ | Tool | Description |
174
+ |------|-------------|
175
+ | `tailscale_get_network_lock_status` | Get tailnet lock status and trusted signing keys |
176
+
177
+ </details>
178
+
179
+ <details>
180
+ <summary><strong>Webhooks</strong> (5 tools)</summary>
166
181
 
167
182
  | Tool | Description |
168
183
  |------|-------------|
169
184
  | `tailscale_list_webhooks` | List webhooks |
170
185
  | `tailscale_get_webhook` | Get a specific webhook |
171
186
  | `tailscale_create_webhook` | Create a webhook |
187
+ | `tailscale_update_webhook` | Update a webhook's endpoint URL and/or subscriptions |
172
188
  | `tailscale_delete_webhook` | Delete a webhook |
173
189
 
174
190
  </details>
@@ -186,11 +202,12 @@ The server checks for an API key first, then falls back to OAuth. If neither is
186
202
  </details>
187
203
 
188
204
  <details>
189
- <summary><strong>Audit Log</strong> (1 tool)</summary>
205
+ <summary><strong>Logging</strong> (2 tools)</summary>
190
206
 
191
207
  | Tool | Description |
192
208
  |------|-------------|
193
209
  | `tailscale_get_audit_log` | Get configuration audit log (who changed what, when) |
210
+ | `tailscale_get_network_flow_logs` | Get network traffic flow logs between devices |
194
211
 
195
212
  </details>
196
213
 
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ const allTools = [
27
27
  ];
28
28
  const server = new McpServer({
29
29
  name: "@yawlabs/tailscale-mcp",
30
- version: "0.1.2",
30
+ version: "0.1.4",
31
31
  });
32
32
  for (const tool of allTools) {
33
33
  server.tool(tool.name, tool.description, tool.inputSchema.shape, async (input) => {
@@ -16,4 +16,21 @@ export declare const auditTools: readonly [{
16
16
  start: string;
17
17
  end?: string;
18
18
  }) => Promise<import("../api.js").ApiResponse<unknown>>;
19
+ }, {
20
+ readonly name: "tailscale_get_network_flow_logs";
21
+ readonly description: "Get network traffic flow logs showing connections between devices. Shows source/destination nodes, timestamps, and traffic metadata — useful for security monitoring and debugging connectivity.";
22
+ readonly inputSchema: z.ZodObject<{
23
+ start: z.ZodString;
24
+ end: z.ZodOptional<z.ZodString>;
25
+ }, "strip", z.ZodTypeAny, {
26
+ start: string;
27
+ end?: string | undefined;
28
+ }, {
29
+ start: string;
30
+ end?: string | undefined;
31
+ }>;
32
+ readonly handler: (input: {
33
+ start: string;
34
+ end?: string;
35
+ }) => Promise<import("../api.js").ApiResponse<unknown>>;
19
36
  }];
@@ -20,5 +20,24 @@ export const auditTools = [
20
20
  return apiGet(`/tailnet/${getTailnet()}/logging/configuration?${params}`);
21
21
  },
22
22
  },
23
+ {
24
+ name: "tailscale_get_network_flow_logs",
25
+ description: "Get network traffic flow logs showing connections between devices. Shows source/destination nodes, timestamps, and traffic metadata — useful for security monitoring and debugging connectivity.",
26
+ inputSchema: z.object({
27
+ start: z
28
+ .string()
29
+ .describe("Start time in RFC3339 format (e.g. '2026-04-01T00:00:00Z'). Required."),
30
+ end: z
31
+ .string()
32
+ .optional()
33
+ .describe("End time in RFC3339 format. Defaults to now."),
34
+ }),
35
+ handler: async (input) => {
36
+ const params = new URLSearchParams({ start: input.start });
37
+ if (input.end)
38
+ params.set("end", input.end);
39
+ return apiGet(`/tailnet/${getTailnet()}/logging/network?${params}`);
40
+ },
41
+ },
23
42
  ];
24
43
  //# sourceMappingURL=audit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/tools/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB;QACE,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EACT,uHAAuH;QACzH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,CAAC,uEAAuE,CAAC;YACpF,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,8CAA8C,CAAC;SAC5D,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAsC,EAAE,EAAE;YACxD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC3D,IAAI,KAAK,CAAC,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5C,OAAO,MAAM,CACX,YAAY,UAAU,EAAE,0BAA0B,MAAM,EAAE,CAC3D,CAAC;QACJ,CAAC;KACF;CACO,CAAC"}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/tools/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB;QACE,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EACT,uHAAuH;QACzH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,CAAC,uEAAuE,CAAC;YACpF,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,8CAA8C,CAAC;SAC5D,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAsC,EAAE,EAAE;YACxD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC3D,IAAI,KAAK,CAAC,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5C,OAAO,MAAM,CACX,YAAY,UAAU,EAAE,0BAA0B,MAAM,EAAE,CAC3D,CAAC;QACJ,CAAC;KACF;IACD;QACE,IAAI,EAAE,iCAAiC;QACvC,WAAW,EACT,kMAAkM;QACpM,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,CAAC,uEAAuE,CAAC;YACpF,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,8CAA8C,CAAC;SAC5D,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAsC,EAAE,EAAE;YACxD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC3D,IAAI,KAAK,CAAC,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YAC5C,OAAO,MAAM,CACX,YAAY,UAAU,EAAE,oBAAoB,MAAM,EAAE,CACrD,CAAC;QACJ,CAAC;KACF;CACO,CAAC"}
@@ -124,6 +124,61 @@ export declare const deviceTools: readonly [{
124
124
  deviceId: string;
125
125
  routes: string[];
126
126
  }) => Promise<import("../api.js").ApiResponse<unknown>>;
127
+ }, {
128
+ readonly name: "tailscale_get_device_posture_attributes";
129
+ readonly description: "Get all posture attributes for a device, including custom and system-managed attributes.";
130
+ readonly inputSchema: z.ZodObject<{
131
+ deviceId: z.ZodString;
132
+ }, "strip", z.ZodTypeAny, {
133
+ deviceId: string;
134
+ }, {
135
+ deviceId: string;
136
+ }>;
137
+ readonly handler: (input: {
138
+ deviceId: string;
139
+ }) => Promise<import("../api.js").ApiResponse<unknown>>;
140
+ }, {
141
+ readonly name: "tailscale_set_device_posture_attribute";
142
+ readonly description: "Set a custom posture attribute on a device. Attribute keys must start with 'custom:'. Useful for compliance tracking, JIT access, and custom security policies.";
143
+ readonly inputSchema: z.ZodObject<{
144
+ deviceId: z.ZodString;
145
+ attributeKey: z.ZodString;
146
+ value: z.ZodString;
147
+ expiry: z.ZodOptional<z.ZodString>;
148
+ }, "strip", z.ZodTypeAny, {
149
+ value: string;
150
+ deviceId: string;
151
+ attributeKey: string;
152
+ expiry?: string | undefined;
153
+ }, {
154
+ value: string;
155
+ deviceId: string;
156
+ attributeKey: string;
157
+ expiry?: string | undefined;
158
+ }>;
159
+ readonly handler: (input: {
160
+ deviceId: string;
161
+ attributeKey: string;
162
+ value: string;
163
+ expiry?: string;
164
+ }) => Promise<import("../api.js").ApiResponse<unknown>>;
165
+ }, {
166
+ readonly name: "tailscale_delete_device_posture_attribute";
167
+ readonly description: "Delete a custom posture attribute from a device.";
168
+ readonly inputSchema: z.ZodObject<{
169
+ deviceId: z.ZodString;
170
+ attributeKey: z.ZodString;
171
+ }, "strip", z.ZodTypeAny, {
172
+ deviceId: string;
173
+ attributeKey: string;
174
+ }, {
175
+ deviceId: string;
176
+ attributeKey: string;
177
+ }>;
178
+ readonly handler: (input: {
179
+ deviceId: string;
180
+ attributeKey: string;
181
+ }) => Promise<import("../api.js").ApiResponse<unknown>>;
127
182
  }, {
128
183
  readonly name: "tailscale_set_device_tags";
129
184
  readonly description: "Set ACL tags on a device. Replaces all existing tags.";
@@ -97,6 +97,43 @@ export const deviceTools = [
97
97
  return apiPost(`/device/${input.deviceId}/routes`, { routes: input.routes });
98
98
  },
99
99
  },
100
+ {
101
+ name: "tailscale_get_device_posture_attributes",
102
+ description: "Get all posture attributes for a device, including custom and system-managed attributes.",
103
+ inputSchema: z.object({
104
+ deviceId: z.string().describe("The device ID"),
105
+ }),
106
+ handler: async (input) => {
107
+ return apiGet(`/device/${input.deviceId}/attributes`);
108
+ },
109
+ },
110
+ {
111
+ name: "tailscale_set_device_posture_attribute",
112
+ description: "Set a custom posture attribute on a device. Attribute keys must start with 'custom:'. Useful for compliance tracking, JIT access, and custom security policies.",
113
+ inputSchema: z.object({
114
+ deviceId: z.string().describe("The device ID"),
115
+ attributeKey: z.string().describe("The attribute key (must start with 'custom:', e.g. 'custom:lastAuditDate')"),
116
+ value: z.string().describe("The attribute value"),
117
+ expiry: z.string().optional().describe("Optional expiry time in RFC3339 format (e.g. '2026-12-01T00:00:00Z'). Attribute is automatically removed after expiry."),
118
+ }),
119
+ handler: async (input) => {
120
+ const body = { value: input.value };
121
+ if (input.expiry !== undefined)
122
+ body.expiry = input.expiry;
123
+ return apiPost(`/device/${input.deviceId}/attributes/${input.attributeKey}`, body);
124
+ },
125
+ },
126
+ {
127
+ name: "tailscale_delete_device_posture_attribute",
128
+ description: "Delete a custom posture attribute from a device.",
129
+ inputSchema: z.object({
130
+ deviceId: z.string().describe("The device ID"),
131
+ attributeKey: z.string().describe("The attribute key to delete (e.g. 'custom:lastAuditDate')"),
132
+ }),
133
+ handler: async (input) => {
134
+ return apiDelete(`/device/${input.deviceId}/attributes/${input.attributeKey}`);
135
+ },
136
+ },
100
137
  {
101
138
  name: "tailscale_set_device_tags",
102
139
  description: "Set ACL tags on a device. Replaces all existing tags.",
@@ -1 +1 @@
1
- {"version":3,"file":"devices.js","sourceRoot":"","sources":["../../src/tools/devices.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"devices.js","sourceRoot":"","sources":["../../src/tools/devices.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ export declare const statusTools: readonly [{
9
9
  ok: boolean;
10
10
  status: number;
11
11
  data: {
12
+ settingsError?: string | undefined;
12
13
  connected: boolean;
13
14
  tailnet: string;
14
15
  deviceCount: number;
@@ -22,6 +22,7 @@ export const statusTools = [
22
22
  tailnet: getTailnet(),
23
23
  deviceCount,
24
24
  settings: settingsRes.ok ? settingsRes.data : undefined,
25
+ ...(settingsRes.ok ? {} : { settingsError: settingsRes.error || "Failed to fetch tailnet settings" }),
25
26
  },
26
27
  };
27
28
  },
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB;QACE,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,8JAA8J;QAChK,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAClD,MAAM,CAAyB,YAAY,UAAU,EAAE,oBAAoB,CAAC;gBAC5E,MAAM,CAA0B,YAAY,UAAU,EAAE,WAAW,CAAC;aACrE,CAAC,CAAC;YAEH,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;gBACnB,OAAO,UAAU,CAAC;YACpB,CAAC;YACD,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;YAE1D,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE;oBACJ,SAAS,EAAE,IAAI;oBACf,OAAO,EAAE,UAAU,EAAE;oBACrB,WAAW;oBACX,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;iBACxD;aACF,CAAC;QACJ,CAAC;KACF;CACO,CAAC"}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB;QACE,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,8JAA8J;QAChK,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAClD,MAAM,CAAyB,YAAY,UAAU,EAAE,oBAAoB,CAAC;gBAC5E,MAAM,CAA0B,YAAY,UAAU,EAAE,WAAW,CAAC;aACrE,CAAC,CAAC;YAEH,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;gBACnB,OAAO,UAAU,CAAC;YACpB,CAAC;YACD,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;YAE1D,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE;oBACJ,SAAS,EAAE,IAAI;oBACf,OAAO,EAAE,UAAU,EAAE;oBACrB,WAAW;oBACX,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;oBACvD,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,WAAW,CAAC,KAAK,IAAI,kCAAkC,EAAE,CAAC;iBACtG;aACF,CAAC;QACJ,CAAC;KACF;CACO,CAAC"}
@@ -17,4 +17,60 @@ export declare const userTools: readonly [{
17
17
  readonly handler: (input: {
18
18
  userId: string;
19
19
  }) => Promise<import("../api.js").ApiResponse<unknown>>;
20
+ }, {
21
+ readonly name: "tailscale_approve_user";
22
+ readonly description: "Approve a pending user, granting them access to the tailnet.";
23
+ readonly inputSchema: z.ZodObject<{
24
+ userId: z.ZodString;
25
+ }, "strip", z.ZodTypeAny, {
26
+ userId: string;
27
+ }, {
28
+ userId: string;
29
+ }>;
30
+ readonly handler: (input: {
31
+ userId: string;
32
+ }) => Promise<import("../api.js").ApiResponse<unknown>>;
33
+ }, {
34
+ readonly name: "tailscale_suspend_user";
35
+ readonly description: "Suspend a user, immediately revoking their access to the tailnet. Their devices will be disconnected.";
36
+ readonly inputSchema: z.ZodObject<{
37
+ userId: z.ZodString;
38
+ }, "strip", z.ZodTypeAny, {
39
+ userId: string;
40
+ }, {
41
+ userId: string;
42
+ }>;
43
+ readonly handler: (input: {
44
+ userId: string;
45
+ }) => Promise<import("../api.js").ApiResponse<unknown>>;
46
+ }, {
47
+ readonly name: "tailscale_restore_user";
48
+ readonly description: "Restore a previously suspended user, re-granting them access to the tailnet.";
49
+ readonly inputSchema: z.ZodObject<{
50
+ userId: z.ZodString;
51
+ }, "strip", z.ZodTypeAny, {
52
+ userId: string;
53
+ }, {
54
+ userId: string;
55
+ }>;
56
+ readonly handler: (input: {
57
+ userId: string;
58
+ }) => Promise<import("../api.js").ApiResponse<unknown>>;
59
+ }, {
60
+ readonly name: "tailscale_update_user_role";
61
+ readonly description: "Update a user's role in the tailnet.";
62
+ readonly inputSchema: z.ZodObject<{
63
+ userId: z.ZodString;
64
+ role: z.ZodEnum<["owner", "admin", "it-admin", "network-admin", "billing-admin", "auditor", "member"]>;
65
+ }, "strip", z.ZodTypeAny, {
66
+ userId: string;
67
+ role: "owner" | "admin" | "it-admin" | "network-admin" | "billing-admin" | "auditor" | "member";
68
+ }, {
69
+ userId: string;
70
+ role: "owner" | "admin" | "it-admin" | "network-admin" | "billing-admin" | "auditor" | "member";
71
+ }>;
72
+ readonly handler: (input: {
73
+ userId: string;
74
+ role: string;
75
+ }) => Promise<import("../api.js").ApiResponse<unknown>>;
20
76
  }];
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { apiGet, getTailnet } from "../api.js";
2
+ import { apiGet, apiPost, apiPatch, getTailnet } from "../api.js";
3
3
  export const userTools = [
4
4
  {
5
5
  name: "tailscale_list_users",
@@ -19,5 +19,47 @@ export const userTools = [
19
19
  return apiGet(`/users/${input.userId}`);
20
20
  },
21
21
  },
22
+ {
23
+ name: "tailscale_approve_user",
24
+ description: "Approve a pending user, granting them access to the tailnet.",
25
+ inputSchema: z.object({
26
+ userId: z.string().describe("The user ID to approve"),
27
+ }),
28
+ handler: async (input) => {
29
+ return apiPost(`/users/${input.userId}/approve`);
30
+ },
31
+ },
32
+ {
33
+ name: "tailscale_suspend_user",
34
+ description: "Suspend a user, immediately revoking their access to the tailnet. Their devices will be disconnected.",
35
+ inputSchema: z.object({
36
+ userId: z.string().describe("The user ID to suspend"),
37
+ }),
38
+ handler: async (input) => {
39
+ return apiPost(`/users/${input.userId}/suspend`);
40
+ },
41
+ },
42
+ {
43
+ name: "tailscale_restore_user",
44
+ description: "Restore a previously suspended user, re-granting them access to the tailnet.",
45
+ inputSchema: z.object({
46
+ userId: z.string().describe("The user ID to restore"),
47
+ }),
48
+ handler: async (input) => {
49
+ return apiPost(`/users/${input.userId}/restore`);
50
+ },
51
+ },
52
+ {
53
+ name: "tailscale_update_user_role",
54
+ description: "Update a user's role in the tailnet.",
55
+ inputSchema: z.object({
56
+ userId: z.string().describe("The user ID"),
57
+ role: z.enum(["owner", "admin", "it-admin", "network-admin", "billing-admin", "auditor", "member"])
58
+ .describe("The new role to assign"),
59
+ }),
60
+ handler: async (input) => {
61
+ return apiPatch(`/users/${input.userId}/role`, { role: input.role });
62
+ },
63
+ },
22
64
  ];
23
65
  //# sourceMappingURL=users.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/tools/users.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,iCAAiC;QAC9C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,OAAO,MAAM,CAAC,YAAY,UAAU,EAAE,QAAQ,CAAC,CAAC;QAClD,CAAC;KACF;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,kCAAkC;QAC/C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;SAC3C,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAyB,EAAE,EAAE;YAC3C,OAAO,MAAM,CAAC,UAAU,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1C,CAAC;KACF;CACO,CAAC"}
1
+ {"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/tools/users.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAElE,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,iCAAiC;QAC9C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,OAAO,MAAM,CAAC,YAAY,UAAU,EAAE,QAAQ,CAAC,CAAC;QAClD,CAAC;KACF;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,kCAAkC;QAC/C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;SAC3C,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAyB,EAAE,EAAE;YAC3C,OAAO,MAAM,CAAC,UAAU,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1C,CAAC;KACF;IACD;QACE,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,8DAA8D;QAC3E,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;SACtD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAyB,EAAE,EAAE;YAC3C,OAAO,OAAO,CAAC,UAAU,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC;QACnD,CAAC;KACF;IACD;QACE,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,uGAAuG;QACpH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;SACtD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAyB,EAAE,EAAE;YAC3C,OAAO,OAAO,CAAC,UAAU,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC;QACnD,CAAC;KACF;IACD;QACE,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,8EAA8E;QAC3F,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;SACtD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAyB,EAAE,EAAE;YAC3C,OAAO,OAAO,CAAC,UAAU,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC;QACnD,CAAC;KACF;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,WAAW,EAAE,sCAAsC;QACnD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC1C,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;iBAChG,QAAQ,CAAC,wBAAwB,CAAC;SACtC,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAuC,EAAE,EAAE;YACzD,OAAO,QAAQ,CAAC,UAAU,KAAK,CAAC,MAAM,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;KACF;CACO,CAAC"}
@@ -36,20 +36,24 @@ export declare const webhookTools: readonly [{
36
36
  }) => Promise<import("../api.js").ApiResponse<unknown>>;
37
37
  }, {
38
38
  readonly name: "tailscale_update_webhook";
39
- readonly description: "Update an existing webhook's subscriptions.";
39
+ readonly description: "Update an existing webhook's endpoint URL and/or subscriptions.";
40
40
  readonly inputSchema: z.ZodObject<{
41
41
  webhookId: z.ZodString;
42
- subscriptions: z.ZodArray<z.ZodString, "many">;
42
+ endpointUrl: z.ZodOptional<z.ZodString>;
43
+ subscriptions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
43
44
  }, "strip", z.ZodTypeAny, {
44
45
  webhookId: string;
45
- subscriptions: string[];
46
+ endpointUrl?: string | undefined;
47
+ subscriptions?: string[] | undefined;
46
48
  }, {
47
49
  webhookId: string;
48
- subscriptions: string[];
50
+ endpointUrl?: string | undefined;
51
+ subscriptions?: string[] | undefined;
49
52
  }>;
50
53
  readonly handler: (input: {
51
54
  webhookId: string;
52
- subscriptions: string[];
55
+ endpointUrl?: string;
56
+ subscriptions?: string[];
53
57
  }) => Promise<import("../api.js").ApiResponse<unknown>>;
54
58
  }, {
55
59
  readonly name: "tailscale_delete_webhook";
@@ -37,17 +37,22 @@ export const webhookTools = [
37
37
  },
38
38
  {
39
39
  name: "tailscale_update_webhook",
40
- description: "Update an existing webhook's subscriptions.",
40
+ description: "Update an existing webhook's endpoint URL and/or subscriptions.",
41
41
  inputSchema: z.object({
42
42
  webhookId: z.string().describe("The webhook ID to update"),
43
+ endpointUrl: z.string().optional().describe("New URL to send webhook events to"),
43
44
  subscriptions: z
44
45
  .array(z.string())
46
+ .optional()
45
47
  .describe("Updated list of event types to subscribe to (e.g. ['nodeCreated', 'nodeDeleted', 'nodeApproved', 'policyUpdate', 'userCreated', 'userDeleted'])"),
46
48
  }),
47
49
  handler: async (input) => {
48
- return apiPatch(`/webhooks/${input.webhookId}`, {
49
- subscriptions: input.subscriptions,
50
- });
50
+ const body = {};
51
+ if (input.endpointUrl !== undefined)
52
+ body.endpointUrl = input.endpointUrl;
53
+ if (input.subscriptions !== undefined)
54
+ body.subscriptions = input.subscriptions;
55
+ return apiPatch(`/webhooks/${input.webhookId}`, body);
51
56
  },
52
57
  },
53
58
  {
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/tools/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE7E,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B;QACE,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EAAE,gDAAgD;QAC7D,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,OAAO,MAAM,CAAC,YAAY,UAAU,EAAE,WAAW,CAAC,CAAC;QACrD,CAAC;KACF;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,qCAAqC;QAClD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;SACjD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAA4B,EAAE,EAAE;YAC9C,OAAO,MAAM,CAAC,aAAa,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QAChD,CAAC;KACF;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,uBAAuB;QACpC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YACrE,aAAa,EAAE,CAAC;iBACb,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,CACP,iIAAiI,CAClI;SACJ,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAuD,EAAE,EAAE;YACzE,OAAO,OAAO,CAAC,YAAY,UAAU,EAAE,WAAW,EAAE;gBAClD,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;aACnC,CAAC,CAAC;QACL,CAAC;KACF;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,6CAA6C;QAC1D,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;YAC1D,aAAa,EAAE,CAAC;iBACb,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,CACP,iJAAiJ,CAClJ;SACJ,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAqD,EAAE,EAAE;YACvE,OAAO,QAAQ,CAAC,aAAa,KAAK,CAAC,SAAS,EAAE,EAAE;gBAC9C,aAAa,EAAE,KAAK,CAAC,aAAa;aACnC,CAAC,CAAC;QACL,CAAC;KACF;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,kFAAkF;QAC/F,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SAC3D,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAA4B,EAAE,EAAE;YAC9C,OAAO,SAAS,CAAC,aAAa,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QACnD,CAAC;KACF;CACO,CAAC"}
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/tools/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE7E,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B;QACE,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EAAE,gDAAgD;QAC7D,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,OAAO,MAAM,CAAC,YAAY,UAAU,EAAE,WAAW,CAAC,CAAC;QACrD,CAAC;KACF;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,qCAAqC;QAClD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;SACjD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAA4B,EAAE,EAAE;YAC9C,OAAO,MAAM,CAAC,aAAa,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QAChD,CAAC;KACF;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,uBAAuB;QACpC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YACrE,aAAa,EAAE,CAAC;iBACb,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,CACP,iIAAiI,CAClI;SACJ,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAuD,EAAE,EAAE;YACzE,OAAO,OAAO,CAAC,YAAY,UAAU,EAAE,WAAW,EAAE;gBAClD,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;aACnC,CAAC,CAAC;QACL,CAAC;KACF;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,iEAAiE;QAC9E,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;YAC1D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YAChF,aAAa,EAAE,CAAC;iBACb,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,EAAE;iBACV,QAAQ,CACP,iJAAiJ,CAClJ;SACJ,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAA4E,EAAE,EAAE;YAC9F,MAAM,IAAI,GAA4B,EAAE,CAAC;YACzC,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;gBAAE,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;YAC1E,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS;gBAAE,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;YAChF,OAAO,QAAQ,CAAC,aAAa,KAAK,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,CAAC;QACxD,CAAC;KACF;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,kFAAkF;QAC/F,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SAC3D,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAA4B,EAAE,EAAE;YAC9C,OAAO,SAAS,CAAC,aAAa,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QACnD,CAAC;KACF;CACO,CAAC"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@yawlabs/tailscale-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Tailscale MCP server for managing your tailnet from AI assistants",
5
5
  "license": "MIT",
6
6
  "author": "YawLabs <contact@yaw.sh>",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/YawLabs/tailscale-mcp"
9
+ "url": "git+https://github.com/YawLabs/tailscale-mcp.git"
10
10
  },
11
11
  "keywords": [
12
12
  "tailscale",
@@ -22,7 +22,8 @@
22
22
  "tailscale-mcp": "dist/index.js"
23
23
  },
24
24
  "files": [
25
- "dist"
25
+ "dist",
26
+ "!dist/**/*.test.*"
26
27
  ],
27
28
  "scripts": {
28
29
  "build": "tsc",
@@ -1 +0,0 @@
1
- export {};
package/dist/api.test.js DELETED
@@ -1,218 +0,0 @@
1
- import { describe, it, beforeEach, afterEach } from "node:test";
2
- import assert from "node:assert/strict";
3
- // We need to mock fetch before importing api module, so we use dynamic imports
4
- // and mock global.fetch
5
- function mockFetchResponse(status, body, headers) {
6
- const responseHeaders = new Headers(headers);
7
- return new Response(typeof body === "string" ? body : JSON.stringify(body), { status, headers: responseHeaders });
8
- }
9
- describe("API client", () => {
10
- const originalFetch = globalThis.fetch;
11
- const originalEnv = { ...process.env };
12
- let apiModule;
13
- beforeEach(async () => {
14
- // Reset env for each test
15
- process.env.TAILSCALE_API_KEY = "tskey-api-test123";
16
- delete process.env.TAILSCALE_OAUTH_CLIENT_ID;
17
- delete process.env.TAILSCALE_OAUTH_CLIENT_SECRET;
18
- process.env.TAILSCALE_TAILNET = "test.tailnet.ts.net";
19
- // Dynamic import to get fresh module reference
20
- apiModule = await import("./api.js");
21
- });
22
- afterEach(() => {
23
- globalThis.fetch = originalFetch;
24
- Object.keys(process.env).forEach((key) => {
25
- if (!(key in originalEnv))
26
- delete process.env[key];
27
- else
28
- process.env[key] = originalEnv[key];
29
- });
30
- });
31
- describe("getTailnet", () => {
32
- it("should return the configured tailnet", () => {
33
- process.env.TAILSCALE_TAILNET = "my.tailnet.ts.net";
34
- assert.equal(apiModule.getTailnet(), "my.tailnet.ts.net");
35
- });
36
- it("should return '-' when no tailnet is configured", () => {
37
- delete process.env.TAILSCALE_TAILNET;
38
- assert.equal(apiModule.getTailnet(), "-");
39
- });
40
- });
41
- describe("apiGet", () => {
42
- it("should make a GET request with correct auth header", async () => {
43
- let capturedRequest;
44
- globalThis.fetch = async (input, init) => {
45
- const url = typeof input === "string" ? input : input.toString();
46
- capturedRequest = { url, method: init?.method ?? "GET", headers: new Headers(init?.headers) };
47
- return mockFetchResponse(200, { devices: [] });
48
- };
49
- const res = await apiModule.apiGet("/tailnet/test/devices");
50
- assert.ok(res.ok);
51
- assert.equal(res.status, 200);
52
- assert.ok(capturedRequest);
53
- assert.equal(capturedRequest.url, "https://api.tailscale.com/api/v2/tailnet/test/devices");
54
- assert.ok(capturedRequest.headers.get("Authorization")?.startsWith("Basic "));
55
- });
56
- it("should return error on non-ok response", async () => {
57
- globalThis.fetch = async () => mockFetchResponse(403, "Forbidden");
58
- const res = await apiModule.apiGet("/tailnet/test/devices");
59
- assert.equal(res.ok, false);
60
- assert.equal(res.status, 403);
61
- assert.equal(res.error, "Forbidden");
62
- });
63
- it("should return raw body when acceptRaw is true", async () => {
64
- globalThis.fetch = async () => mockFetchResponse(200, "raw text content", { etag: '"abc123"' });
65
- const res = await apiModule.apiGet("/tailnet/test/acl", { acceptRaw: true });
66
- assert.ok(res.ok);
67
- assert.equal(res.rawBody, "raw text content");
68
- assert.equal(res.etag, '"abc123"');
69
- });
70
- it("should handle 204 no-content responses", async () => {
71
- globalThis.fetch = async () => new Response(null, { status: 204 });
72
- const res = await apiModule.apiGet("/some/endpoint");
73
- assert.ok(res.ok);
74
- assert.equal(res.status, 204);
75
- assert.equal(res.data, undefined);
76
- });
77
- it("should include etag in all response types", async () => {
78
- // JSON success
79
- globalThis.fetch = async () => mockFetchResponse(200, { ok: true }, { etag: '"json-etag"' });
80
- let res = await apiModule.apiGet("/test");
81
- assert.equal(res.etag, '"json-etag"');
82
- // Error response
83
- globalThis.fetch = async () => mockFetchResponse(500, "error", { etag: '"err-etag"' });
84
- res = await apiModule.apiGet("/test");
85
- assert.equal(res.etag, '"err-etag"');
86
- });
87
- });
88
- describe("apiPost", () => {
89
- it("should send JSON body", async () => {
90
- let capturedBody;
91
- let capturedContentType;
92
- globalThis.fetch = async (_input, init) => {
93
- capturedBody = init?.body;
94
- capturedContentType = new Headers(init?.headers).get("Content-Type") ?? undefined;
95
- return mockFetchResponse(200, { success: true });
96
- };
97
- await apiModule.apiPost("/test", { key: "value" });
98
- assert.equal(capturedContentType, "application/json");
99
- assert.equal(capturedBody, '{"key":"value"}');
100
- });
101
- it("should send raw body with custom content type", async () => {
102
- let capturedBody;
103
- let capturedContentType;
104
- globalThis.fetch = async (_input, init) => {
105
- capturedBody = init?.body;
106
- capturedContentType = new Headers(init?.headers).get("Content-Type") ?? undefined;
107
- return mockFetchResponse(200, { success: true });
108
- };
109
- await apiModule.apiPost("/test", undefined, {
110
- rawBody: '{ /* hujson */ }',
111
- contentType: "application/hujson",
112
- });
113
- assert.equal(capturedContentType, "application/hujson");
114
- assert.equal(capturedBody, '{ /* hujson */ }');
115
- });
116
- it("should send If-Match header when ifMatch is provided", async () => {
117
- let capturedHeaders;
118
- globalThis.fetch = async (_input, init) => {
119
- capturedHeaders = new Headers(init?.headers);
120
- return mockFetchResponse(200, { success: true });
121
- };
122
- await apiModule.apiPost("/test", undefined, {
123
- rawBody: "body",
124
- ifMatch: '"etag-value"',
125
- });
126
- assert.equal(capturedHeaders?.get("If-Match"), '"etag-value"');
127
- });
128
- });
129
- describe("apiPatch", () => {
130
- it("should make a PATCH request", async () => {
131
- let capturedMethod;
132
- globalThis.fetch = async (_input, init) => {
133
- capturedMethod = init?.method;
134
- return mockFetchResponse(200, { updated: true });
135
- };
136
- const res = await apiModule.apiPatch("/test", { field: "value" });
137
- assert.ok(res.ok);
138
- assert.equal(capturedMethod, "PATCH");
139
- });
140
- });
141
- describe("apiDelete", () => {
142
- it("should make a DELETE request", async () => {
143
- let capturedMethod;
144
- globalThis.fetch = async (_input, init) => {
145
- capturedMethod = init?.method;
146
- return mockFetchResponse(200, {});
147
- };
148
- const res = await apiModule.apiDelete("/test");
149
- assert.ok(res.ok);
150
- assert.equal(capturedMethod, "DELETE");
151
- });
152
- });
153
- describe("apiRequest with absolute URL", () => {
154
- it("should use absolute URL as-is without prepending base URL", async () => {
155
- let capturedUrl;
156
- globalThis.fetch = async (input, _init) => {
157
- capturedUrl = typeof input === "string" ? input : input.toString();
158
- return mockFetchResponse(200, {});
159
- };
160
- await apiModule.apiGet("https://custom.api.example.com/endpoint");
161
- assert.equal(capturedUrl, "https://custom.api.example.com/endpoint");
162
- });
163
- });
164
- describe("Authentication", () => {
165
- it("should throw when no credentials are configured", async () => {
166
- delete process.env.TAILSCALE_API_KEY;
167
- delete process.env.TAILSCALE_OAUTH_CLIENT_ID;
168
- delete process.env.TAILSCALE_OAUTH_CLIENT_SECRET;
169
- globalThis.fetch = async () => mockFetchResponse(200, {});
170
- await assert.rejects(() => apiModule.apiGet("/test"), { message: /No Tailscale credentials configured/ });
171
- });
172
- it("should use Basic auth with API key", async () => {
173
- process.env.TAILSCALE_API_KEY = "tskey-api-mykey";
174
- let capturedAuth;
175
- globalThis.fetch = async (_input, init) => {
176
- capturedAuth = new Headers(init?.headers).get("Authorization") ?? undefined;
177
- return mockFetchResponse(200, {});
178
- };
179
- await apiModule.apiGet("/test");
180
- const expected = `Basic ${Buffer.from("tskey-api-mykey:").toString("base64")}`;
181
- assert.equal(capturedAuth, expected);
182
- });
183
- it("should use Bearer auth with OAuth", async () => {
184
- delete process.env.TAILSCALE_API_KEY;
185
- process.env.TAILSCALE_OAUTH_CLIENT_ID = "client-id";
186
- process.env.TAILSCALE_OAUTH_CLIENT_SECRET = "client-secret";
187
- let callCount = 0;
188
- let capturedAuth;
189
- globalThis.fetch = async (input, init) => {
190
- callCount++;
191
- const url = typeof input === "string" ? input : input.toString();
192
- if (url.includes("/oauth/token")) {
193
- return mockFetchResponse(200, {
194
- access_token: "oauth-token-123",
195
- expires_in: 3600,
196
- });
197
- }
198
- capturedAuth = new Headers(init?.headers).get("Authorization") ?? undefined;
199
- return mockFetchResponse(200, {});
200
- };
201
- await apiModule.apiGet("/test");
202
- assert.equal(capturedAuth, "Bearer oauth-token-123");
203
- assert.equal(callCount, 2); // token + actual request
204
- });
205
- });
206
- describe("Request timeout", () => {
207
- it("should pass an AbortSignal to fetch", async () => {
208
- let capturedSignal;
209
- globalThis.fetch = async (_input, init) => {
210
- capturedSignal = init?.signal ?? undefined;
211
- return mockFetchResponse(200, {});
212
- };
213
- await apiModule.apiGet("/test");
214
- assert.ok(capturedSignal, "AbortSignal should be present");
215
- });
216
- });
217
- });
218
- //# sourceMappingURL=api.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.test.js","sourceRoot":"","sources":["../src/api.test.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,218 +0,0 @@
1
- import { describe, it, beforeEach, afterEach } from "node:test";
2
- import assert from "node:assert/strict";
3
- function mockFetchResponse(status, body, headers) {
4
- return new Response(typeof body === "string" ? body : JSON.stringify(body), { status, headers: new Headers(headers) });
5
- }
6
- describe("Tool handlers", () => {
7
- const originalFetch = globalThis.fetch;
8
- const originalEnv = { ...process.env };
9
- beforeEach(() => {
10
- process.env.TAILSCALE_API_KEY = "tskey-api-test";
11
- process.env.TAILSCALE_TAILNET = "test.ts.net";
12
- });
13
- afterEach(() => {
14
- globalThis.fetch = originalFetch;
15
- Object.keys(process.env).forEach((key) => {
16
- if (!(key in originalEnv))
17
- delete process.env[key];
18
- else
19
- process.env[key] = originalEnv[key];
20
- });
21
- });
22
- describe("tailscale_status", () => {
23
- it("should make parallel requests for devices and settings", async () => {
24
- const { statusTools } = await import("./tools/status.js");
25
- const urls = [];
26
- globalThis.fetch = async (input) => {
27
- const url = typeof input === "string" ? input : input.toString();
28
- urls.push(url);
29
- if (url.includes("/devices")) {
30
- return mockFetchResponse(200, { devices: [{ id: "1" }, { id: "2" }] });
31
- }
32
- return mockFetchResponse(200, { devicesApprovalOn: true });
33
- };
34
- const handler = statusTools[0].handler;
35
- const result = await handler();
36
- assert.ok(result.ok);
37
- assert.equal(result.data.deviceCount, 2);
38
- assert.equal(result.data.connected, true);
39
- assert.ok(urls.some((u) => u.includes("/devices")));
40
- assert.ok(urls.some((u) => u.includes("/settings")));
41
- });
42
- });
43
- describe("tailscale_list_devices", () => {
44
- it("should pass fields parameter when provided", async () => {
45
- const { deviceTools } = await import("./tools/devices.js");
46
- let capturedUrl = "";
47
- globalThis.fetch = async (input) => {
48
- capturedUrl = typeof input === "string" ? input : input.toString();
49
- return mockFetchResponse(200, { devices: [] });
50
- };
51
- const handler = deviceTools[0].handler;
52
- await handler({ fields: "id,name,addresses" });
53
- assert.ok(capturedUrl.includes("fields=id%2Cname%2Caddresses"));
54
- });
55
- it("should not include fields parameter when omitted", async () => {
56
- const { deviceTools } = await import("./tools/devices.js");
57
- let capturedUrl = "";
58
- globalThis.fetch = async (input) => {
59
- capturedUrl = typeof input === "string" ? input : input.toString();
60
- return mockFetchResponse(200, { devices: [] });
61
- };
62
- const handler = deviceTools[0].handler;
63
- await handler({});
64
- assert.ok(!capturedUrl.includes("fields="));
65
- });
66
- });
67
- describe("tailscale_get_acl", () => {
68
- it("should append ETag instructions to raw body", async () => {
69
- const { aclTools } = await import("./tools/acl.js");
70
- globalThis.fetch = async () => new Response('{ "acls": [] }', {
71
- status: 200,
72
- headers: { etag: '"acl-etag-1"' },
73
- });
74
- const handler = aclTools[0].handler;
75
- const result = await handler();
76
- assert.ok(result.ok);
77
- assert.ok(result.rawBody.includes('{ "acls": [] }'));
78
- assert.ok(result.rawBody.includes("ETag:"));
79
- assert.ok(result.rawBody.includes('"acl-etag-1"'));
80
- });
81
- });
82
- describe("tailscale_validate_acl", () => {
83
- it("should return friendly message on successful validation", async () => {
84
- const { aclTools } = await import("./tools/acl.js");
85
- globalThis.fetch = async () => new Response(null, { status: 200, headers: { "content-length": "0" } });
86
- const handler = aclTools[2].handler;
87
- const result = await handler({ policy: '{ "acls": [] }' });
88
- assert.ok(result.ok);
89
- assert.equal(result.data?.message, "ACL policy is valid.");
90
- });
91
- });
92
- describe("tailscale_update_acl", () => {
93
- it("should send raw HuJSON body with If-Match header", async () => {
94
- const { aclTools } = await import("./tools/acl.js");
95
- let capturedHeaders;
96
- let capturedBody;
97
- globalThis.fetch = async (_input, init) => {
98
- capturedHeaders = new Headers(init?.headers);
99
- capturedBody = init?.body;
100
- return mockFetchResponse(200, { success: true });
101
- };
102
- const handler = aclTools[1].handler;
103
- await handler({ policy: '{ /* hujson */ "acls": [] }', etag: '"etag-1"' });
104
- assert.equal(capturedHeaders?.get("If-Match"), '"etag-1"');
105
- assert.equal(capturedHeaders?.get("Content-Type"), "application/hujson");
106
- assert.equal(capturedBody, '{ /* hujson */ "acls": [] }');
107
- });
108
- });
109
- describe("tailscale_create_key", () => {
110
- it("should include expirySeconds when set to a number", async () => {
111
- const { keyTools } = await import("./tools/keys.js");
112
- let capturedBody;
113
- globalThis.fetch = async (_input, init) => {
114
- capturedBody = init?.body;
115
- return mockFetchResponse(200, { key: "tskey-auth-test" });
116
- };
117
- const handler = keyTools[2].handler;
118
- await handler({ expirySeconds: 3600, description: "test key" });
119
- const parsed = JSON.parse(capturedBody);
120
- assert.equal(parsed.expirySeconds, 3600);
121
- assert.equal(parsed.description, "test key");
122
- });
123
- it("should include description even if empty string", async () => {
124
- const { keyTools } = await import("./tools/keys.js");
125
- let capturedBody;
126
- globalThis.fetch = async (_input, init) => {
127
- capturedBody = init?.body;
128
- return mockFetchResponse(200, { key: "tskey-auth-test" });
129
- };
130
- const handler = keyTools[2].handler;
131
- await handler({ description: "" });
132
- const parsed = JSON.parse(capturedBody);
133
- assert.equal(parsed.description, "");
134
- });
135
- });
136
- describe("tailscale_update_tailnet_settings", () => {
137
- it("should only send defined fields to the API", async () => {
138
- const { tailnetTools } = await import("./tools/tailnet.js");
139
- let capturedBody;
140
- globalThis.fetch = async (_input, init) => {
141
- capturedBody = init?.body;
142
- return mockFetchResponse(200, { success: true });
143
- };
144
- // The update tool is index 1
145
- const handler = tailnetTools[1].handler;
146
- await handler({ devicesApprovalOn: true });
147
- const parsed = JSON.parse(capturedBody);
148
- assert.deepEqual(parsed, { devicesApprovalOn: true });
149
- assert.ok(!("devicesAutoUpdatesOn" in parsed));
150
- });
151
- });
152
- describe("tailscale_update_webhook", () => {
153
- it("should PATCH webhook subscriptions", async () => {
154
- const { webhookTools } = await import("./tools/webhooks.js");
155
- let capturedMethod;
156
- let capturedUrl;
157
- let capturedBody;
158
- globalThis.fetch = async (input, init) => {
159
- capturedUrl = typeof input === "string" ? input : input.toString();
160
- capturedMethod = init?.method;
161
- capturedBody = init?.body;
162
- return mockFetchResponse(200, { id: "wh-123" });
163
- };
164
- // The update tool is index 3
165
- const handler = webhookTools[3].handler;
166
- await handler({ webhookId: "wh-123", subscriptions: ["nodeCreated", "policyUpdate"] });
167
- assert.equal(capturedMethod, "PATCH");
168
- assert.ok(capturedUrl?.includes("/webhooks/wh-123"));
169
- const parsed = JSON.parse(capturedBody);
170
- assert.deepEqual(parsed.subscriptions, ["nodeCreated", "policyUpdate"]);
171
- });
172
- });
173
- describe("tailscale_get_network_lock_status", () => {
174
- it("should call the correct network-lock endpoint", async () => {
175
- const { networkLockTools } = await import("./tools/network-lock.js");
176
- let capturedUrl = "";
177
- globalThis.fetch = async (input) => {
178
- capturedUrl = typeof input === "string" ? input : input.toString();
179
- return mockFetchResponse(200, { enabled: false });
180
- };
181
- const handler = networkLockTools[0].handler;
182
- await handler();
183
- assert.ok(capturedUrl.includes("/network-lock/status"), `Expected URL to contain /network-lock/status, got: ${capturedUrl}`);
184
- });
185
- });
186
- describe("tailscale_get_audit_log", () => {
187
- it("should pass start and end params", async () => {
188
- const { auditTools } = await import("./tools/audit.js");
189
- let capturedUrl = "";
190
- globalThis.fetch = async (input) => {
191
- capturedUrl = typeof input === "string" ? input : input.toString();
192
- return mockFetchResponse(200, { logs: [] });
193
- };
194
- const handler = auditTools[0].handler;
195
- await handler({ start: "2026-01-01T00:00:00Z", end: "2026-01-31T23:59:59Z" });
196
- assert.ok(capturedUrl.includes("start=2026-01-01T00%3A00%3A00Z"));
197
- assert.ok(capturedUrl.includes("end=2026-01-31T23%3A59%3A59Z"));
198
- });
199
- });
200
- describe("tailscale_set_contacts", () => {
201
- it("should only send provided contact fields", async () => {
202
- const { tailnetTools } = await import("./tools/tailnet.js");
203
- let capturedBody;
204
- globalThis.fetch = async (_input, init) => {
205
- capturedBody = init?.body;
206
- return mockFetchResponse(200, {});
207
- };
208
- // set_contacts is index 3
209
- const handler = tailnetTools[3].handler;
210
- await handler({ security: { email: "sec@example.com" } });
211
- const parsed = JSON.parse(capturedBody);
212
- assert.deepEqual(parsed, { security: { email: "sec@example.com" } });
213
- assert.ok(!("account" in parsed));
214
- assert.ok(!("support" in parsed));
215
- });
216
- });
217
- });
218
- //# sourceMappingURL=handlers.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handlers.test.js","sourceRoot":"","sources":["../src/handlers.test.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,70 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { deviceTools } from "./devices.js";
4
- import { aclTools } from "./acl.js";
5
- import { dnsTools } from "./dns.js";
6
- import { keyTools } from "./keys.js";
7
- import { userTools } from "./users.js";
8
- import { tailnetTools } from "./tailnet.js";
9
- import { webhookTools } from "./webhooks.js";
10
- import { networkLockTools } from "./network-lock.js";
11
- import { postureTools } from "./posture.js";
12
- import { auditTools } from "./audit.js";
13
- import { statusTools } from "./status.js";
14
- const allTools = [
15
- ...statusTools,
16
- ...deviceTools,
17
- ...aclTools,
18
- ...dnsTools,
19
- ...keyTools,
20
- ...userTools,
21
- ...tailnetTools,
22
- ...webhookTools,
23
- ...networkLockTools,
24
- ...postureTools,
25
- ...auditTools,
26
- ];
27
- describe("Tool definitions", () => {
28
- it("should have no duplicate tool names", () => {
29
- const names = allTools.map((t) => t.name);
30
- const unique = new Set(names);
31
- assert.equal(names.length, unique.size, `Duplicate tool names found: ${names.filter((n, i) => names.indexOf(n) !== i)}`);
32
- });
33
- it("should have the expected total tool count", () => {
34
- assert.equal(allTools.length, 44);
35
- });
36
- for (const tool of allTools) {
37
- describe(tool.name, () => {
38
- it("should have a non-empty name", () => {
39
- assert.ok(tool.name.length > 0);
40
- });
41
- it("should have a name prefixed with tailscale_", () => {
42
- assert.ok(tool.name.startsWith("tailscale_"), `Tool name ${tool.name} should start with tailscale_`);
43
- });
44
- it("should have a non-empty description", () => {
45
- assert.ok(tool.description.length > 0);
46
- });
47
- it("should have a Zod input schema", () => {
48
- assert.ok(tool.inputSchema);
49
- assert.ok(typeof tool.inputSchema.shape === "object");
50
- });
51
- it("should have an async handler function", () => {
52
- assert.equal(typeof tool.handler, "function");
53
- });
54
- });
55
- }
56
- });
57
- describe("Tool modules export correct counts", () => {
58
- it("statusTools has 1 tool", () => assert.equal(statusTools.length, 1));
59
- it("deviceTools has 10 tools", () => assert.equal(deviceTools.length, 10));
60
- it("aclTools has 4 tools", () => assert.equal(aclTools.length, 4));
61
- it("dnsTools has 8 tools", () => assert.equal(dnsTools.length, 8));
62
- it("keyTools has 4 tools", () => assert.equal(keyTools.length, 4));
63
- it("userTools has 2 tools", () => assert.equal(userTools.length, 2));
64
- it("tailnetTools has 4 tools", () => assert.equal(tailnetTools.length, 4));
65
- it("webhookTools has 5 tools", () => assert.equal(webhookTools.length, 5));
66
- it("networkLockTools has 1 tool", () => assert.equal(networkLockTools.length, 1));
67
- it("postureTools has 4 tools", () => assert.equal(postureTools.length, 4));
68
- it("auditTools has 1 tool", () => assert.equal(auditTools.length, 1));
69
- });
70
- //# sourceMappingURL=tools.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tools.test.js","sourceRoot":"","sources":["../../src/tools/tools.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,QAAQ,GAAG;IACf,GAAG,WAAW;IACd,GAAG,WAAW;IACd,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,GAAG,SAAS;IACZ,GAAG,YAAY;IACf,GAAG,YAAY;IACf,GAAG,gBAAgB;IACnB,GAAG,YAAY;IACf,GAAG,UAAU;CACd,CAAC;AAEF,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,+BAA+B,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3H,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE;YACvB,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;gBACtC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;gBACrD,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,aAAa,IAAI,CAAC,IAAI,+BAA+B,CAAC,CAAC;YACvG,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;gBAC7C,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;gBACxC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC5B,MAAM,CAAC,EAAE,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC;YACxD,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;gBAC/C,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YAChD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAClD,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3E,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACnE,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACnE,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACnE,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACrE,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAClF,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC,CAAC,CAAC"}