@owine/unifi-network-mcp 2.14.2 → 2.15.0
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 +12 -10
- package/dist/tools/acl.js +3 -3
- package/dist/tools/client-history.js +1 -1
- package/dist/tools/clients.js +4 -4
- package/dist/tools/devices.js +9 -9
- package/dist/tools/dns-policies.js +96 -49
- package/dist/tools/firewall.js +53 -13
- package/dist/tools/networks.js +117 -36
- package/dist/tools/sites.js +1 -1
- package/dist/tools/supporting.js +9 -9
- package/dist/tools/switching.js +6 -6
- package/dist/tools/traffic-matching.js +10 -9
- package/dist/tools/wifi.js +164 -293
- package/dist/utils/full-put.d.ts +23 -0
- package/dist/utils/full-put.js +51 -0
- package/dist/utils/output-schemas.d.ts +51 -5
- package/dist/utils/output-schemas.js +26 -6
- package/dist/utils/secrets.d.ts +24 -0
- package/dist/utils/secrets.js +89 -0
- package/package.json +6 -6
package/dist/tools/networks.js
CHANGED
|
@@ -5,10 +5,106 @@ const zod_1 = require("zod");
|
|
|
5
5
|
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
|
+
const full_put_js_1 = require("../utils/full-put.js");
|
|
8
9
|
const output_schemas_js_1 = require("../utils/output-schemas.js");
|
|
10
|
+
// Request-body fields per management type, from the 11.0.83 POST/PUT schemas.
|
|
11
|
+
const COMMON_FIELDS = ["management", "name", "enabled", "vlanId", "dhcpGuarding"];
|
|
12
|
+
const COMMON_REQUIRED = ["management", "name", "enabled", "vlanId"];
|
|
13
|
+
const NETWORK_VARIANTS = {
|
|
14
|
+
UNMANAGED: { allowed: COMMON_FIELDS, required: COMMON_REQUIRED },
|
|
15
|
+
GATEWAY: {
|
|
16
|
+
allowed: [
|
|
17
|
+
...COMMON_FIELDS,
|
|
18
|
+
"isolationEnabled",
|
|
19
|
+
"cellularBackupEnabled",
|
|
20
|
+
"zoneId",
|
|
21
|
+
"internetAccessEnabled",
|
|
22
|
+
"mdnsForwardingEnabled",
|
|
23
|
+
"ipv4Configuration",
|
|
24
|
+
"ipv6Configuration",
|
|
25
|
+
],
|
|
26
|
+
required: [
|
|
27
|
+
...COMMON_REQUIRED,
|
|
28
|
+
"isolationEnabled",
|
|
29
|
+
"cellularBackupEnabled",
|
|
30
|
+
"internetAccessEnabled",
|
|
31
|
+
"ipv4Configuration",
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
SWITCH: {
|
|
35
|
+
allowed: [
|
|
36
|
+
...COMMON_FIELDS,
|
|
37
|
+
"isolationEnabled",
|
|
38
|
+
"cellularBackupEnabled",
|
|
39
|
+
"deviceId",
|
|
40
|
+
"ipv4Configuration",
|
|
41
|
+
],
|
|
42
|
+
required: [
|
|
43
|
+
...COMMON_REQUIRED,
|
|
44
|
+
"isolationEnabled",
|
|
45
|
+
"cellularBackupEnabled",
|
|
46
|
+
"deviceId",
|
|
47
|
+
"ipv4Configuration",
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
const NETWORK_VARIANTS_DOC = "all types take name, enabled, vlanId, optional dhcpGuarding (omitted or null = disabled). GATEWAY (routed by the UniFi gateway) also requires isolationEnabled, cellularBackupEnabled, internetAccessEnabled, ipv4Configuration (gateway-managed subnet + DHCP), optional zoneId, mdnsForwardingEnabled, ipv6Configuration. SWITCH (routed by an L3 switch) also requires isolationEnabled, cellularBackupEnabled, deviceId (the L3 switch), ipv4Configuration (switch-managed).";
|
|
52
|
+
function fieldsForManagement(management) {
|
|
53
|
+
if (management === "UNMANAGED" || management === "GATEWAY" || management === "SWITCH") {
|
|
54
|
+
return NETWORK_VARIANTS[management];
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Unsupported network management type: ${String(management)}`);
|
|
57
|
+
}
|
|
58
|
+
function checkNetworkBody(body, updates) {
|
|
59
|
+
const { allowed, required } = fieldsForManagement(body.management);
|
|
60
|
+
const rejected = (0, full_put_js_1.unsupportedFields)(updates, allowed);
|
|
61
|
+
if (rejected.length > 0) {
|
|
62
|
+
throw new Error(`Fields not accepted for management ${String(body.management)}: ${rejected.join(", ")}`);
|
|
63
|
+
}
|
|
64
|
+
const missing = (0, full_put_js_1.missingFields)(body, required);
|
|
65
|
+
if (missing.length > 0) {
|
|
66
|
+
throw new Error(`Missing required fields for management ${String(body.management)}: ${missing.join(", ")}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const config = (description) => zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).describe(description);
|
|
70
|
+
// Every body field as an optional input. Create tightens the common fields to
|
|
71
|
+
// required; update keeps them optional and fills gaps from the current network.
|
|
72
|
+
const networkFields = {
|
|
73
|
+
name: zod_1.z.string().optional().describe("Network name"),
|
|
74
|
+
management: zod_1.z
|
|
75
|
+
.enum(["UNMANAGED", "GATEWAY", "SWITCH"])
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Network management type"),
|
|
78
|
+
enabled: zod_1.z.boolean().optional().describe("Enable the network"),
|
|
79
|
+
vlanId: zod_1.z
|
|
80
|
+
.number()
|
|
81
|
+
.int()
|
|
82
|
+
.min(1)
|
|
83
|
+
.max(4009)
|
|
84
|
+
.optional()
|
|
85
|
+
.describe("VLAN ID (1 for the default network, 2-4009 for additional networks)"),
|
|
86
|
+
dhcpGuarding: config("DHCP Guarding settings (trustedDhcpServerIpAddresses). Omitted or null = disabled.")
|
|
87
|
+
.nullable()
|
|
88
|
+
.optional(),
|
|
89
|
+
isolationEnabled: zod_1.z.boolean().optional().describe("Isolate this network from all other networks. GATEWAY/SWITCH."),
|
|
90
|
+
cellularBackupEnabled: zod_1.z.boolean().optional().describe("Allow cellular data when WAN connection(s) are down. GATEWAY/SWITCH."),
|
|
91
|
+
internetAccessEnabled: zod_1.z.boolean().optional().describe("Allow internet access for devices on this network. GATEWAY only."),
|
|
92
|
+
mdnsForwardingEnabled: zod_1.z
|
|
93
|
+
.boolean()
|
|
94
|
+
.nullable()
|
|
95
|
+
.optional()
|
|
96
|
+
.describe("Participate in mDNS forwarding; null = use the site mDNS default. GATEWAY only."),
|
|
97
|
+
zoneId: zod_1.z.string().optional().describe("Firewall zone ID for this network. GATEWAY only."),
|
|
98
|
+
deviceId: zod_1.z.string().optional().describe("ID of the L3 switching capable device that manages this network. SWITCH only."),
|
|
99
|
+
ipv4Configuration: config("IPv4 configuration (hostIpAddress, prefixLength, dhcpConfiguration, …) — copy the shape from unifi_get_network. GATEWAY/SWITCH.")
|
|
100
|
+
.optional(),
|
|
101
|
+
ipv6Configuration: config("IPv6 configuration (interfaceType, clientAddressAssignment, routerAdvertisement, …); omitted or null = IPv6 not configured. GATEWAY only.")
|
|
102
|
+
.nullable()
|
|
103
|
+
.optional(),
|
|
104
|
+
};
|
|
9
105
|
function registerNetworkTools(server, client, readOnly = false) {
|
|
10
106
|
server.registerTool("unifi_list_networks", {
|
|
11
|
-
description: "List all networks (VLANs/LAN segments) at a site. Returns: id, name, management (UNMANAGED/GATEWAY/SWITCH), enabled, vlanId, default (true for the default network), zoneId (the firewall zone the network sits in), dhcpGuarding, metadata.origin. NOTE: the list view is sparse — for subnet/DHCP/NTP detail (ipv4Configuration/ipv6Configuration), call unifi_get_network on a specific id. Use for: VLAN inventory; pair with unifi_get_network_references to find what consumes a network.",
|
|
107
|
+
description: "List all networks (VLANs/LAN segments) at a site. Returns: id, name, management (UNMANAGED/GATEWAY/SWITCH), enabled, vlanId, default (true for the default network), zoneId (the firewall zone the network sits in), dhcpGuarding (only present when enabled), metadata.origin (+ metadata.configurable on the system-defined default network). NOTE: the list view is sparse — for subnet/DHCP/NTP detail (ipv4Configuration/ipv6Configuration), call unifi_get_network on a specific id. Use for: VLAN inventory; pair with unifi_get_network_references to find what consumes a network.",
|
|
12
108
|
inputSchema: {
|
|
13
109
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
14
110
|
offset: zod_1.z
|
|
@@ -27,7 +123,7 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
27
123
|
filter: zod_1.z
|
|
28
124
|
.string()
|
|
29
125
|
.optional()
|
|
30
|
-
.describe("Filter expression"),
|
|
126
|
+
.describe("Filter expression, e.g. vlanId.eq(20)"),
|
|
31
127
|
},
|
|
32
128
|
outputSchema: output_schemas_js_1.listNetworksOutputSchema,
|
|
33
129
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -42,7 +138,7 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
42
138
|
}
|
|
43
139
|
});
|
|
44
140
|
server.registerTool("unifi_get_network", {
|
|
45
|
-
description: "Get a network/VLAN by ID. Returns the list fields PLUS (live-verified) isolationEnabled, internetAccessEnabled, mdnsForwardingEnabled, cellularBackupEnabled, a full ipv4Configuration object (autoScaleEnabled, hostIpAddress, prefixLength, dhcpConfiguration with mode/ipAddressRange/leaseTimeSeconds/domainName/pingConflictDetectionEnabled/ntpServerIpAddresses), and an ipv6Configuration object (interfaceType, clientAddressAssignment, routerAdvertisement, hostIpAddress, prefixLength). NOTE: subnet/DHCP detail appears here at get-by-id but NOT in unifi_list_networks (sparse list view).",
|
|
141
|
+
description: "Get a network/VLAN by ID. Returns the list fields PLUS (live-verified) isolationEnabled, internetAccessEnabled, mdnsForwardingEnabled, cellularBackupEnabled, a full ipv4Configuration object (autoScaleEnabled, hostIpAddress, prefixLength, dhcpConfiguration with mode/ipAddressRange/leaseTimeSeconds/domainName/pingConflictDetectionEnabled/ntpServerIpAddresses — which may contain empty-string entries), and an ipv6Configuration object when IPv6 is configured (interfaceType, clientAddressAssignment, routerAdvertisement, hostIpAddress, prefixLength). NOTE: subnet/DHCP detail appears here at get-by-id but NOT in unifi_list_networks (sparse list view).",
|
|
46
142
|
inputSchema: {
|
|
47
143
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
48
144
|
networkId: zod_1.z.string().describe("Network ID"),
|
|
@@ -59,7 +155,7 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
59
155
|
}
|
|
60
156
|
});
|
|
61
157
|
server.registerTool("unifi_get_network_references", {
|
|
62
|
-
description: "Get all objects that reference this network
|
|
158
|
+
description: "Get all objects that reference this network. Returns: { referenceResources: [{ resourceType (e.g. WIFI), referenceCount, references: [{ referenceId }] }] }. Use before deleting a network to find dependencies that need to be re-pointed or removed. NOTE: on 11.0.83 the controller returns HTTP 500 (api.unexpected-error) for many networks — observed on the default network and on networks used by PPSK keys. On a 500, fall back to unifi_list_wifi + unifi_get_wifi (network.networkId and securityConfiguration.presharedKeys[].network.networkId) and unifi_list_firewall_zones (networkIds).",
|
|
63
159
|
inputSchema: {
|
|
64
160
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
65
161
|
networkId: zod_1.z.string().describe("Network ID"),
|
|
@@ -78,9 +174,10 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
78
174
|
if (readOnly)
|
|
79
175
|
return;
|
|
80
176
|
server.registerTool("unifi_create_network", {
|
|
81
|
-
description:
|
|
177
|
+
description: `Create a new VLAN/network. The body depends on management (per 11.0.83 docs): ${NETWORK_VARIANTS_DOC} Idempotency: not safe to retry — re-running creates duplicates.`,
|
|
82
178
|
inputSchema: {
|
|
83
179
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
180
|
+
...networkFields,
|
|
84
181
|
name: zod_1.z.string().describe("Network name"),
|
|
85
182
|
management: zod_1.z
|
|
86
183
|
.enum(["UNMANAGED", "GATEWAY", "SWITCH"])
|
|
@@ -92,10 +189,6 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
92
189
|
.min(1)
|
|
93
190
|
.max(4009)
|
|
94
191
|
.describe("VLAN ID (1 for default, 2+ for additional)"),
|
|
95
|
-
dhcpGuarding: zod_1.z
|
|
96
|
-
.record(zod_1.z.string(), zod_1.z.unknown())
|
|
97
|
-
.optional()
|
|
98
|
-
.describe("DHCP Guarding settings (trustedDhcpServerIpAddresses)"),
|
|
99
192
|
dryRun: zod_1.z
|
|
100
193
|
.boolean()
|
|
101
194
|
.optional()
|
|
@@ -103,11 +196,10 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
103
196
|
},
|
|
104
197
|
outputSchema: output_schemas_js_1.networkOutputSchema,
|
|
105
198
|
annotations: safety_js_1.WRITE_NOT_IDEMPOTENT,
|
|
106
|
-
}, async ({ siteId,
|
|
199
|
+
}, async ({ siteId, dryRun, ...fields }) => {
|
|
107
200
|
try {
|
|
108
|
-
const body =
|
|
109
|
-
|
|
110
|
-
body.dhcpGuarding = dhcpGuarding;
|
|
201
|
+
const body = (0, full_put_js_1.definedEntries)(fields);
|
|
202
|
+
checkNetworkBody(body, body);
|
|
111
203
|
if (dryRun)
|
|
112
204
|
return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/networks`, body);
|
|
113
205
|
const data = await client.post(`/sites/${siteId}/networks`, body);
|
|
@@ -118,40 +210,29 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
118
210
|
}
|
|
119
211
|
});
|
|
120
212
|
server.registerTool("unifi_update_network", {
|
|
121
|
-
description:
|
|
213
|
+
description: `Update an existing network. Pass only the fields to change. The API's PUT replaces the whole network — omitting dhcpGuarding disables DHCP guarding, and GATEWAY networks must resend their full ipv4Configuration — so this tool reads the current network, keeps every documented body field for its management type, overlays your changes and sends the full body. Pass null to clear an optional field (e.g. ipv6Configuration: null removes IPv6). Nested objects such as ipv4Configuration are replaced whole, not deep-merged: read them with unifi_get_network and send the complete object. Variants: ${NETWORK_VARIANTS_DOC} dryRun shows the merged body.`,
|
|
122
214
|
inputSchema: {
|
|
123
215
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
124
216
|
networkId: zod_1.z.string().describe("Network ID"),
|
|
125
|
-
|
|
126
|
-
management: zod_1.z
|
|
127
|
-
.enum(["UNMANAGED", "GATEWAY", "SWITCH"])
|
|
128
|
-
.describe("Network management type"),
|
|
129
|
-
enabled: zod_1.z.boolean().describe("Enable the network"),
|
|
130
|
-
vlanId: zod_1.z
|
|
131
|
-
.number()
|
|
132
|
-
.int()
|
|
133
|
-
.min(1)
|
|
134
|
-
.max(4009)
|
|
135
|
-
.describe("VLAN ID (1-4009)"),
|
|
136
|
-
dhcpGuarding: zod_1.z
|
|
137
|
-
.record(zod_1.z.string(), zod_1.z.unknown())
|
|
138
|
-
.optional()
|
|
139
|
-
.describe("DHCP Guarding settings (trustedDhcpServerIpAddresses)"),
|
|
217
|
+
...networkFields,
|
|
140
218
|
dryRun: zod_1.z
|
|
141
219
|
.boolean()
|
|
142
220
|
.optional()
|
|
143
|
-
.describe("Preview
|
|
221
|
+
.describe("Preview the merged PUT body without executing it (still reads the current network)"),
|
|
144
222
|
},
|
|
145
223
|
outputSchema: output_schemas_js_1.networkOutputSchema,
|
|
146
224
|
annotations: safety_js_1.WRITE,
|
|
147
|
-
}, async ({ siteId, networkId,
|
|
225
|
+
}, async ({ siteId, networkId, dryRun, ...fields }) => {
|
|
148
226
|
try {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
227
|
+
const path = `/sites/${siteId}/networks/${networkId}`;
|
|
228
|
+
const current = (0, full_put_js_1.asObject)(await client.get(path), "the current network");
|
|
229
|
+
const updates = (0, full_put_js_1.definedEntries)(fields);
|
|
230
|
+
const { allowed } = fieldsForManagement(updates.management ?? current.management);
|
|
231
|
+
const body = (0, full_put_js_1.mergeForPut)(current, updates, allowed);
|
|
232
|
+
checkNetworkBody(body, updates);
|
|
152
233
|
if (dryRun)
|
|
153
|
-
return (0, safety_js_1.formatDryRun)("PUT",
|
|
154
|
-
const data = await client.put(
|
|
234
|
+
return (0, safety_js_1.formatDryRun)("PUT", path, body);
|
|
235
|
+
const data = await client.put(path, body);
|
|
155
236
|
return (0, responses_js_1.formatSuccess)(data, { structured: true });
|
|
156
237
|
}
|
|
157
238
|
catch (err) {
|
package/dist/tools/sites.js
CHANGED
|
@@ -26,7 +26,7 @@ function registerSiteTools(server, client) {
|
|
|
26
26
|
filter: zod_1.z
|
|
27
27
|
.string()
|
|
28
28
|
.optional()
|
|
29
|
-
.describe("Filter expression (
|
|
29
|
+
.describe("Filter expression. Filterable: id, internalReference, name — eq/ne/in/notIn only (like is rejected). Example: name.eq('Default')"),
|
|
30
30
|
},
|
|
31
31
|
outputSchema: output_schemas_js_1.listSitesOutputSchema,
|
|
32
32
|
annotations: safety_js_1.READ_ONLY,
|
package/dist/tools/supporting.js
CHANGED
|
@@ -8,7 +8,7 @@ const safety_js_1 = require("../utils/safety.js");
|
|
|
8
8
|
const output_schemas_js_1 = require("../utils/output-schemas.js");
|
|
9
9
|
function registerSupportingTools(server, client) {
|
|
10
10
|
server.registerTool("unifi_list_wans", {
|
|
11
|
-
description: "List WAN interface definitions at a site. Returns: id, name only (verified against
|
|
11
|
+
description: "List WAN interface definitions at a site. Returns: id, name only (verified against 11.0.83 — the Integration API exposes no live link status or throughput rates here). Use for: WAN inventory, multi-WAN topology.",
|
|
12
12
|
inputSchema: {
|
|
13
13
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
14
14
|
offset: zod_1.z
|
|
@@ -38,7 +38,7 @@ function registerSupportingTools(server, client) {
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
server.registerTool("unifi_list_vpn_tunnels", {
|
|
41
|
-
description: "List site-to-site VPN tunnels (IPsec, WireGuard, OpenVPN site-to-site) at a site. Returns: tunnel definitions per row (per-row schema not rendered in
|
|
41
|
+
description: "List site-to-site VPN tunnels (IPsec, WireGuard, OpenVPN site-to-site) at a site. Returns: tunnel definitions per row (per-row schema not rendered in 11.0.83 docs and no tunnels to sample live; the older 10.4.57 spec lists id, type (IPSEC/OPENVPN/WIREGUARD), name, metadata.origin/source — call to inspect). For roaming client VPN servers, see unifi_list_vpn_servers.",
|
|
42
42
|
inputSchema: {
|
|
43
43
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
44
44
|
offset: zod_1.z
|
|
@@ -57,7 +57,7 @@ function registerSupportingTools(server, client) {
|
|
|
57
57
|
filter: zod_1.z
|
|
58
58
|
.string()
|
|
59
59
|
.optional()
|
|
60
|
-
.describe("Filter expression"),
|
|
60
|
+
.describe("Filter expression, e.g. metadata.source.isNull()"),
|
|
61
61
|
},
|
|
62
62
|
outputSchema: output_schemas_js_1.listVpnTunnelsOutputSchema,
|
|
63
63
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -72,7 +72,7 @@ function registerSupportingTools(server, client) {
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
server.registerTool("unifi_list_vpn_servers", {
|
|
75
|
-
description: "List VPN servers (roaming/client-access VPNs
|
|
75
|
+
description: "List VPN servers (roaming/client-access VPNs) at a site. Returns: id, type (L2TP, OPENVPN, PPTP, UID, WIREGUARD), name, enabled, metadata.origin.",
|
|
76
76
|
inputSchema: {
|
|
77
77
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
78
78
|
offset: zod_1.z
|
|
@@ -91,7 +91,7 @@ function registerSupportingTools(server, client) {
|
|
|
91
91
|
filter: zod_1.z
|
|
92
92
|
.string()
|
|
93
93
|
.optional()
|
|
94
|
-
.describe("Filter expression"),
|
|
94
|
+
.describe("Filter expression, e.g. type.eq('WIREGUARD')"),
|
|
95
95
|
},
|
|
96
96
|
outputSchema: output_schemas_js_1.listVpnServersOutputSchema,
|
|
97
97
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -125,7 +125,7 @@ function registerSupportingTools(server, client) {
|
|
|
125
125
|
filter: zod_1.z
|
|
126
126
|
.string()
|
|
127
127
|
.optional()
|
|
128
|
-
.describe("Filter expression"),
|
|
128
|
+
.describe("Filter expression, e.g. name.eq('Default')"),
|
|
129
129
|
},
|
|
130
130
|
outputSchema: output_schemas_js_1.listRadiusProfilesOutputSchema,
|
|
131
131
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -140,7 +140,7 @@ function registerSupportingTools(server, client) {
|
|
|
140
140
|
}
|
|
141
141
|
});
|
|
142
142
|
server.registerTool("unifi_list_device_tags", {
|
|
143
|
-
description: "List device tags at a site. Tags group APs/switches for selective WiFi broadcast (via broadcastingDeviceFilter on a WiFi network). Returns: id, name, deviceIds[].",
|
|
143
|
+
description: "List device tags at a site. Tags group APs/switches for selective WiFi broadcast (via broadcastingDeviceFilter on a WiFi network). Returns: id, name, deviceIds[], metadata.",
|
|
144
144
|
inputSchema: {
|
|
145
145
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
146
146
|
offset: zod_1.z
|
|
@@ -192,7 +192,7 @@ function registerSupportingTools(server, client) {
|
|
|
192
192
|
filter: zod_1.z
|
|
193
193
|
.string()
|
|
194
194
|
.optional()
|
|
195
|
-
.describe("Filter expression"),
|
|
195
|
+
.describe("Filter expression, e.g. name.like('Stream*') or id.eq(3). NOTE: totalCount ignores the filter here — page by count"),
|
|
196
196
|
},
|
|
197
197
|
outputSchema: output_schemas_js_1.listDpiCategoriesOutputSchema,
|
|
198
198
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -225,7 +225,7 @@ function registerSupportingTools(server, client) {
|
|
|
225
225
|
filter: zod_1.z
|
|
226
226
|
.string()
|
|
227
227
|
.optional()
|
|
228
|
-
.describe("Filter expression"),
|
|
228
|
+
.describe("Filter expression, e.g. name.like('Net*') or id.eq(3). NOTE: totalCount ignores the filter here — page by count"),
|
|
229
229
|
},
|
|
230
230
|
outputSchema: output_schemas_js_1.listDpiApplicationsOutputSchema,
|
|
231
231
|
annotations: safety_js_1.READ_ONLY,
|
package/dist/tools/switching.js
CHANGED
|
@@ -8,7 +8,7 @@ const safety_js_1 = require("../utils/safety.js");
|
|
|
8
8
|
const output_schemas_js_1 = require("../utils/output-schemas.js");
|
|
9
9
|
function registerSwitchingTools(server, client) {
|
|
10
10
|
server.registerTool("unifi_list_switch_stacks", {
|
|
11
|
-
description: "List Switch Stacks (multiple physical switches managed as one logical unit) at a site. Returns: id, deviceId (the stack's primary device), name, units[] (the stacked switches — named `members` on consoles older than 10.6.106), lags[] (LAGs spanning the stack), metadata.origin. Per-unit schema is not rendered in the
|
|
11
|
+
description: "List Switch Stacks (multiple physical switches managed as one logical unit) at a site. Returns: id, deviceId (the stack's primary device), name, units[] (the stacked switches — named `members` on consoles older than 10.6.106), lags[] (LAGs spanning the stack), metadata.origin. Per-unit schema is not rendered in the 11.0.83 docs — call unifi_get_switch_stack to inspect. Use for: identifying stacked switches; individual member configs/stats still come from unifi_get_device.",
|
|
12
12
|
inputSchema: {
|
|
13
13
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
14
14
|
offset: zod_1.z
|
|
@@ -27,7 +27,7 @@ function registerSwitchingTools(server, client) {
|
|
|
27
27
|
filter: zod_1.z
|
|
28
28
|
.string()
|
|
29
29
|
.optional()
|
|
30
|
-
.describe("Filter expression"),
|
|
30
|
+
.describe("Filter expression, e.g. name.like('Core*') or metadata.origin.eq('USER_DEFINED'). Device filters (members.deviceId, units.deviceId) are rejected on 11.0.83"),
|
|
31
31
|
},
|
|
32
32
|
outputSchema: output_schemas_js_1.listSwitchStacksOutputSchema,
|
|
33
33
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -59,7 +59,7 @@ function registerSwitchingTools(server, client) {
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
server.registerTool("unifi_list_mc_lag_domains", {
|
|
62
|
-
description: "List MC-LAG (Multi-Chassis Link Aggregation) Domains — pairs of switches presenting as one for LAG redundancy. Returns: id, name, peers[], lags[] (LAGs spanning the domain), metadata.origin.",
|
|
62
|
+
description: "List MC-LAG (Multi-Chassis Link Aggregation) Domains — pairs of switches presenting as one for LAG redundancy. Returns: id, name, peers[] (per the older 10.4.57 spec: {deviceId, linkPortIdxs[], role TOP|BOTTOM}; not live-verified), lags[] (LAGs spanning the domain), metadata.origin.",
|
|
63
63
|
inputSchema: {
|
|
64
64
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
65
65
|
offset: zod_1.z
|
|
@@ -78,7 +78,7 @@ function registerSwitchingTools(server, client) {
|
|
|
78
78
|
filter: zod_1.z
|
|
79
79
|
.string()
|
|
80
80
|
.optional()
|
|
81
|
-
.describe("Filter expression"),
|
|
81
|
+
.describe("Filter expression, e.g. peers.deviceId.contains('<deviceId>')"),
|
|
82
82
|
},
|
|
83
83
|
outputSchema: output_schemas_js_1.listMcLagDomainsOutputSchema,
|
|
84
84
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -110,7 +110,7 @@ function registerSwitchingTools(server, client) {
|
|
|
110
110
|
}
|
|
111
111
|
});
|
|
112
112
|
server.registerTool("unifi_list_lags", {
|
|
113
|
-
description: "List LAGs (Link Aggregation Groups — bonded switch ports) at a site. Returns: id, type (LOCAL/SWITCH_STACK/MULTI_CHASSIS), members[], metadata.origin.",
|
|
113
|
+
description: "List LAGs (Link Aggregation Groups — bonded switch ports) at a site. Returns: id, type (LOCAL/SWITCH_STACK/MULTI_CHASSIS), members[] ({deviceId, portIdxs[]}), switchStackId (SWITCH_STACK) or mcLagDomainId (MULTI_CHASSIS), metadata.origin.",
|
|
114
114
|
inputSchema: {
|
|
115
115
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
116
116
|
offset: zod_1.z
|
|
@@ -129,7 +129,7 @@ function registerSwitchingTools(server, client) {
|
|
|
129
129
|
filter: zod_1.z
|
|
130
130
|
.string()
|
|
131
131
|
.optional()
|
|
132
|
-
.describe("Filter expression"),
|
|
132
|
+
.describe("Filter expression, e.g. type.eq('LOCAL'), members.deviceId.contains('<deviceId>'), switchStackId.isNull()"),
|
|
133
133
|
},
|
|
134
134
|
outputSchema: output_schemas_js_1.listLagsOutputSchema,
|
|
135
135
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -6,9 +6,13 @@ 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
|
const output_schemas_js_1 = require("../utils/output-schemas.js");
|
|
9
|
+
const matchingItems = zod_1.z
|
|
10
|
+
.array(zod_1.z.object({ type: zod_1.z.string().describe("Item type, e.g. PORT_NUMBER, IP_ADDRESS, SUBNET") }).passthrough())
|
|
11
|
+
.min(1)
|
|
12
|
+
.describe("Non-empty array of tagged item objects, e.g. [{type:'PORT_NUMBER', value:443}]");
|
|
9
13
|
function registerTrafficMatchingTools(server, client, readOnly = false) {
|
|
10
14
|
server.registerTool("unifi_list_traffic_matching_lists", {
|
|
11
|
-
description: "List traffic matching lists at a site — named collections of ports or IPs reused in firewall/ACL rules. Returns: id, type (PORTS/IPV4_ADDRESSES/IPV6_ADDRESSES), name, items[]. Use for: finding the matching-list ID to reference from a firewall policy.",
|
|
15
|
+
description: "List traffic matching lists at a site — named collections of ports or IPs reused in firewall/ACL rules. Returns: id, type (PORTS/IPV4_ADDRESSES/IPV6_ADDRESSES), name, items[] (tagged objects such as {type:'PORT_NUMBER', value:22} or {type:'SUBNET', value:'<cidr>'}). Use for: finding the matching-list ID to reference from a firewall policy.",
|
|
12
16
|
inputSchema: {
|
|
13
17
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
14
18
|
offset: zod_1.z
|
|
@@ -27,7 +31,7 @@ function registerTrafficMatchingTools(server, client, readOnly = false) {
|
|
|
27
31
|
filter: zod_1.z
|
|
28
32
|
.string()
|
|
29
33
|
.optional()
|
|
30
|
-
.describe("Filter expression"),
|
|
34
|
+
.describe("Filter expression. Only id and name are filterable (type is rejected with HTTP 400). Example: name.like('Web*'). NOTE: totalCount ignores the filter here — page by count"),
|
|
31
35
|
},
|
|
32
36
|
outputSchema: output_schemas_js_1.listTrafficMatchingListsOutputSchema,
|
|
33
37
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -61,17 +65,14 @@ function registerTrafficMatchingTools(server, client, readOnly = false) {
|
|
|
61
65
|
if (readOnly)
|
|
62
66
|
return;
|
|
63
67
|
server.registerTool("unifi_create_traffic_matching_list", {
|
|
64
|
-
description: "Create a new traffic matching list",
|
|
68
|
+
description: "Create a new traffic matching list. Items are tagged objects, not strings (live-verified 11.0.83): PORTS lists take {type:'PORT_NUMBER', value:443} (the older 10.4.57 spec also lists {type:'PORT_NUMBER_RANGE', start, stop}); IPV4_ADDRESSES lists take {type:'IP_ADDRESS', value:'192.0.2.1'} or {type:'SUBNET', value:'192.0.2.0/24'} (spec also: IP_ADDRESS_RANGE {start, stop}); IPV6_ADDRESSES lists take IP_ADDRESS or SUBNET. Idempotency: not safe to retry — re-running creates a duplicate list.",
|
|
65
69
|
inputSchema: {
|
|
66
70
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
67
71
|
type: zod_1.z
|
|
68
72
|
.enum(["PORTS", "IPV4_ADDRESSES", "IPV6_ADDRESSES"])
|
|
69
73
|
.describe("List type"),
|
|
70
74
|
name: zod_1.z.string().min(1).describe("List name"),
|
|
71
|
-
items:
|
|
72
|
-
.array(zod_1.z.unknown())
|
|
73
|
-
.min(1)
|
|
74
|
-
.describe("List items (ports or IP addresses)"),
|
|
75
|
+
items: matchingItems,
|
|
75
76
|
dryRun: zod_1.z
|
|
76
77
|
.boolean()
|
|
77
78
|
.optional()
|
|
@@ -92,7 +93,7 @@ function registerTrafficMatchingTools(server, client, readOnly = false) {
|
|
|
92
93
|
}
|
|
93
94
|
});
|
|
94
95
|
server.registerTool("unifi_update_traffic_matching_list", {
|
|
95
|
-
description: "
|
|
96
|
+
description: "Replace a traffic matching list (full PUT — type, name and the complete items array are all required; read the current list with unifi_get_traffic_matching_list first). Items are tagged objects, not strings (live-verified 11.0.83): PORTS lists take {type:'PORT_NUMBER', value:443} (the older 10.4.57 spec also lists {type:'PORT_NUMBER_RANGE', start, stop}); IPV4_ADDRESSES lists take {type:'IP_ADDRESS', value:'192.0.2.1'} or {type:'SUBNET', value:'192.0.2.0/24'} (spec also: IP_ADDRESS_RANGE {start, stop}); IPV6_ADDRESSES lists take IP_ADDRESS or SUBNET.",
|
|
96
97
|
inputSchema: {
|
|
97
98
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
98
99
|
trafficMatchingListId: zod_1.z.string().describe("Traffic matching list ID"),
|
|
@@ -100,7 +101,7 @@ function registerTrafficMatchingTools(server, client, readOnly = false) {
|
|
|
100
101
|
.enum(["PORTS", "IPV4_ADDRESSES", "IPV6_ADDRESSES"])
|
|
101
102
|
.describe("List type"),
|
|
102
103
|
name: zod_1.z.string().min(1).describe("List name"),
|
|
103
|
-
items:
|
|
104
|
+
items: matchingItems,
|
|
104
105
|
dryRun: zod_1.z
|
|
105
106
|
.boolean()
|
|
106
107
|
.optional()
|