@owine/unifi-network-mcp 2.11.4 → 2.13.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 +1 -1
- package/dist/tools/acl.js +2 -2
- package/dist/tools/clients.js +4 -4
- package/dist/tools/devices.js +8 -6
- package/dist/tools/dns-policies.js +2 -2
- package/dist/tools/firewall.js +3 -3
- package/dist/tools/hotspot.js +4 -4
- package/dist/tools/networks.js +3 -3
- package/dist/tools/sites.js +2 -2
- package/dist/tools/supporting.js +11 -11
- package/dist/tools/switching.js +5 -5
- package/dist/tools/system.js +1 -1
- package/dist/tools/traffic-matching.js +1 -1
- package/dist/tools/wifi.js +6 -6
- package/dist/utils/output-schemas.d.ts +9 -1
- package/dist/utils/output-schemas.js +14 -4
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ This server provides layered safety controls for responsible operation:
|
|
|
81
81
|
|
|
82
82
|
58 of the 74 tools (all 41 read tools, plus the 17 write tools whose API responses return the affected resource) declare an MCP `outputSchema` and return `structuredContent` alongside the usual text content. Clients that understand structured output get typed, machine-readable results instead of parsing JSON out of a text blob.
|
|
83
83
|
|
|
84
|
-
The schemas live in `src/utils/output-schemas.ts` and are verified against UniFi Network API 10.
|
|
84
|
+
The schemas live in `src/utils/output-schemas.ts` and are verified against UniFi Network API 10.6.106. They deliberately use a **loose strategy**: every non-key field is optional and nested objects use `.passthrough()`, so firmware- and hardware-specific fields flow through unchanged rather than being stripped or triggering a validation error. This keeps the contract stable across console versions and hardware models.
|
|
85
85
|
|
|
86
86
|
## Tools (74 total)
|
|
87
87
|
|
package/dist/tools/acl.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 registerAclTools(server, client, readOnly = false) {
|
|
10
10
|
server.registerTool("unifi_list_acl_rules", {
|
|
11
|
-
description: "List ACL rules (switch/AP-level access control lists, distinct from zone-based firewall policies) at a site. Returns: id, type (IPV4/MAC), name, enabled, action (ALLOW/BLOCK), description, protocolFilter,
|
|
11
|
+
description: "List ACL rules (switch/AP-level access control lists, distinct from zone-based firewall policies) at a site. Returns: id, type (IPV4/MAC), name, enabled, action (ALLOW/BLOCK), description, protocolFilter, sourceFilter/destinationFilter matchers, enforcingDeviceFilter (which devices apply the rule), index (evaluation order — read-only; change it with unifi_reorder_acl_rules), metadata.origin. ACLs apply earlier in the path than firewall policies.",
|
|
12
12
|
inputSchema: {
|
|
13
13
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
14
14
|
offset: zod_1.z
|
|
@@ -20,7 +20,7 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
20
20
|
limit: zod_1.z
|
|
21
21
|
.number()
|
|
22
22
|
.int()
|
|
23
|
-
.min(
|
|
23
|
+
.min(0)
|
|
24
24
|
.max(200)
|
|
25
25
|
.optional()
|
|
26
26
|
.describe("Number of records to return (default: 25, max: 200)"),
|
package/dist/tools/clients.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 registerClientTools(server, client, readOnly = false) {
|
|
10
10
|
server.registerTool("unifi_list_clients", {
|
|
11
|
-
description: "List currently connected clients at a site. Returns per client: id, name, type (WIRED/WIRELESS/VPN/TELEPORT), macAddress, ipAddress, connectedAt, uplinkDeviceId (the switch/AP they're attached to), access.type. NOTE: verified against 10.
|
|
11
|
+
description: "List currently connected clients at a site. Returns per client: id, name, type (WIRED/WIRELESS/VPN/TELEPORT), macAddress, ipAddress (frequently absent — do not rely on it being present), connectedAt, uplinkDeviceId (the switch/AP they're attached to), access.type. NOTE: verified against 10.6.106 — the Integration API client schema is minimal and identical across types; it does NOT expose signal strength, channel, or per-port binding. Use for: who's online right now. Disconnected/historical clients are NOT in the Integration API.",
|
|
12
12
|
inputSchema: {
|
|
13
13
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
14
14
|
offset: zod_1.z
|
|
@@ -20,7 +20,7 @@ function registerClientTools(server, client, readOnly = false) {
|
|
|
20
20
|
limit: zod_1.z
|
|
21
21
|
.number()
|
|
22
22
|
.int()
|
|
23
|
-
.min(
|
|
23
|
+
.min(0)
|
|
24
24
|
.max(200)
|
|
25
25
|
.optional()
|
|
26
26
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -58,7 +58,7 @@ function registerClientTools(server, client, readOnly = false) {
|
|
|
58
58
|
if (readOnly)
|
|
59
59
|
return;
|
|
60
60
|
server.registerTool("unifi_authorize_guest", {
|
|
61
|
-
description: "Authorize a guest client through a captive-portal hotspot. Optional limits override hotspot defaults. Idempotency: not safe to retry — re-authorizing extends the session.",
|
|
61
|
+
description: "Authorize a guest client through a captive-portal hotspot. Optional limits override hotspot defaults. Returns (per 10.6.106 docs, not live-verified): action, grantedAuthorization and revokedAuthorization objects (authorizedAt, authorizationMethod, expiresAt, dataUsageLimitMBytes, rxRateLimitKbps, txRateLimitKbps, usage). Idempotency: not safe to retry — re-authorizing extends the session.",
|
|
62
62
|
inputSchema: {
|
|
63
63
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
64
64
|
clientId: zod_1.z.string().describe("Client ID"),
|
|
@@ -116,7 +116,7 @@ function registerClientTools(server, client, readOnly = false) {
|
|
|
116
116
|
}
|
|
117
117
|
});
|
|
118
118
|
server.registerTool("unifi_unauthorize_guest", {
|
|
119
|
-
description: "Revoke guest authorization; the client returns to the captive portal on next request. Idempotent.",
|
|
119
|
+
description: "Revoke guest authorization; the client returns to the captive portal on next request. Returns (per 10.6.106 docs, not live-verified): action plus a revokedAuthorization object. Idempotent.",
|
|
120
120
|
inputSchema: {
|
|
121
121
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
122
122
|
clientId: zod_1.z.string().describe("Client ID"),
|
package/dist/tools/devices.js
CHANGED
|
@@ -22,7 +22,7 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
22
22
|
limit: zod_1.z
|
|
23
23
|
.number()
|
|
24
24
|
.int()
|
|
25
|
-
.min(
|
|
25
|
+
.min(0)
|
|
26
26
|
.max(200)
|
|
27
27
|
.optional()
|
|
28
28
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -41,7 +41,7 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
server.registerTool("unifi_get_device", {
|
|
44
|
-
description: "Get full configuration for a device. Returns (in addition to list fields): supported, firmwareUpdatable, provisionedAt, configurationId, uplink.deviceId, features (object keyed by capability: switching {lags[]} / accessPoint {}), interfaces.ports[] for switches ({idx, state, connector, maxSpeedMbps, speedMbps, poe:{standard, type, enabled, state}}), interfaces.radios[] for APs ({wlanStandard, frequencyGHz, channelWidthMHz, channel}). NOTE: in the LIST endpoint, features/interfaces are capability-tag string arrays instead. Use for: switch port layout/PoE state, AP radio config, uplink topology. For live throughput/CPU/memory, use unifi_get_device_statistics.",
|
|
44
|
+
description: "Get full configuration for a device. Returns (in addition to list fields): supported, firmwareUpdatable, adoptedAt, provisionedAt, configurationId, uplink.deviceId, features (object keyed by capability: switching {lags[]} / accessPoint {}), interfaces.ports[] for switches ({idx, state, connector, maxSpeedMbps, speedMbps, poe:{standard, type, enabled, state}}), interfaces.radios[] for APs ({wlanStandard, frequencyGHz, channelWidthMHz, channel}). NOTE: in the LIST endpoint, features/interfaces are capability-tag string arrays instead. Use for: switch port layout/PoE state, AP radio config, uplink topology. For live throughput/CPU/memory, use unifi_get_device_statistics.",
|
|
45
45
|
inputSchema: {
|
|
46
46
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
47
47
|
deviceId: zod_1.z.string().describe("Device ID"),
|
|
@@ -58,7 +58,7 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
server.registerTool("unifi_get_device_statistics", {
|
|
61
|
-
description: "Get latest live statistics for a device. Returns: uptimeSec, lastHeartbeatAt, nextHeartbeatAt, loadAverage1/5/15Min, cpuUtilizationPct, memoryUtilizationPct, uplink (txRateBps, rxRateBps), interfaces.radios[] for APs ({frequencyGHz, txRetriesPct}). NOTE: verified against 10.
|
|
61
|
+
description: "Get latest live statistics for a device. Returns: uptimeSec, lastHeartbeatAt, nextHeartbeatAt, loadAverage1/5/15Min, cpuUtilizationPct, memoryUtilizationPct, uplink (txRateBps, rxRateBps), interfaces.radios[] for APs ({frequencyGHz, txRetriesPct}). NOTE: verified against 10.6.106 — the Integration API does NOT expose per-switch-port byte/error/PoE-power counters here; port-level live stats are unavailable. Use for: device health and AP radio metrics. For config (channel, power, port assignment), use unifi_get_device.",
|
|
62
62
|
inputSchema: {
|
|
63
63
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
64
64
|
deviceId: zod_1.z.string().describe("Device ID"),
|
|
@@ -75,7 +75,7 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
77
|
server.registerTool("unifi_list_pending_devices", {
|
|
78
|
-
description: "List devices pending adoption across all sites (global endpoint, not site-scoped). Returns: basic device info per pending device (macAddress, model, ipAddress, firmwareVersion, etc. — exact per-row schema is not rendered in the 10.
|
|
78
|
+
description: "List devices pending adoption across all sites (global endpoint, not site-scoped). Returns: basic device info per pending device (macAddress, model, ipAddress, firmwareVersion, etc. — exact per-row schema is not rendered in the 10.6.106 docs). Use for: discovering new devices on the network before calling unifi_adopt_device.",
|
|
79
79
|
inputSchema: {
|
|
80
80
|
offset: zod_1.z
|
|
81
81
|
.number()
|
|
@@ -86,7 +86,7 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
86
86
|
limit: zod_1.z
|
|
87
87
|
.number()
|
|
88
88
|
.int()
|
|
89
|
-
.min(
|
|
89
|
+
.min(0)
|
|
90
90
|
.max(200)
|
|
91
91
|
.optional()
|
|
92
92
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -111,7 +111,9 @@ function registerDeviceTools(server, client, readOnly = false) {
|
|
|
111
111
|
inputSchema: {
|
|
112
112
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
113
113
|
macAddress: zod_1.z.string().describe("MAC address of the device"),
|
|
114
|
-
|
|
114
|
+
// Required by the API; kept optional here and defaulted to false below
|
|
115
|
+
// so callers need not pass it explicitly.
|
|
116
|
+
ignoreDeviceLimit: zod_1.z.boolean().optional().describe("Ignore device limit when adopting (required by the API; sent as false when omitted)"),
|
|
115
117
|
dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
|
|
116
118
|
},
|
|
117
119
|
outputSchema: adoptDeviceOutputSchema,
|
|
@@ -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 registerDnsPolicyTools(server, client, readOnly = false) {
|
|
10
10
|
server.registerTool("unifi_list_dns_policies", {
|
|
11
|
-
description: "List DNS policies (local DNS records and forward rules served by the gateway) at a site. Returns: id, type (A_RECORD, AAAA_RECORD, CNAME_RECORD, MX_RECORD, TXT_RECORD, SRV_RECORD, FORWARD_DOMAIN), enabled, domain, ipv4Address, ttlSeconds.",
|
|
11
|
+
description: "List DNS policies (local DNS records and forward rules served by the gateway) at a site. Returns: id, type (A_RECORD, AAAA_RECORD, CNAME_RECORD, MX_RECORD, TXT_RECORD, SRV_RECORD, FORWARD_DOMAIN), enabled, domain, ipv4Address, ttlSeconds, metadata.origin.",
|
|
12
12
|
inputSchema: {
|
|
13
13
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
14
14
|
offset: zod_1.z
|
|
@@ -20,7 +20,7 @@ function registerDnsPolicyTools(server, client, readOnly = false) {
|
|
|
20
20
|
limit: zod_1.z
|
|
21
21
|
.number()
|
|
22
22
|
.int()
|
|
23
|
-
.min(
|
|
23
|
+
.min(0)
|
|
24
24
|
.max(200)
|
|
25
25
|
.optional()
|
|
26
26
|
.describe("Number of records to return (default: 25, max: 200)"),
|
package/dist/tools/firewall.js
CHANGED
|
@@ -20,7 +20,7 @@ function registerFirewallTools(server, client, readOnly = false) {
|
|
|
20
20
|
limit: zod_1.z
|
|
21
21
|
.number()
|
|
22
22
|
.int()
|
|
23
|
-
.min(
|
|
23
|
+
.min(0)
|
|
24
24
|
.max(200)
|
|
25
25
|
.optional()
|
|
26
26
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -71,7 +71,7 @@ function registerFirewallTools(server, client, readOnly = false) {
|
|
|
71
71
|
limit: zod_1.z
|
|
72
72
|
.number()
|
|
73
73
|
.int()
|
|
74
|
-
.min(
|
|
74
|
+
.min(0)
|
|
75
75
|
.max(200)
|
|
76
76
|
.optional()
|
|
77
77
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -270,7 +270,7 @@ function registerFirewallTools(server, client, readOnly = false) {
|
|
|
270
270
|
}
|
|
271
271
|
});
|
|
272
272
|
server.registerTool("unifi_patch_firewall_policy", {
|
|
273
|
-
description: "Partially update a firewall policy without resending all fields.
|
|
273
|
+
description: "Partially update a firewall policy without resending all fields. loggingEnabled is the only field documented as patchable in API 10.6.106; other fields may be rejected — use unifi_update_firewall_policy for a full update. Idempotent for fields supplied.",
|
|
274
274
|
inputSchema: {
|
|
275
275
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
276
276
|
firewallPolicyId: zod_1.z.string().describe("Firewall policy ID"),
|
package/dist/tools/hotspot.js
CHANGED
|
@@ -21,7 +21,7 @@ function registerHotspotTools(server, client, readOnly = false) {
|
|
|
21
21
|
limit: zod_1.z
|
|
22
22
|
.number()
|
|
23
23
|
.int()
|
|
24
|
-
.min(
|
|
24
|
+
.min(0)
|
|
25
25
|
.max(1000)
|
|
26
26
|
.optional()
|
|
27
27
|
.default(100)
|
|
@@ -143,7 +143,7 @@ function registerHotspotTools(server, client, readOnly = false) {
|
|
|
143
143
|
}
|
|
144
144
|
});
|
|
145
145
|
server.registerTool("unifi_delete_voucher", {
|
|
146
|
-
description: "DESTRUCTIVE: Delete a hotspot voucher",
|
|
146
|
+
description: "DESTRUCTIVE: Delete a hotspot voucher. Returns: vouchersDeleted (count of vouchers removed).",
|
|
147
147
|
inputSchema: {
|
|
148
148
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
149
149
|
voucherId: zod_1.z.string().describe("Voucher ID"),
|
|
@@ -173,12 +173,12 @@ function registerHotspotTools(server, client, readOnly = false) {
|
|
|
173
173
|
}
|
|
174
174
|
});
|
|
175
175
|
server.registerTool("unifi_bulk_delete_vouchers", {
|
|
176
|
-
description: "DESTRUCTIVE: Bulk delete hotspot vouchers based on filter criteria",
|
|
176
|
+
description: "DESTRUCTIVE: Bulk delete hotspot vouchers based on filter criteria. Returns: vouchersDeleted (count of vouchers removed).",
|
|
177
177
|
inputSchema: {
|
|
178
178
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
179
179
|
filter: zod_1.z
|
|
180
180
|
.string()
|
|
181
|
-
.describe("Required filter expression (e.g., 'expired.eq(true)',
|
|
181
|
+
.describe("Required filter expression (e.g., 'expired.eq(true)', name.like('guest*'))"),
|
|
182
182
|
confirm: zod_1.z
|
|
183
183
|
.boolean()
|
|
184
184
|
.optional()
|
package/dist/tools/networks.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 registerNetworkTools(server, client, readOnly = false) {
|
|
10
10
|
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), dhcpGuarding, metadata.origin. NOTE: the list view is sparse — for subnet/DHCP/NTP detail (ipv4Configuration), call unifi_get_network on a specific id. Use for: VLAN inventory; pair with unifi_get_network_references to find what consumes a network.",
|
|
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.",
|
|
12
12
|
inputSchema: {
|
|
13
13
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
14
14
|
offset: zod_1.z
|
|
@@ -20,7 +20,7 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
20
20
|
limit: zod_1.z
|
|
21
21
|
.number()
|
|
22
22
|
.int()
|
|
23
|
-
.min(
|
|
23
|
+
.min(0)
|
|
24
24
|
.max(200)
|
|
25
25
|
.optional()
|
|
26
26
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -42,7 +42,7 @@ function registerNetworkTools(server, client, readOnly = false) {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
server.registerTool("unifi_get_network", {
|
|
45
|
-
description: "Get a network/VLAN by ID. Returns the list fields PLUS (live-verified)
|
|
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).",
|
|
46
46
|
inputSchema: {
|
|
47
47
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
48
48
|
networkId: zod_1.z.string().describe("Network ID"),
|
package/dist/tools/sites.js
CHANGED
|
@@ -19,14 +19,14 @@ function registerSiteTools(server, client) {
|
|
|
19
19
|
limit: zod_1.z
|
|
20
20
|
.number()
|
|
21
21
|
.int()
|
|
22
|
-
.min(
|
|
22
|
+
.min(0)
|
|
23
23
|
.max(200)
|
|
24
24
|
.optional()
|
|
25
25
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
26
26
|
filter: zod_1.z
|
|
27
27
|
.string()
|
|
28
28
|
.optional()
|
|
29
|
-
.describe("Filter expression (e.g.,
|
|
29
|
+
.describe("Filter expression (e.g., name.like('office*'))"),
|
|
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 10.
|
|
11
|
+
description: "List WAN interface definitions at a site. Returns: id, name only (verified against 10.6.106 — 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
|
|
@@ -20,7 +20,7 @@ function registerSupportingTools(server, client) {
|
|
|
20
20
|
limit: zod_1.z
|
|
21
21
|
.number()
|
|
22
22
|
.int()
|
|
23
|
-
.min(
|
|
23
|
+
.min(0)
|
|
24
24
|
.max(200)
|
|
25
25
|
.optional()
|
|
26
26
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -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 10.
|
|
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 10.6.106 docs — 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
|
|
@@ -50,7 +50,7 @@ function registerSupportingTools(server, client) {
|
|
|
50
50
|
limit: zod_1.z
|
|
51
51
|
.number()
|
|
52
52
|
.int()
|
|
53
|
-
.min(
|
|
53
|
+
.min(0)
|
|
54
54
|
.max(200)
|
|
55
55
|
.optional()
|
|
56
56
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -84,7 +84,7 @@ function registerSupportingTools(server, client) {
|
|
|
84
84
|
limit: zod_1.z
|
|
85
85
|
.number()
|
|
86
86
|
.int()
|
|
87
|
-
.min(
|
|
87
|
+
.min(0)
|
|
88
88
|
.max(200)
|
|
89
89
|
.optional()
|
|
90
90
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -118,7 +118,7 @@ function registerSupportingTools(server, client) {
|
|
|
118
118
|
limit: zod_1.z
|
|
119
119
|
.number()
|
|
120
120
|
.int()
|
|
121
|
-
.min(
|
|
121
|
+
.min(0)
|
|
122
122
|
.max(200)
|
|
123
123
|
.optional()
|
|
124
124
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -152,7 +152,7 @@ function registerSupportingTools(server, client) {
|
|
|
152
152
|
limit: zod_1.z
|
|
153
153
|
.number()
|
|
154
154
|
.int()
|
|
155
|
-
.min(
|
|
155
|
+
.min(0)
|
|
156
156
|
.max(200)
|
|
157
157
|
.optional()
|
|
158
158
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -185,7 +185,7 @@ function registerSupportingTools(server, client) {
|
|
|
185
185
|
limit: zod_1.z
|
|
186
186
|
.number()
|
|
187
187
|
.int()
|
|
188
|
-
.min(
|
|
188
|
+
.min(0)
|
|
189
189
|
.max(200)
|
|
190
190
|
.optional()
|
|
191
191
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -218,7 +218,7 @@ function registerSupportingTools(server, client) {
|
|
|
218
218
|
limit: zod_1.z
|
|
219
219
|
.number()
|
|
220
220
|
.int()
|
|
221
|
-
.min(
|
|
221
|
+
.min(0)
|
|
222
222
|
.max(200)
|
|
223
223
|
.optional()
|
|
224
224
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -251,14 +251,14 @@ function registerSupportingTools(server, client) {
|
|
|
251
251
|
limit: zod_1.z
|
|
252
252
|
.number()
|
|
253
253
|
.int()
|
|
254
|
-
.min(
|
|
254
|
+
.min(0)
|
|
255
255
|
.max(200)
|
|
256
256
|
.optional()
|
|
257
257
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
258
258
|
filter: zod_1.z
|
|
259
259
|
.string()
|
|
260
260
|
.optional()
|
|
261
|
-
.describe("Filter expression (e.g.,
|
|
261
|
+
.describe("Filter expression (e.g., name.like('United*'))"),
|
|
262
262
|
},
|
|
263
263
|
outputSchema: output_schemas_js_1.listCountriesOutputSchema,
|
|
264
264
|
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, name,
|
|
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 10.6.106 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
|
|
@@ -20,7 +20,7 @@ function registerSwitchingTools(server, client) {
|
|
|
20
20
|
limit: zod_1.z
|
|
21
21
|
.number()
|
|
22
22
|
.int()
|
|
23
|
-
.min(
|
|
23
|
+
.min(0)
|
|
24
24
|
.max(200)
|
|
25
25
|
.optional()
|
|
26
26
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -42,7 +42,7 @@ function registerSwitchingTools(server, client) {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
server.registerTool("unifi_get_switch_stack", {
|
|
45
|
-
description: "Get full details of a Switch Stack including all
|
|
45
|
+
description: "Get full details of a Switch Stack including all units and stacking topology. Returns the same fields as the list response but for a single stack: id, deviceId, name, units[] (named `members` before 10.6.106), lags[], metadata.origin.",
|
|
46
46
|
inputSchema: {
|
|
47
47
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
48
48
|
switchStackId: zod_1.z.string().describe("Switch Stack ID"),
|
|
@@ -71,7 +71,7 @@ function registerSwitchingTools(server, client) {
|
|
|
71
71
|
limit: zod_1.z
|
|
72
72
|
.number()
|
|
73
73
|
.int()
|
|
74
|
-
.min(
|
|
74
|
+
.min(0)
|
|
75
75
|
.max(200)
|
|
76
76
|
.optional()
|
|
77
77
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -122,7 +122,7 @@ function registerSwitchingTools(server, client) {
|
|
|
122
122
|
limit: zod_1.z
|
|
123
123
|
.number()
|
|
124
124
|
.int()
|
|
125
|
-
.min(
|
|
125
|
+
.min(0)
|
|
126
126
|
.max(200)
|
|
127
127
|
.optional()
|
|
128
128
|
.describe("Number of records to return (default: 25, max: 200)"),
|
package/dist/tools/system.js
CHANGED
|
@@ -6,7 +6,7 @@ const safety_js_1 = require("../utils/safety.js");
|
|
|
6
6
|
const output_schemas_js_1 = require("../utils/output-schemas.js");
|
|
7
7
|
function registerSystemTools(server, client) {
|
|
8
8
|
server.registerTool("unifi_get_info", {
|
|
9
|
-
description: "Get UniFi Network application info. Returns: applicationVersion. NOTE: verified against 10.
|
|
9
|
+
description: "Get UniFi Network application info. Returns: applicationVersion. NOTE: verified against 10.6.106 on a UniFi OS console — the Integration API returns ONLY applicationVersion here; there is no isUniFiOSConsole or other field. Use for: version checks before calling version-gated tools.",
|
|
10
10
|
inputSchema: {},
|
|
11
11
|
outputSchema: output_schemas_js_1.getInfoOutputSchema,
|
|
12
12
|
annotations: safety_js_1.READ_ONLY,
|
package/dist/tools/wifi.js
CHANGED
|
@@ -20,7 +20,7 @@ function registerWifiTools(server, client, readOnly = false) {
|
|
|
20
20
|
limit: zod_1.z
|
|
21
21
|
.number()
|
|
22
22
|
.int()
|
|
23
|
-
.min(
|
|
23
|
+
.min(0)
|
|
24
24
|
.max(200)
|
|
25
25
|
.optional()
|
|
26
26
|
.describe("Number of records to return (default: 25, max: 200)"),
|
|
@@ -68,8 +68,8 @@ function registerWifiTools(server, client, readOnly = false) {
|
|
|
68
68
|
enabled: zod_1.z.boolean().describe("Enable the WiFi network"),
|
|
69
69
|
type: zod_1.z.enum(["STANDARD", "IOT_OPTIMIZED"]).describe("WiFi type"),
|
|
70
70
|
broadcastingFrequenciesGHz: zod_1.z
|
|
71
|
-
.array(zod_1.z.
|
|
72
|
-
.describe("Frequencies
|
|
71
|
+
.array(zod_1.z.enum(["2.4", "5", "6"]))
|
|
72
|
+
.describe("Frequencies to broadcast on. Accepted as strings and sent to the API as numbers (2.4, 5, 6)."),
|
|
73
73
|
securityConfiguration: zod_1.z
|
|
74
74
|
.record(zod_1.z.string(), zod_1.z.unknown())
|
|
75
75
|
.describe("Security configuration object"),
|
|
@@ -209,7 +209,7 @@ function registerWifiTools(server, client, readOnly = false) {
|
|
|
209
209
|
}
|
|
210
210
|
});
|
|
211
211
|
server.registerTool("unifi_update_wifi", {
|
|
212
|
-
description: "Update an existing WiFi network",
|
|
212
|
+
description: "Update an existing WiFi network. NOTE: this is a full PUT — API 10.6.106 documents 14 of these fields as required (type, name, enabled, securityConfiguration, multicastToUnicastConversionEnabled, clientIsolationEnabled, hideName, uapsdEnabled, channel2gLockedTo6, dtimPeriod2gLockedTo3, broadcastingFrequenciesGHz, arpProxyEnabled, bssTransitionEnabled, advertiseDeviceName). They are optional here for convenience, but a sparse update may be rejected by the API — read the current values with unifi_get_wifi and resend the full set if you get a 400.",
|
|
213
213
|
inputSchema: {
|
|
214
214
|
siteId: zod_1.z.string().describe("Site ID"),
|
|
215
215
|
wifiBroadcastId: zod_1.z.string().describe("WiFi Broadcast ID"),
|
|
@@ -220,9 +220,9 @@ function registerWifiTools(server, client, readOnly = false) {
|
|
|
220
220
|
.optional()
|
|
221
221
|
.describe("WiFi type"),
|
|
222
222
|
broadcastingFrequenciesGHz: zod_1.z
|
|
223
|
-
.array(zod_1.z.
|
|
223
|
+
.array(zod_1.z.enum(["2.4", "5", "6"]))
|
|
224
224
|
.optional()
|
|
225
|
-
.describe("Frequencies
|
|
225
|
+
.describe("Frequencies to broadcast on. Accepted as strings and sent to the API as numbers (2.4, 5, 6)."),
|
|
226
226
|
securityConfiguration: zod_1.z
|
|
227
227
|
.record(zod_1.z.string(), zod_1.z.unknown())
|
|
228
228
|
.optional()
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* field optional, every nested object uses .passthrough() to allow
|
|
4
4
|
* firmware/hardware-specific fields to flow through unchanged).
|
|
5
5
|
*
|
|
6
|
-
* Verified against UniFi Network API 10.
|
|
6
|
+
* Verified against UniFi Network API 10.6.106. Where the docs collapse
|
|
7
7
|
* nested arrays/objects (e.g. interfaces.ports[], radios[]), the schema
|
|
8
8
|
* uses passthrough records so the contract doesn't lock to fields we
|
|
9
9
|
* haven't verified.
|
|
@@ -37,6 +37,7 @@ export declare const networkOutputSchema: {
|
|
|
37
37
|
cellularBackupEnabled: z.ZodOptional<z.ZodBoolean>;
|
|
38
38
|
dhcpGuarding: z.ZodOptional<z.ZodUnknown>;
|
|
39
39
|
ipv4Configuration: z.ZodOptional<z.ZodUnknown>;
|
|
40
|
+
ipv6Configuration: z.ZodOptional<z.ZodUnknown>;
|
|
40
41
|
metadata: z.ZodOptional<z.ZodObject<{
|
|
41
42
|
origin: z.ZodOptional<z.ZodString>;
|
|
42
43
|
}, z.core.$loose>>;
|
|
@@ -60,6 +61,7 @@ export declare const listNetworksOutputSchema: {
|
|
|
60
61
|
cellularBackupEnabled: z.ZodOptional<z.ZodBoolean>;
|
|
61
62
|
dhcpGuarding: z.ZodOptional<z.ZodUnknown>;
|
|
62
63
|
ipv4Configuration: z.ZodOptional<z.ZodUnknown>;
|
|
64
|
+
ipv6Configuration: z.ZodOptional<z.ZodUnknown>;
|
|
63
65
|
metadata: z.ZodOptional<z.ZodObject<{
|
|
64
66
|
origin: z.ZodOptional<z.ZodString>;
|
|
65
67
|
}, z.core.$loose>>;
|
|
@@ -202,6 +204,7 @@ export declare const aclRuleOutputSchema: {
|
|
|
202
204
|
sourceFilter: z.ZodOptional<z.ZodUnknown>;
|
|
203
205
|
destinationFilter: z.ZodOptional<z.ZodUnknown>;
|
|
204
206
|
enforcingDeviceFilter: z.ZodOptional<z.ZodUnknown>;
|
|
207
|
+
index: z.ZodOptional<z.ZodNumber>;
|
|
205
208
|
metadata: z.ZodOptional<z.ZodObject<{
|
|
206
209
|
origin: z.ZodOptional<z.ZodString>;
|
|
207
210
|
}, z.core.$loose>>;
|
|
@@ -222,6 +225,7 @@ export declare const listAclRulesOutputSchema: {
|
|
|
222
225
|
sourceFilter: z.ZodOptional<z.ZodUnknown>;
|
|
223
226
|
destinationFilter: z.ZodOptional<z.ZodUnknown>;
|
|
224
227
|
enforcingDeviceFilter: z.ZodOptional<z.ZodUnknown>;
|
|
228
|
+
index: z.ZodOptional<z.ZodNumber>;
|
|
225
229
|
metadata: z.ZodOptional<z.ZodObject<{
|
|
226
230
|
origin: z.ZodOptional<z.ZodString>;
|
|
227
231
|
}, z.core.$loose>>;
|
|
@@ -233,6 +237,8 @@ export declare const aclRuleOrderingOutputSchema: {
|
|
|
233
237
|
export declare const switchStackOutputSchema: {
|
|
234
238
|
id: z.ZodString;
|
|
235
239
|
name: z.ZodOptional<z.ZodString>;
|
|
240
|
+
deviceId: z.ZodOptional<z.ZodString>;
|
|
241
|
+
units: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
236
242
|
members: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
237
243
|
lags: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
238
244
|
metadata: z.ZodOptional<z.ZodObject<{
|
|
@@ -247,6 +253,8 @@ export declare const listSwitchStacksOutputSchema: {
|
|
|
247
253
|
readonly data: z.ZodArray<z.ZodObject<{
|
|
248
254
|
id: z.ZodString;
|
|
249
255
|
name: z.ZodOptional<z.ZodString>;
|
|
256
|
+
deviceId: z.ZodOptional<z.ZodString>;
|
|
257
|
+
units: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
250
258
|
members: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
251
259
|
lags: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
252
260
|
metadata: z.ZodOptional<z.ZodObject<{
|
|
@@ -6,7 +6,7 @@ exports.listPendingDevicesOutputSchema = exports.listCountriesOutputSchema = exp
|
|
|
6
6
|
* field optional, every nested object uses .passthrough() to allow
|
|
7
7
|
* firmware/hardware-specific fields to flow through unchanged).
|
|
8
8
|
*
|
|
9
|
-
* Verified against UniFi Network API 10.
|
|
9
|
+
* Verified against UniFi Network API 10.6.106. Where the docs collapse
|
|
10
10
|
* nested arrays/objects (e.g. interfaces.ports[], radios[]), the schema
|
|
11
11
|
* uses passthrough records so the contract doesn't lock to fields we
|
|
12
12
|
* haven't verified.
|
|
@@ -160,7 +160,7 @@ const WifiBroadcast = zod_1.z
|
|
|
160
160
|
.passthrough();
|
|
161
161
|
const ApplicationInfo = zod_1.z
|
|
162
162
|
.object({
|
|
163
|
-
// Live-verified (10.
|
|
163
|
+
// Live-verified (10.6.106): only applicationVersion is returned.
|
|
164
164
|
applicationVersion: zod_1.z.string().optional(),
|
|
165
165
|
})
|
|
166
166
|
.passthrough();
|
|
@@ -185,8 +185,9 @@ const Network = zod_1.z
|
|
|
185
185
|
mdnsForwardingEnabled: zod_1.z.boolean().optional(),
|
|
186
186
|
cellularBackupEnabled: zod_1.z.boolean().optional(),
|
|
187
187
|
dhcpGuarding: zod_1.z.unknown().optional(),
|
|
188
|
-
// get-by-id only (absent in list view); nested
|
|
188
|
+
// get-by-id only (absent in list view); nested shapes left loose.
|
|
189
189
|
ipv4Configuration: zod_1.z.unknown().optional(),
|
|
190
|
+
ipv6Configuration: zod_1.z.unknown().optional(),
|
|
190
191
|
metadata: Metadata.optional(),
|
|
191
192
|
})
|
|
192
193
|
.passthrough();
|
|
@@ -263,6 +264,9 @@ const AclRule = zod_1.z
|
|
|
263
264
|
sourceFilter: zod_1.z.unknown().optional(),
|
|
264
265
|
destinationFilter: zod_1.z.unknown().optional(),
|
|
265
266
|
enforcingDeviceFilter: zod_1.z.unknown().optional(),
|
|
267
|
+
// Read-only in the response. Deprecated as a *request* field — ordering
|
|
268
|
+
// is set via the dedicated reorder endpoint, so no write tool accepts it.
|
|
269
|
+
index: zod_1.z.number().int().optional(),
|
|
266
270
|
metadata: Metadata.optional(),
|
|
267
271
|
})
|
|
268
272
|
.passthrough();
|
|
@@ -275,6 +279,12 @@ const SwitchStack = zod_1.z
|
|
|
275
279
|
.object({
|
|
276
280
|
id: zod_1.z.string(),
|
|
277
281
|
name: zod_1.z.string().optional(),
|
|
282
|
+
// The stack's primary device. Added in 10.6.106.
|
|
283
|
+
deviceId: zod_1.z.string().optional(),
|
|
284
|
+
// Renamed from `members` in 10.6.106 (was `members` through 10.4.57).
|
|
285
|
+
// `members` stays declared so responses from older consoles still
|
|
286
|
+
// describe themselves; both are optional, so either shape validates.
|
|
287
|
+
units: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
278
288
|
members: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
279
289
|
lags: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
280
290
|
metadata: Metadata.optional(),
|
|
@@ -324,7 +334,7 @@ const TrafficMatchingList = zod_1.z
|
|
|
324
334
|
metadata: Metadata.optional(),
|
|
325
335
|
})
|
|
326
336
|
.passthrough();
|
|
327
|
-
// Verified against the live Integration API (10.
|
|
337
|
+
// Verified against the live Integration API (10.6.106). The WAN and
|
|
328
338
|
// site-to-site-tunnel list rows are intentionally minimal in the API.
|
|
329
339
|
const Wan = zod_1.z
|
|
330
340
|
.object({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owine/unifi-network-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "MCP server for the UniFi Network API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": "^22.13.0 || ^24.0.0"
|
|
24
24
|
},
|
|
25
|
-
"packageManager": "pnpm@
|
|
25
|
+
"packageManager": "pnpm@12.4.1",
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
28
|
"url": "git+https://github.com/owine/unifi-network-mcp.git"
|
|
@@ -38,17 +38,17 @@
|
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
41
|
-
"zod": "4.
|
|
41
|
+
"zod": "4.6.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@eslint/js": "10.0.1",
|
|
45
|
-
"@types/node": "24.13.
|
|
45
|
+
"@types/node": "24.13.4",
|
|
46
46
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
47
|
-
"@vitest/coverage-v8": "
|
|
48
|
-
"eslint": "10.
|
|
47
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
48
|
+
"eslint": "10.10.0",
|
|
49
49
|
"lefthook": "2.1.12",
|
|
50
50
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
51
|
-
"typescript-eslint": "8.
|
|
52
|
-
"vitest": "
|
|
51
|
+
"typescript-eslint": "8.70.0",
|
|
52
|
+
"vitest": "5.0.0"
|
|
53
53
|
}
|
|
54
54
|
}
|