@owine/unifi-network-mcp 2.0.1 → 2.0.2
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/tools/acl.js +107 -79
- package/dist/tools/clients.js +75 -59
- package/dist/tools/devices.js +95 -63
- package/dist/tools/dns-policies.js +77 -57
- package/dist/tools/firewall.js +200 -148
- package/dist/tools/hotspot.js +123 -103
- package/dist/tools/networks.js +106 -82
- package/dist/tools/sites.js +23 -19
- package/dist/tools/supporting.js +185 -153
- package/dist/tools/system.js +5 -1
- package/dist/tools/traffic-matching.js +83 -63
- package/dist/tools/wifi.js +84 -64
- package/package.json +4 -4
package/dist/tools/acl.js
CHANGED
|
@@ -6,26 +6,30 @@ const responses_js_1 = require("../utils/responses.js");
|
|
|
6
6
|
const query_js_1 = require("../utils/query.js");
|
|
7
7
|
const safety_js_1 = require("../utils/safety.js");
|
|
8
8
|
function registerAclTools(server, client, readOnly = false) {
|
|
9
|
-
server.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
|
|
9
|
+
server.registerTool("unifi_list_acl_rules", {
|
|
10
|
+
description: "List all ACL (firewall) rules at a site",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
13
|
+
offset: zod_1.z
|
|
14
|
+
.number()
|
|
15
|
+
.int()
|
|
16
|
+
.nonnegative()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Number of records to skip (default: 0)"),
|
|
19
|
+
limit: zod_1.z
|
|
20
|
+
.number()
|
|
21
|
+
.int()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(200)
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Number of records to return (default: 25, max: 200)"),
|
|
26
|
+
filter: zod_1.z
|
|
27
|
+
.string()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("Filter expression"),
|
|
30
|
+
},
|
|
31
|
+
annotations: safety_js_1.READ_ONLY,
|
|
32
|
+
}, async ({ siteId, offset, limit, filter }) => {
|
|
29
33
|
try {
|
|
30
34
|
const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
|
|
31
35
|
const data = await client.get(`/sites/${siteId}/acl-rules${query}`);
|
|
@@ -35,10 +39,14 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
35
39
|
return (0, responses_js_1.formatError)(err);
|
|
36
40
|
}
|
|
37
41
|
});
|
|
38
|
-
server.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
server.registerTool("unifi_get_acl_rule", {
|
|
43
|
+
description: "Get a specific ACL rule by ID",
|
|
44
|
+
inputSchema: {
|
|
45
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
46
|
+
aclRuleId: zod_1.z.string().describe("ACL rule ID"),
|
|
47
|
+
},
|
|
48
|
+
annotations: safety_js_1.READ_ONLY,
|
|
49
|
+
}, async ({ siteId, aclRuleId }) => {
|
|
42
50
|
try {
|
|
43
51
|
const data = await client.get(`/sites/${siteId}/acl-rules/${aclRuleId}`);
|
|
44
52
|
return (0, responses_js_1.formatSuccess)(data);
|
|
@@ -47,9 +55,13 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
47
55
|
return (0, responses_js_1.formatError)(err);
|
|
48
56
|
}
|
|
49
57
|
});
|
|
50
|
-
server.
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
server.registerTool("unifi_get_acl_rule_ordering", {
|
|
59
|
+
description: "Get user-defined ACL rule ordering",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
62
|
+
},
|
|
63
|
+
annotations: safety_js_1.READ_ONLY,
|
|
64
|
+
}, async ({ siteId }) => {
|
|
53
65
|
try {
|
|
54
66
|
const data = await client.get(`/sites/${siteId}/acl-rules/ordering`);
|
|
55
67
|
return (0, responses_js_1.formatSuccess)(data);
|
|
@@ -60,25 +72,29 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
60
72
|
});
|
|
61
73
|
if (readOnly)
|
|
62
74
|
return;
|
|
63
|
-
server.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.
|
|
71
|
-
.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
server.registerTool("unifi_create_acl_rule", {
|
|
76
|
+
description: "Create a new ACL (firewall) rule",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
79
|
+
type: zod_1.z.enum(["IPV4", "MAC"]).describe("Rule type"),
|
|
80
|
+
name: zod_1.z.string().describe("Rule name"),
|
|
81
|
+
enabled: zod_1.z.boolean().describe("Enable the rule"),
|
|
82
|
+
action: zod_1.z.enum(["ALLOW", "BLOCK"]).describe("Rule action"),
|
|
83
|
+
description: zod_1.z
|
|
84
|
+
.string()
|
|
85
|
+
.optional()
|
|
86
|
+
.describe("Rule description"),
|
|
87
|
+
protocolFilter: zod_1.z
|
|
88
|
+
.array(zod_1.z.string())
|
|
89
|
+
.optional()
|
|
90
|
+
.describe("Protocols: TCP, UDP"),
|
|
91
|
+
dryRun: zod_1.z
|
|
92
|
+
.boolean()
|
|
93
|
+
.optional()
|
|
94
|
+
.describe("Preview this action without executing it"),
|
|
95
|
+
},
|
|
96
|
+
annotations: safety_js_1.WRITE_NOT_IDEMPOTENT,
|
|
97
|
+
}, async ({ siteId, type, name, enabled, action, description, protocolFilter, dryRun }) => {
|
|
82
98
|
try {
|
|
83
99
|
const body = { type, name, enabled, action };
|
|
84
100
|
if (description !== undefined)
|
|
@@ -94,17 +110,21 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
94
110
|
return (0, responses_js_1.formatError)(err);
|
|
95
111
|
}
|
|
96
112
|
});
|
|
97
|
-
server.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
.
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
server.registerTool("unifi_update_acl_rule", {
|
|
114
|
+
description: "Update an ACL rule",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
117
|
+
aclRuleId: zod_1.z.string().describe("ACL rule ID"),
|
|
118
|
+
rule: zod_1.z
|
|
119
|
+
.record(zod_1.z.string(), zod_1.z.unknown())
|
|
120
|
+
.describe("ACL rule configuration (JSON object)"),
|
|
121
|
+
dryRun: zod_1.z
|
|
122
|
+
.boolean()
|
|
123
|
+
.optional()
|
|
124
|
+
.describe("Preview this action without executing it"),
|
|
125
|
+
},
|
|
126
|
+
annotations: safety_js_1.WRITE,
|
|
127
|
+
}, async ({ siteId, aclRuleId, rule, dryRun }) => {
|
|
108
128
|
try {
|
|
109
129
|
if (dryRun)
|
|
110
130
|
return (0, safety_js_1.formatDryRun)("PUT", `/sites/${siteId}/acl-rules/${aclRuleId}`, rule);
|
|
@@ -115,18 +135,22 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
115
135
|
return (0, responses_js_1.formatError)(err);
|
|
116
136
|
}
|
|
117
137
|
});
|
|
118
|
-
server.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
.
|
|
123
|
-
.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
server.registerTool("unifi_delete_acl_rule", {
|
|
139
|
+
description: "DESTRUCTIVE: Delete an ACL rule",
|
|
140
|
+
inputSchema: {
|
|
141
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
142
|
+
aclRuleId: zod_1.z.string().describe("ACL rule ID"),
|
|
143
|
+
confirm: zod_1.z
|
|
144
|
+
.boolean()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("Must be true to execute this destructive action"),
|
|
147
|
+
dryRun: zod_1.z
|
|
148
|
+
.boolean()
|
|
149
|
+
.optional()
|
|
150
|
+
.describe("Preview this action without executing it"),
|
|
151
|
+
},
|
|
152
|
+
annotations: safety_js_1.DESTRUCTIVE,
|
|
153
|
+
}, async ({ siteId, aclRuleId, confirm, dryRun }) => {
|
|
130
154
|
const guard = (0, safety_js_1.requireConfirmation)(confirm, "This will delete the ACL rule");
|
|
131
155
|
if (guard)
|
|
132
156
|
return guard;
|
|
@@ -140,16 +164,20 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
140
164
|
return (0, responses_js_1.formatError)(err);
|
|
141
165
|
}
|
|
142
166
|
});
|
|
143
|
-
server.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
.
|
|
151
|
-
|
|
152
|
-
|
|
167
|
+
server.registerTool("unifi_reorder_acl_rules", {
|
|
168
|
+
description: "Reorder user-defined ACL rules",
|
|
169
|
+
inputSchema: {
|
|
170
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
171
|
+
orderedAclRuleIds: zod_1.z
|
|
172
|
+
.array(zod_1.z.string())
|
|
173
|
+
.describe("Ordered ACL rule IDs"),
|
|
174
|
+
dryRun: zod_1.z
|
|
175
|
+
.boolean()
|
|
176
|
+
.optional()
|
|
177
|
+
.describe("Preview this action without executing it"),
|
|
178
|
+
},
|
|
179
|
+
annotations: safety_js_1.WRITE,
|
|
180
|
+
}, async ({ siteId, orderedAclRuleIds, dryRun }) => {
|
|
153
181
|
try {
|
|
154
182
|
const body = { orderedAclRuleIds };
|
|
155
183
|
if (dryRun)
|
package/dist/tools/clients.js
CHANGED
|
@@ -6,23 +6,27 @@ const responses_js_1 = require("../utils/responses.js");
|
|
|
6
6
|
const query_js_1 = require("../utils/query.js");
|
|
7
7
|
const safety_js_1 = require("../utils/safety.js");
|
|
8
8
|
function registerClientTools(server, client, readOnly = false) {
|
|
9
|
-
server.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
9
|
+
server.registerTool("unifi_list_clients", {
|
|
10
|
+
description: "List all connected clients (wired, wireless, VPN) at a site",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
13
|
+
offset: zod_1.z
|
|
14
|
+
.number()
|
|
15
|
+
.int()
|
|
16
|
+
.nonnegative()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Number of records to skip (default: 0)"),
|
|
19
|
+
limit: zod_1.z
|
|
20
|
+
.number()
|
|
21
|
+
.int()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(200)
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Number of records to return (default: 25, max: 200)"),
|
|
26
|
+
filter: zod_1.z.string().optional().describe("Filter expression"),
|
|
27
|
+
},
|
|
28
|
+
annotations: safety_js_1.READ_ONLY,
|
|
29
|
+
}, async ({ siteId, offset, limit, filter }) => {
|
|
26
30
|
try {
|
|
27
31
|
const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
|
|
28
32
|
const data = await client.get(`/sites/${siteId}/clients${query}`);
|
|
@@ -32,10 +36,14 @@ function registerClientTools(server, client, readOnly = false) {
|
|
|
32
36
|
return (0, responses_js_1.formatError)(err);
|
|
33
37
|
}
|
|
34
38
|
});
|
|
35
|
-
server.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
server.registerTool("unifi_get_client", {
|
|
40
|
+
description: "Get a specific client by ID",
|
|
41
|
+
inputSchema: {
|
|
42
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
43
|
+
clientId: zod_1.z.string().describe("Client ID"),
|
|
44
|
+
},
|
|
45
|
+
annotations: safety_js_1.READ_ONLY,
|
|
46
|
+
}, async ({ siteId, clientId }) => {
|
|
39
47
|
try {
|
|
40
48
|
const data = await client.get(`/sites/${siteId}/clients/${clientId}`);
|
|
41
49
|
return (0, responses_js_1.formatSuccess)(data);
|
|
@@ -46,39 +54,43 @@ function registerClientTools(server, client, readOnly = false) {
|
|
|
46
54
|
});
|
|
47
55
|
if (readOnly)
|
|
48
56
|
return;
|
|
49
|
-
server.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
57
|
+
server.registerTool("unifi_authorize_guest", {
|
|
58
|
+
description: "Authorize a guest client on a hotspot network",
|
|
59
|
+
inputSchema: {
|
|
60
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
61
|
+
clientId: zod_1.z.string().describe("Client ID"),
|
|
62
|
+
timeLimitMinutes: zod_1.z
|
|
63
|
+
.number()
|
|
64
|
+
.int()
|
|
65
|
+
.min(1)
|
|
66
|
+
.max(1000000)
|
|
67
|
+
.optional()
|
|
68
|
+
.describe("How long (in minutes) the guest will be authorized (1-1000000)"),
|
|
69
|
+
dataUsageLimitMBytes: zod_1.z
|
|
70
|
+
.number()
|
|
71
|
+
.int()
|
|
72
|
+
.min(1)
|
|
73
|
+
.max(1048576)
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("Data usage limit in megabytes (1-1048576)"),
|
|
76
|
+
rxRateLimitKbps: zod_1.z
|
|
77
|
+
.number()
|
|
78
|
+
.int()
|
|
79
|
+
.min(2)
|
|
80
|
+
.max(100000)
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("Download rate limit in kilobits per second (2-100000)"),
|
|
83
|
+
txRateLimitKbps: zod_1.z
|
|
84
|
+
.number()
|
|
85
|
+
.int()
|
|
86
|
+
.min(2)
|
|
87
|
+
.max(100000)
|
|
88
|
+
.optional()
|
|
89
|
+
.describe("Upload rate limit in kilobits per second (2-100000)"),
|
|
90
|
+
dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
|
|
91
|
+
},
|
|
92
|
+
annotations: safety_js_1.WRITE_NOT_IDEMPOTENT,
|
|
93
|
+
}, async ({ siteId, clientId, timeLimitMinutes, dataUsageLimitMBytes, rxRateLimitKbps, txRateLimitKbps, dryRun, }) => {
|
|
82
94
|
const body = {
|
|
83
95
|
action: "AUTHORIZE_GUEST_ACCESS",
|
|
84
96
|
};
|
|
@@ -100,11 +112,15 @@ function registerClientTools(server, client, readOnly = false) {
|
|
|
100
112
|
return (0, responses_js_1.formatError)(err);
|
|
101
113
|
}
|
|
102
114
|
});
|
|
103
|
-
server.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
115
|
+
server.registerTool("unifi_unauthorize_guest", {
|
|
116
|
+
description: "Unauthorize a guest client",
|
|
117
|
+
inputSchema: {
|
|
118
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
119
|
+
clientId: zod_1.z.string().describe("Client ID"),
|
|
120
|
+
dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
|
|
121
|
+
},
|
|
122
|
+
annotations: safety_js_1.WRITE,
|
|
123
|
+
}, async ({ siteId, clientId, dryRun }) => {
|
|
108
124
|
const body = { action: "UNAUTHORIZE_GUEST_ACCESS" };
|
|
109
125
|
if (dryRun)
|
|
110
126
|
return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/clients/${clientId}/actions`, body);
|
package/dist/tools/devices.js
CHANGED
|
@@ -6,23 +6,27 @@ const responses_js_1 = require("../utils/responses.js");
|
|
|
6
6
|
const query_js_1 = require("../utils/query.js");
|
|
7
7
|
const safety_js_1 = require("../utils/safety.js");
|
|
8
8
|
function registerDeviceTools(server, client, readOnly = false) {
|
|
9
|
-
server.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
9
|
+
server.registerTool("unifi_list_devices", {
|
|
10
|
+
description: "List all adopted devices at a site",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
13
|
+
offset: zod_1.z
|
|
14
|
+
.number()
|
|
15
|
+
.int()
|
|
16
|
+
.nonnegative()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Number of records to skip (default: 0)"),
|
|
19
|
+
limit: zod_1.z
|
|
20
|
+
.number()
|
|
21
|
+
.int()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(200)
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Number of records to return (default: 25, max: 200)"),
|
|
26
|
+
filter: zod_1.z.string().optional().describe("Filter expression"),
|
|
27
|
+
},
|
|
28
|
+
annotations: safety_js_1.READ_ONLY,
|
|
29
|
+
}, async ({ siteId, offset, limit, filter }) => {
|
|
26
30
|
try {
|
|
27
31
|
const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
|
|
28
32
|
const data = await client.get(`/sites/${siteId}/devices${query}`);
|
|
@@ -32,10 +36,14 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
32
36
|
return (0, responses_js_1.formatError)(err);
|
|
33
37
|
}
|
|
34
38
|
});
|
|
35
|
-
server.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
server.registerTool("unifi_get_device", {
|
|
40
|
+
description: "Get a specific device by ID",
|
|
41
|
+
inputSchema: {
|
|
42
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
43
|
+
deviceId: zod_1.z.string().describe("Device ID"),
|
|
44
|
+
},
|
|
45
|
+
annotations: safety_js_1.READ_ONLY,
|
|
46
|
+
}, async ({ siteId, deviceId }) => {
|
|
39
47
|
try {
|
|
40
48
|
const data = await client.get(`/sites/${siteId}/devices/${deviceId}`);
|
|
41
49
|
return (0, responses_js_1.formatSuccess)(data);
|
|
@@ -44,10 +52,14 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
44
52
|
return (0, responses_js_1.formatError)(err);
|
|
45
53
|
}
|
|
46
54
|
});
|
|
47
|
-
server.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
server.registerTool("unifi_get_device_statistics", {
|
|
56
|
+
description: "Get latest statistics for a device",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
59
|
+
deviceId: zod_1.z.string().describe("Device ID"),
|
|
60
|
+
},
|
|
61
|
+
annotations: safety_js_1.READ_ONLY,
|
|
62
|
+
}, async ({ siteId, deviceId }) => {
|
|
51
63
|
try {
|
|
52
64
|
const data = await client.get(`/sites/${siteId}/devices/${deviceId}/statistics/latest`);
|
|
53
65
|
return (0, responses_js_1.formatSuccess)(data);
|
|
@@ -56,22 +68,26 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
56
68
|
return (0, responses_js_1.formatError)(err);
|
|
57
69
|
}
|
|
58
70
|
});
|
|
59
|
-
server.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
server.registerTool("unifi_list_pending_devices", {
|
|
72
|
+
description: "List devices pending adoption (global, not site-specific)",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
offset: zod_1.z
|
|
75
|
+
.number()
|
|
76
|
+
.int()
|
|
77
|
+
.nonnegative()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("Number of records to skip (default: 0)"),
|
|
80
|
+
limit: zod_1.z
|
|
81
|
+
.number()
|
|
82
|
+
.int()
|
|
83
|
+
.min(1)
|
|
84
|
+
.max(200)
|
|
85
|
+
.optional()
|
|
86
|
+
.describe("Number of records to return (default: 25, max: 200)"),
|
|
87
|
+
filter: zod_1.z.string().optional().describe("Filter expression"),
|
|
88
|
+
},
|
|
89
|
+
annotations: safety_js_1.READ_ONLY,
|
|
90
|
+
}, async ({ offset, limit, filter }) => {
|
|
75
91
|
try {
|
|
76
92
|
const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
|
|
77
93
|
const data = await client.get(`/pending-devices${query}`);
|
|
@@ -83,11 +99,15 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
83
99
|
});
|
|
84
100
|
if (readOnly)
|
|
85
101
|
return;
|
|
86
|
-
server.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
102
|
+
server.registerTool("unifi_adopt_device", {
|
|
103
|
+
description: "Adopt a pending device",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
106
|
+
macAddress: zod_1.z.string().describe("MAC address of the device"),
|
|
107
|
+
dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
|
|
108
|
+
},
|
|
109
|
+
annotations: safety_js_1.WRITE_NOT_IDEMPOTENT,
|
|
110
|
+
}, async ({ siteId, macAddress, dryRun }) => {
|
|
91
111
|
const body = {
|
|
92
112
|
macAddress,
|
|
93
113
|
ignoreDeviceLimit: false,
|
|
@@ -102,12 +122,16 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
102
122
|
return (0, responses_js_1.formatError)(err);
|
|
103
123
|
}
|
|
104
124
|
});
|
|
105
|
-
server.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
125
|
+
server.registerTool("unifi_remove_device", {
|
|
126
|
+
description: "DESTRUCTIVE: Remove (unadopt) a device from a site. If the device is online, it will be reset to factory defaults",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
129
|
+
deviceId: zod_1.z.string().describe("Device ID"),
|
|
130
|
+
confirm: zod_1.z.boolean().optional().describe("Must be true to execute this destructive action"),
|
|
131
|
+
dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
|
|
132
|
+
},
|
|
133
|
+
annotations: safety_js_1.DESTRUCTIVE,
|
|
134
|
+
}, async ({ siteId, deviceId, confirm, dryRun }) => {
|
|
111
135
|
const guard = (0, safety_js_1.requireConfirmation)(confirm, "This will remove the device from the site. If the device is online, it will be reset to factory defaults");
|
|
112
136
|
if (guard)
|
|
113
137
|
return guard;
|
|
@@ -121,11 +145,15 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
121
145
|
return (0, responses_js_1.formatError)(err);
|
|
122
146
|
}
|
|
123
147
|
});
|
|
124
|
-
server.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
server.registerTool("unifi_restart_device", {
|
|
149
|
+
description: "Restart a device",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
152
|
+
deviceId: zod_1.z.string().describe("Device ID"),
|
|
153
|
+
dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
|
|
154
|
+
},
|
|
155
|
+
annotations: safety_js_1.WRITE,
|
|
156
|
+
}, async ({ siteId, deviceId, dryRun }) => {
|
|
129
157
|
const body = { action: "RESTART" };
|
|
130
158
|
if (dryRun)
|
|
131
159
|
return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/devices/${deviceId}/actions`, body);
|
|
@@ -137,12 +165,16 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
137
165
|
return (0, responses_js_1.formatError)(err);
|
|
138
166
|
}
|
|
139
167
|
});
|
|
140
|
-
server.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
168
|
+
server.registerTool("unifi_power_cycle_port", {
|
|
169
|
+
description: "Power cycle a specific port on a device (PoE restart)",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
siteId: zod_1.z.string().describe("Site ID"),
|
|
172
|
+
deviceId: zod_1.z.string().describe("Device ID"),
|
|
173
|
+
portIdx: zod_1.z.number().int().describe("Port index number"),
|
|
174
|
+
dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
|
|
175
|
+
},
|
|
176
|
+
annotations: safety_js_1.WRITE,
|
|
177
|
+
}, async ({ siteId, deviceId, portIdx, dryRun }) => {
|
|
146
178
|
const body = { action: "POWER_CYCLE" };
|
|
147
179
|
if (dryRun)
|
|
148
180
|
return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/devices/${deviceId}/interfaces/ports/${portIdx}/actions`, body);
|