@owine/unifi-network-mcp 2.7.4 → 2.8.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/dist/tools/acl.js
CHANGED
|
@@ -92,6 +92,18 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
92
92
|
.array(zod_1.z.enum(["TCP", "UDP"]))
|
|
93
93
|
.optional()
|
|
94
94
|
.describe("Protocols this ACL rule will be applied to (TCP, UDP). When null, applies to all protocols"),
|
|
95
|
+
sourceFilter: zod_1.z
|
|
96
|
+
.unknown()
|
|
97
|
+
.optional()
|
|
98
|
+
.describe("Traffic source filter (opaque object — pass the structure the API expects)"),
|
|
99
|
+
destinationFilter: zod_1.z
|
|
100
|
+
.unknown()
|
|
101
|
+
.optional()
|
|
102
|
+
.describe("Traffic destination filter (opaque object — pass the structure the API expects)"),
|
|
103
|
+
enforcingDeviceFilter: zod_1.z
|
|
104
|
+
.unknown()
|
|
105
|
+
.optional()
|
|
106
|
+
.describe("IDs of the Switch-capable devices used to enforce the ACL rule. When null/omitted, the rule is provisioned to all switches on the site"),
|
|
95
107
|
dryRun: zod_1.z
|
|
96
108
|
.boolean()
|
|
97
109
|
.optional()
|
|
@@ -99,13 +111,19 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
99
111
|
},
|
|
100
112
|
outputSchema: output_schemas_js_1.aclRuleOutputSchema,
|
|
101
113
|
annotations: safety_js_1.WRITE_NOT_IDEMPOTENT,
|
|
102
|
-
}, async ({ siteId, type, name, enabled, action, description, protocolFilter, dryRun }) => {
|
|
114
|
+
}, async ({ siteId, type, name, enabled, action, description, protocolFilter, sourceFilter, destinationFilter, enforcingDeviceFilter, dryRun }) => {
|
|
103
115
|
try {
|
|
104
116
|
const body = { type, name, enabled, action };
|
|
105
117
|
if (description !== undefined)
|
|
106
118
|
body.description = description;
|
|
107
119
|
if (protocolFilter !== undefined)
|
|
108
120
|
body.protocolFilter = protocolFilter;
|
|
121
|
+
if (sourceFilter !== undefined)
|
|
122
|
+
body.sourceFilter = sourceFilter;
|
|
123
|
+
if (destinationFilter !== undefined)
|
|
124
|
+
body.destinationFilter = destinationFilter;
|
|
125
|
+
if (enforcingDeviceFilter !== undefined)
|
|
126
|
+
body.enforcingDeviceFilter = enforcingDeviceFilter;
|
|
109
127
|
if (dryRun)
|
|
110
128
|
return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/acl-rules`, body);
|
|
111
129
|
const data = await client.post(`/sites/${siteId}/acl-rules`, body);
|
|
@@ -132,6 +150,18 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
132
150
|
.array(zod_1.z.enum(["TCP", "UDP"]))
|
|
133
151
|
.optional()
|
|
134
152
|
.describe("Protocols this ACL rule will be applied to (TCP, UDP). When null, applies to all protocols"),
|
|
153
|
+
sourceFilter: zod_1.z
|
|
154
|
+
.unknown()
|
|
155
|
+
.optional()
|
|
156
|
+
.describe("Traffic source filter (opaque object — pass the structure the API expects)"),
|
|
157
|
+
destinationFilter: zod_1.z
|
|
158
|
+
.unknown()
|
|
159
|
+
.optional()
|
|
160
|
+
.describe("Traffic destination filter (opaque object — pass the structure the API expects)"),
|
|
161
|
+
enforcingDeviceFilter: zod_1.z
|
|
162
|
+
.unknown()
|
|
163
|
+
.optional()
|
|
164
|
+
.describe("IDs of the Switch-capable devices used to enforce the ACL rule. When null/omitted, the rule is provisioned to all switches on the site"),
|
|
135
165
|
dryRun: zod_1.z
|
|
136
166
|
.boolean()
|
|
137
167
|
.optional()
|
|
@@ -139,13 +169,19 @@ function registerAclTools(server, client, readOnly = false) {
|
|
|
139
169
|
},
|
|
140
170
|
outputSchema: output_schemas_js_1.aclRuleOutputSchema,
|
|
141
171
|
annotations: safety_js_1.WRITE,
|
|
142
|
-
}, async ({ siteId, aclRuleId, type, name, enabled, action, description, protocolFilter, dryRun }) => {
|
|
172
|
+
}, async ({ siteId, aclRuleId, type, name, enabled, action, description, protocolFilter, sourceFilter, destinationFilter, enforcingDeviceFilter, dryRun }) => {
|
|
143
173
|
try {
|
|
144
174
|
const body = { type, name, enabled, action };
|
|
145
175
|
if (description !== undefined)
|
|
146
176
|
body.description = description;
|
|
147
177
|
if (protocolFilter !== undefined)
|
|
148
178
|
body.protocolFilter = protocolFilter;
|
|
179
|
+
if (sourceFilter !== undefined)
|
|
180
|
+
body.sourceFilter = sourceFilter;
|
|
181
|
+
if (destinationFilter !== undefined)
|
|
182
|
+
body.destinationFilter = destinationFilter;
|
|
183
|
+
if (enforcingDeviceFilter !== undefined)
|
|
184
|
+
body.enforcingDeviceFilter = enforcingDeviceFilter;
|
|
149
185
|
if (dryRun)
|
|
150
186
|
return (0, safety_js_1.formatDryRun)("PUT", `/sites/${siteId}/acl-rules/${aclRuleId}`, body);
|
|
151
187
|
const data = await client.put(`/sites/${siteId}/acl-rules/${aclRuleId}`, body);
|
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, connectedAt, uplinkDeviceId (the switch/AP they're attached to), access.type. NOTE: verified against 10.5.43 — 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
|
package/dist/tools/devices.js
CHANGED
|
@@ -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.5.43 — 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.5.43 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()
|
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.5.43 — 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 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.5.43 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
|
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.5.43 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,
|
|
@@ -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.5.43. 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.
|
|
@@ -105,7 +105,7 @@ export declare const listVouchersOutputSchema: {
|
|
|
105
105
|
readonly totalCount: z.ZodOptional<z.ZodNumber>;
|
|
106
106
|
};
|
|
107
107
|
export declare const createVouchersOutputSchema: {
|
|
108
|
-
readonly
|
|
108
|
+
readonly vouchers: z.ZodArray<z.ZodObject<{
|
|
109
109
|
id: z.ZodString;
|
|
110
110
|
code: z.ZodOptional<z.ZodString>;
|
|
111
111
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -120,10 +120,6 @@ export declare const createVouchersOutputSchema: {
|
|
|
120
120
|
authorizedGuestCount: z.ZodOptional<z.ZodNumber>;
|
|
121
121
|
expired: z.ZodOptional<z.ZodBoolean>;
|
|
122
122
|
}, z.core.$loose>>;
|
|
123
|
-
readonly offset: z.ZodOptional<z.ZodNumber>;
|
|
124
|
-
readonly limit: z.ZodOptional<z.ZodNumber>;
|
|
125
|
-
readonly count: z.ZodOptional<z.ZodNumber>;
|
|
126
|
-
readonly totalCount: z.ZodOptional<z.ZodNumber>;
|
|
127
123
|
};
|
|
128
124
|
export declare const firewallZoneOutputSchema: {
|
|
129
125
|
id: z.ZodString;
|
|
@@ -205,6 +201,7 @@ export declare const aclRuleOutputSchema: {
|
|
|
205
201
|
protocolFilter: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
206
202
|
sourceFilter: z.ZodOptional<z.ZodUnknown>;
|
|
207
203
|
destinationFilter: z.ZodOptional<z.ZodUnknown>;
|
|
204
|
+
enforcingDeviceFilter: z.ZodOptional<z.ZodUnknown>;
|
|
208
205
|
metadata: z.ZodOptional<z.ZodObject<{
|
|
209
206
|
origin: z.ZodOptional<z.ZodString>;
|
|
210
207
|
}, z.core.$loose>>;
|
|
@@ -220,6 +217,7 @@ export declare const listAclRulesOutputSchema: {
|
|
|
220
217
|
protocolFilter: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
221
218
|
sourceFilter: z.ZodOptional<z.ZodUnknown>;
|
|
222
219
|
destinationFilter: z.ZodOptional<z.ZodUnknown>;
|
|
220
|
+
enforcingDeviceFilter: z.ZodOptional<z.ZodUnknown>;
|
|
223
221
|
metadata: z.ZodOptional<z.ZodObject<{
|
|
224
222
|
origin: z.ZodOptional<z.ZodString>;
|
|
225
223
|
}, z.core.$loose>>;
|
|
@@ -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.5.43. 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.5.43): only applicationVersion is returned.
|
|
164
164
|
applicationVersion: zod_1.z.string().optional(),
|
|
165
165
|
})
|
|
166
166
|
.passthrough();
|
|
@@ -262,6 +262,7 @@ const AclRule = zod_1.z
|
|
|
262
262
|
protocolFilter: zod_1.z.array(zod_1.z.string()).optional(),
|
|
263
263
|
sourceFilter: zod_1.z.unknown().optional(),
|
|
264
264
|
destinationFilter: zod_1.z.unknown().optional(),
|
|
265
|
+
enforcingDeviceFilter: zod_1.z.unknown().optional(),
|
|
265
266
|
metadata: Metadata.optional(),
|
|
266
267
|
})
|
|
267
268
|
.passthrough();
|
|
@@ -323,7 +324,7 @@ const TrafficMatchingList = zod_1.z
|
|
|
323
324
|
metadata: Metadata.optional(),
|
|
324
325
|
})
|
|
325
326
|
.passthrough();
|
|
326
|
-
// Verified against the live Integration API (10.
|
|
327
|
+
// Verified against the live Integration API (10.5.43). The WAN and
|
|
327
328
|
// site-to-site-tunnel list rows are intentionally minimal in the API.
|
|
328
329
|
const Wan = zod_1.z
|
|
329
330
|
.object({
|
|
@@ -407,9 +408,12 @@ exports.listVouchersOutputSchema = {
|
|
|
407
408
|
...PaginationFields,
|
|
408
409
|
data: zod_1.z.array(Voucher),
|
|
409
410
|
};
|
|
411
|
+
// NOTE: the Generate Vouchers endpoint does NOT return the standard
|
|
412
|
+
// paginated envelope. Live-verified (10.5.43): it returns
|
|
413
|
+
// `{ "vouchers": [ ...Voucher ] }` with no offset/limit/count/totalCount.
|
|
414
|
+
// An over-tight schema here (e.g. requiring `data`) hard-fails every call.
|
|
410
415
|
exports.createVouchersOutputSchema = {
|
|
411
|
-
|
|
412
|
-
data: zod_1.z.array(Voucher),
|
|
416
|
+
vouchers: zod_1.z.array(Voucher),
|
|
413
417
|
};
|
|
414
418
|
exports.firewallZoneOutputSchema = FirewallZone.shape;
|
|
415
419
|
exports.listFirewallZonesOutputSchema = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owine/unifi-network-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.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@11.
|
|
25
|
+
"packageManager": "pnpm@11.8.0",
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
28
|
"url": "https://github.com/owine/unifi-network-mcp.git"
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@eslint/js": "10.0.1",
|
|
45
|
-
"@types/node": "24.13.
|
|
46
|
-
"@vitest/coverage-v8": "4.1.
|
|
47
|
-
"eslint": "10.
|
|
45
|
+
"@types/node": "24.13.2",
|
|
46
|
+
"@vitest/coverage-v8": "4.1.9",
|
|
47
|
+
"eslint": "10.5.0",
|
|
48
48
|
"lefthook": "2.1.9",
|
|
49
49
|
"typescript": "6.0.3",
|
|
50
|
-
"typescript-eslint": "8.
|
|
51
|
-
"vitest": "4.1.
|
|
50
|
+
"typescript-eslint": "8.61.1",
|
|
51
|
+
"vitest": "4.1.9"
|
|
52
52
|
}
|
|
53
53
|
}
|