@owine/unifi-protect-mcp 2.7.5 → 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/schemas/cameras.d.ts +18 -3
- package/dist/schemas/cameras.js +16 -4
- package/dist/schemas/common.d.ts +1 -1
- package/dist/schemas/common.js +1 -1
- package/dist/schemas/devices.d.ts +352 -0
- package/dist/schemas/devices.js +108 -25
- package/dist/schemas/misc.d.ts +35 -12
- package/dist/schemas/misc.js +23 -13
- package/dist/tools/arm-profiles.js +1 -1
- package/dist/tools/cameras.js +18 -8
- package/dist/tools/devices.js +18 -14
- package/dist/tools/files.js +2 -2
- package/dist/tools/liveviews.js +1 -1
- package/dist/tools/users.js +2 -2
- package/package.json +5 -5
package/dist/schemas/devices.js
CHANGED
|
@@ -5,40 +5,123 @@ exports.itemOutputShape = itemOutputShape;
|
|
|
5
5
|
exports.listOutputShape = listOutputShape;
|
|
6
6
|
const common_js_1 = require("./common.js");
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Device schemas for the config-driven Protect device types.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Field sets are derived from the UniFi Protect Integration API 7.1.83 docs
|
|
11
|
+
* (per-type response samples). Chimes were additionally verified against live
|
|
12
|
+
* console responses (2026-06-20); the rest have no instances on the available
|
|
13
|
+
* console, so their TOP-LEVEL fields are typed from the published docs while
|
|
14
|
+
* every nested object/array is left as `unknownField()` (z.unknown()) — naming
|
|
15
|
+
* the field for the LLM without risking SDK output-validation failures on a
|
|
16
|
+
* shape we have not observed live. `.passthrough()` lets any extra field flow.
|
|
14
17
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* (camera, nvr, user): id, modelKey, name, mac, state. `.passthrough()` lets
|
|
19
|
-
* every real (unverified) field through without rejection.
|
|
20
|
-
*
|
|
21
|
-
* When a console with these devices is available, capture live samples and
|
|
22
|
-
* tighten these per the skill (Step 4/5).
|
|
18
|
+
* Convention (see schemas/common.ts): only `id` is strictly typed; strings use
|
|
19
|
+
* `nullableString()` (API returns explicit null for absent strings); booleans,
|
|
20
|
+
* numbers, and unverified nested structures use `unknownField()`.
|
|
23
21
|
*/
|
|
24
|
-
|
|
22
|
+
/** Identity fields common to every Protect device resource. */
|
|
23
|
+
const deviceIdentity = () => ({
|
|
25
24
|
id: (0, common_js_1.idField)("Device ID"),
|
|
26
25
|
modelKey: (0, common_js_1.nullableString)("Resource kind"),
|
|
27
26
|
name: (0, common_js_1.nullableString)("Device name"),
|
|
28
27
|
mac: (0, common_js_1.nullableString)("MAC address"),
|
|
29
28
|
state: (0, common_js_1.nullableString)("CONNECTED | DISCONNECTED | ..."),
|
|
30
29
|
});
|
|
31
|
-
exports.lightSchema =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
exports.lightSchema = (0, common_js_1.passthroughObject)({
|
|
31
|
+
...deviceIdentity(),
|
|
32
|
+
lightModeSettings: (0, common_js_1.unknownField)("Activation settings (object: mode, enableAt)"),
|
|
33
|
+
lightDeviceSettings: (0, common_js_1.unknownField)("Hardware settings (object: isIndicatorEnabled, pirDuration, pirSensitivity, ledLevel)"),
|
|
34
|
+
isDark: (0, common_js_1.unknownField)("Whether it is currently dark out (boolean)"),
|
|
35
|
+
isLightOn: (0, common_js_1.unknownField)("Whether the light is currently on (boolean)"),
|
|
36
|
+
isLightForceEnabled: (0, common_js_1.unknownField)("Main LED force-enabled (boolean)"),
|
|
37
|
+
lastMotion: (0, common_js_1.unknownField)("Last motion timestamp in epoch ms (number)"),
|
|
38
|
+
isPirMotionDetected: (0, common_js_1.unknownField)("PIR motion currently detected (boolean)"),
|
|
39
|
+
camera: (0, common_js_1.nullableString)("Paired camera ID"),
|
|
40
|
+
});
|
|
41
|
+
exports.sensorSchema = (0, common_js_1.passthroughObject)({
|
|
42
|
+
...deviceIdentity(),
|
|
43
|
+
mountType: (0, common_js_1.nullableString)('Mount type, e.g. "door", "leak", "garage"'),
|
|
44
|
+
batteryStatus: (0, common_js_1.unknownField)("Battery status (object: percentage, isLow)"),
|
|
45
|
+
stats: (0, common_js_1.unknownField)("Environmental stats (object: light, humidity, temperature)"),
|
|
46
|
+
lightSettings: (0, common_js_1.unknownField)("Light threshold settings (object)"),
|
|
47
|
+
humiditySettings: (0, common_js_1.unknownField)("Humidity threshold settings (object)"),
|
|
48
|
+
temperatureSettings: (0, common_js_1.unknownField)("Temperature threshold settings (object)"),
|
|
49
|
+
isOpened: (0, common_js_1.unknownField)("Open/close contact state (boolean)"),
|
|
50
|
+
openStatusChangedAt: (0, common_js_1.unknownField)("Open-status change timestamp in epoch ms (number)"),
|
|
51
|
+
isMotionDetected: (0, common_js_1.unknownField)("Motion currently detected (boolean)"),
|
|
52
|
+
motionDetectedAt: (0, common_js_1.unknownField)("Last motion timestamp in epoch ms (number)"),
|
|
53
|
+
motionSettings: (0, common_js_1.unknownField)("Motion detection settings (object)"),
|
|
54
|
+
glassBreakSettings: (0, common_js_1.unknownField)("Glass-break detection settings (object)"),
|
|
55
|
+
scheduleMode: (0, common_js_1.nullableString)('Schedule mode: "always" | "when_armed"'),
|
|
56
|
+
armProfileIds: (0, common_js_1.unknownField)("Arm profile IDs this sensor belongs to (array of strings)"),
|
|
57
|
+
hasCustomSensitivityWhenArmed: (0, common_js_1.unknownField)("Custom armed sensitivity enabled (boolean)"),
|
|
58
|
+
alarmTriggeredAt: (0, common_js_1.unknownField)("Last alarm timestamp in epoch ms (number)"),
|
|
59
|
+
alarmSettings: (0, common_js_1.unknownField)("Alarm settings (object: isEnabled)"),
|
|
60
|
+
leakDetectedAt: (0, common_js_1.unknownField)("Last leak timestamp in epoch ms (number)"),
|
|
61
|
+
externalLeakDetectedAt: (0, common_js_1.unknownField)("Last external-leak timestamp in epoch ms (number)"),
|
|
62
|
+
leakSettings: (0, common_js_1.unknownField)("Leak detection settings (object)"),
|
|
63
|
+
tamperingDetectedAt: (0, common_js_1.unknownField)("Last tampering timestamp in epoch ms (number)"),
|
|
64
|
+
wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object: signalState, batteryStatus, bridge)"),
|
|
65
|
+
});
|
|
66
|
+
// Verified live (7.1.83): chimes return cameraIds[] and ringSettings[] whose
|
|
67
|
+
// items are { cameraId, volume, ringtoneId, repeatTimes }.
|
|
68
|
+
exports.chimeSchema = (0, common_js_1.passthroughObject)({
|
|
69
|
+
...deviceIdentity(),
|
|
70
|
+
cameraIds: (0, common_js_1.unknownField)("Paired camera IDs (array of strings)"),
|
|
71
|
+
ringSettings: (0, common_js_1.unknownField)("Per-camera ring config (array of objects: cameraId, volume, ringtoneId, repeatTimes)"),
|
|
72
|
+
});
|
|
73
|
+
exports.viewerSchema = (0, common_js_1.passthroughObject)({
|
|
74
|
+
...deviceIdentity(),
|
|
75
|
+
liveview: (0, common_js_1.nullableString)("Assigned live view ID, or null"),
|
|
76
|
+
streamLimit: (0, common_js_1.unknownField)("Max concurrent streams (number)"),
|
|
77
|
+
});
|
|
78
|
+
exports.sirenSchema = (0, common_js_1.passthroughObject)({
|
|
79
|
+
...deviceIdentity(),
|
|
80
|
+
volume: (0, common_js_1.unknownField)("Siren volume (number)"),
|
|
81
|
+
ledSettings: (0, common_js_1.unknownField)("LED settings (object: isEnabled)"),
|
|
82
|
+
sirenStatus: (0, common_js_1.unknownField)("Current siren status (object: isActive, activatedAt, duration)"),
|
|
83
|
+
connectionType: (0, common_js_1.nullableString)('Connection type, e.g. "lora"'),
|
|
84
|
+
wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object)"),
|
|
85
|
+
});
|
|
86
|
+
exports.fobSchema = (0, common_js_1.passthroughObject)({
|
|
87
|
+
...deviceIdentity(),
|
|
88
|
+
awayState: (0, common_js_1.nullableString)('Away state, e.g. "ONLINE"'),
|
|
89
|
+
buttonLabels: (0, common_js_1.nullableString)('Button label preset, e.g. "securityActions"'),
|
|
90
|
+
featureFlags: (0, common_js_1.unknownField)("Feature flags (object: buttons[])"),
|
|
91
|
+
wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object)"),
|
|
92
|
+
});
|
|
93
|
+
exports.relaySchema = (0, common_js_1.passthroughObject)({
|
|
94
|
+
...deviceIdentity(),
|
|
95
|
+
ledSettings: (0, common_js_1.unknownField)("LED settings (object: isEnabled)"),
|
|
96
|
+
outputs: (0, common_js_1.unknownField)("Output channels (array of objects)"),
|
|
97
|
+
inputs: (0, common_js_1.unknownField)("Input channels (array of objects)"),
|
|
98
|
+
wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object)"),
|
|
99
|
+
});
|
|
100
|
+
exports.speakerSchema = (0, common_js_1.passthroughObject)({
|
|
101
|
+
...deviceIdentity(),
|
|
102
|
+
volume: (0, common_js_1.unknownField)("Speaker volume (number)"),
|
|
103
|
+
micVolume: (0, common_js_1.unknownField)("Microphone volume (number)"),
|
|
104
|
+
isMicEnabled: (0, common_js_1.unknownField)("Microphone enabled (boolean)"),
|
|
105
|
+
speakerState: (0, common_js_1.unknownField)("Speaker state (object: status, mode)"),
|
|
106
|
+
featureFlags: (0, common_js_1.unknownField)("Feature flags (object: hasMic)"),
|
|
107
|
+
});
|
|
108
|
+
exports.bridgeSchema = (0, common_js_1.passthroughObject)({
|
|
109
|
+
...deviceIdentity(),
|
|
110
|
+
platform: (0, common_js_1.nullableString)('Hardware platform, e.g. "mt7621"'),
|
|
111
|
+
clients: (0, common_js_1.unknownField)("Connected client MACs (array of strings)"),
|
|
112
|
+
maxClients: (0, common_js_1.unknownField)("Max client capacity (number)"),
|
|
113
|
+
});
|
|
114
|
+
// Link stations and alarm hubs share one shape (both report modelKey
|
|
115
|
+
// "linkstation"); /alarm-hubs is the same resource filtered to alarm hubs.
|
|
116
|
+
const linkStationLikeSchema = () => (0, common_js_1.passthroughObject)({
|
|
117
|
+
...deviceIdentity(),
|
|
118
|
+
isAlarmHub: (0, common_js_1.unknownField)("Whether this device is an alarm hub (boolean)"),
|
|
119
|
+
ledSettings: (0, common_js_1.unknownField)("LED settings (object: isEnabled)"),
|
|
120
|
+
lastEvent: (0, common_js_1.unknownField)("Last event timestamp in epoch ms (number)"),
|
|
121
|
+
alarmHub: (0, common_js_1.unknownField)("Alarm hub status (object: armed, battery, connector, cover, output, input, …)"),
|
|
122
|
+
});
|
|
123
|
+
exports.linkStationSchema = linkStationLikeSchema();
|
|
124
|
+
exports.alarmHubSchema = linkStationLikeSchema();
|
|
42
125
|
exports.DEVICE_SCHEMAS = {
|
|
43
126
|
light: exports.lightSchema,
|
|
44
127
|
sensor: exports.sensorSchema,
|
package/dist/schemas/misc.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/**
|
|
3
|
-
* Schemas below are verified against live UniFi Protect Integration API 7.1.
|
|
4
|
-
* responses (Melrose NVR Pro console, 2026-
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* Schemas below are verified against live UniFi Protect Integration API 7.1.83
|
|
4
|
+
* responses (Melrose NVR Pro console, 2026-06-20) where instances exist, and
|
|
5
|
+
* against the 7.1.83 published docs otherwise (arm profiles, files — no
|
|
6
|
+
* instances on the console). The Integration API surface is much thinner than
|
|
7
|
+
* the internal Protect app API — only observed/documented fields are typed;
|
|
8
|
+
* `.passthrough()` still allows anything extra a firmware/hardware mix adds.
|
|
8
9
|
*/
|
|
9
10
|
export declare const infoOutputSchema: {
|
|
10
11
|
applicationVersion: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -21,6 +22,7 @@ export declare const nvrSchema: z.ZodObject<{
|
|
|
21
22
|
}, z.core.$loose>>;
|
|
22
23
|
armMode: z.ZodOptional<z.ZodObject<{
|
|
23
24
|
status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
25
|
+
armProfileId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
24
26
|
armedAt: z.ZodOptional<z.ZodUnknown>;
|
|
25
27
|
willBeArmedAt: z.ZodOptional<z.ZodUnknown>;
|
|
26
28
|
breachDetectedAt: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -41,6 +43,7 @@ export declare const nvrOutputSchema: {
|
|
|
41
43
|
}, z.core.$loose>>;
|
|
42
44
|
armMode: z.ZodOptional<z.ZodObject<{
|
|
43
45
|
status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
46
|
+
armProfileId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
44
47
|
armedAt: z.ZodOptional<z.ZodUnknown>;
|
|
45
48
|
willBeArmedAt: z.ZodOptional<z.ZodUnknown>;
|
|
46
49
|
breachDetectedAt: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -150,31 +153,51 @@ export declare const liveviewListOutputSchema: {
|
|
|
150
153
|
};
|
|
151
154
|
export declare const armProfileSchema: z.ZodObject<{
|
|
152
155
|
id: z.ZodOptional<z.ZodString>;
|
|
153
|
-
modelKey: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
154
156
|
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
157
|
+
automations: z.ZodOptional<z.ZodUnknown>;
|
|
158
|
+
creator: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
159
|
+
schedules: z.ZodOptional<z.ZodUnknown>;
|
|
160
|
+
recordEverything: z.ZodOptional<z.ZodUnknown>;
|
|
161
|
+
activationDelay: z.ZodOptional<z.ZodUnknown>;
|
|
162
|
+
createdAt: z.ZodOptional<z.ZodUnknown>;
|
|
163
|
+
updatedAt: z.ZodOptional<z.ZodUnknown>;
|
|
155
164
|
}, z.core.$loose>;
|
|
156
165
|
export declare const armProfileListOutputSchema: {
|
|
157
166
|
result: z.ZodArray<z.ZodObject<{
|
|
158
167
|
id: z.ZodOptional<z.ZodString>;
|
|
159
|
-
modelKey: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
160
168
|
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
169
|
+
automations: z.ZodOptional<z.ZodUnknown>;
|
|
170
|
+
creator: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
171
|
+
schedules: z.ZodOptional<z.ZodUnknown>;
|
|
172
|
+
recordEverything: z.ZodOptional<z.ZodUnknown>;
|
|
173
|
+
activationDelay: z.ZodOptional<z.ZodUnknown>;
|
|
174
|
+
createdAt: z.ZodOptional<z.ZodUnknown>;
|
|
175
|
+
updatedAt: z.ZodOptional<z.ZodUnknown>;
|
|
161
176
|
}, z.core.$loose>>;
|
|
162
177
|
};
|
|
163
178
|
export declare const armProfileOutputSchema: {
|
|
164
179
|
id: z.ZodOptional<z.ZodString>;
|
|
165
|
-
modelKey: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
166
180
|
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
181
|
+
automations: z.ZodOptional<z.ZodUnknown>;
|
|
182
|
+
creator: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
183
|
+
schedules: z.ZodOptional<z.ZodUnknown>;
|
|
184
|
+
recordEverything: z.ZodOptional<z.ZodUnknown>;
|
|
185
|
+
activationDelay: z.ZodOptional<z.ZodUnknown>;
|
|
186
|
+
createdAt: z.ZodOptional<z.ZodUnknown>;
|
|
187
|
+
updatedAt: z.ZodOptional<z.ZodUnknown>;
|
|
167
188
|
};
|
|
168
189
|
export declare const fileSchema: z.ZodObject<{
|
|
169
|
-
id: z.ZodOptional<z.ZodString>;
|
|
170
|
-
modelKey: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
171
190
|
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
191
|
+
type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
192
|
+
originalName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
193
|
+
path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
172
194
|
}, z.core.$loose>;
|
|
173
195
|
export declare const fileListOutputSchema: {
|
|
174
196
|
result: z.ZodArray<z.ZodObject<{
|
|
175
|
-
id: z.ZodOptional<z.ZodString>;
|
|
176
|
-
modelKey: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
177
197
|
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
198
|
+
type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
199
|
+
originalName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
200
|
+
path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
178
201
|
}, z.core.$loose>>;
|
|
179
202
|
};
|
|
180
203
|
export declare const subscriptionOutputSchema: {
|
package/dist/schemas/misc.js
CHANGED
|
@@ -4,11 +4,12 @@ exports.subscriptionOutputSchema = exports.fileListOutputSchema = exports.fileSc
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const common_js_1 = require("./common.js");
|
|
6
6
|
/**
|
|
7
|
-
* Schemas below are verified against live UniFi Protect Integration API 7.1.
|
|
8
|
-
* responses (Melrose NVR Pro console, 2026-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Schemas below are verified against live UniFi Protect Integration API 7.1.83
|
|
8
|
+
* responses (Melrose NVR Pro console, 2026-06-20) where instances exist, and
|
|
9
|
+
* against the 7.1.83 published docs otherwise (arm profiles, files — no
|
|
10
|
+
* instances on the console). The Integration API surface is much thinner than
|
|
11
|
+
* the internal Protect app API — only observed/documented fields are typed;
|
|
12
|
+
* `.passthrough()` still allows anything extra a firmware/hardware mix adds.
|
|
12
13
|
*/
|
|
13
14
|
// --- System ---
|
|
14
15
|
exports.infoOutputSchema = {
|
|
@@ -16,7 +17,8 @@ exports.infoOutputSchema = {
|
|
|
16
17
|
};
|
|
17
18
|
// GET /nvrs returns a SINGLE nvr object (not an array, despite the tool name).
|
|
18
19
|
const armModeSchema = (0, common_js_1.passthroughObject)({
|
|
19
|
-
status: (0, common_js_1.nullableString)("disabled | armed | ..."),
|
|
20
|
+
status: (0, common_js_1.nullableString)("disabled | arming | armed | ..."),
|
|
21
|
+
armProfileId: (0, common_js_1.nullableString)("Active arm profile ID (present when arming/armed)"),
|
|
20
22
|
armedAt: (0, common_js_1.unknownField)("Unix epoch ms when armed, or null (number|null)"),
|
|
21
23
|
willBeArmedAt: (0, common_js_1.unknownField)("Scheduled arm time, or null (number|null)"),
|
|
22
24
|
breachDetectedAt: (0, common_js_1.unknownField)("Unix epoch ms of last breach, or null"),
|
|
@@ -80,21 +82,29 @@ exports.liveviewSchema = (0, common_js_1.passthroughObject)({
|
|
|
80
82
|
exports.liveviewOutputSchema = { ...exports.liveviewSchema.shape };
|
|
81
83
|
exports.liveviewListOutputSchema = (0, common_js_1.listResultSchema)(exports.liveviewSchema);
|
|
82
84
|
// --- Arm profiles ---
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
+
// Fields from the 7.1.83 docs (no instances on the console to verify live).
|
|
86
|
+
// Arm profiles carry no `modelKey`. Nested arrays stay unknownField.
|
|
85
87
|
exports.armProfileSchema = (0, common_js_1.passthroughObject)({
|
|
86
88
|
id: (0, common_js_1.idField)("Arm profile ID"),
|
|
87
|
-
modelKey: (0, common_js_1.nullableString)("Resource kind"),
|
|
88
89
|
name: (0, common_js_1.nullableString)("Arm profile name"),
|
|
90
|
+
automations: (0, common_js_1.unknownField)("Associated automation IDs (array of strings)"),
|
|
91
|
+
creator: (0, common_js_1.nullableString)("ID of the user who created the profile"),
|
|
92
|
+
schedules: (0, common_js_1.unknownField)("Arm schedules (array of objects)"),
|
|
93
|
+
recordEverything: (0, common_js_1.unknownField)("Record everything while active (boolean)"),
|
|
94
|
+
activationDelay: (0, common_js_1.unknownField)("Activation delay in ms: 0 | 60000 | 300000 | 600000 (number)"),
|
|
95
|
+
createdAt: (0, common_js_1.unknownField)("Creation timestamp in epoch ms (number)"),
|
|
96
|
+
updatedAt: (0, common_js_1.unknownField)("Last update timestamp in epoch ms (number)"),
|
|
89
97
|
});
|
|
90
98
|
exports.armProfileListOutputSchema = (0, common_js_1.listResultSchema)(exports.armProfileSchema);
|
|
91
99
|
exports.armProfileOutputSchema = { ...exports.armProfileSchema.shape };
|
|
92
100
|
// --- Files ---
|
|
93
|
-
//
|
|
101
|
+
// Device asset files (only fileType=animations supported). Shape from 7.1.83
|
|
102
|
+
// docs: { name, type, originalName, path } — no id/modelKey.
|
|
94
103
|
exports.fileSchema = (0, common_js_1.passthroughObject)({
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
name: (0, common_js_1.nullableString)("Stored file name (server-generated)"),
|
|
105
|
+
type: (0, common_js_1.nullableString)('Asset file type, e.g. "animations"'),
|
|
106
|
+
originalName: (0, common_js_1.nullableString)("Original uploaded file name"),
|
|
107
|
+
path: (0, common_js_1.nullableString)("Server-side storage path"),
|
|
98
108
|
});
|
|
99
109
|
exports.fileListOutputSchema = (0, common_js_1.listResultSchema)(exports.fileSchema);
|
|
100
110
|
// --- Subscriptions ---
|
|
@@ -9,7 +9,7 @@ const misc_js_1 = require("../schemas/misc.js");
|
|
|
9
9
|
const ACTIVATION_DELAY_VALUES = [0, 60000, 300000, 600000];
|
|
10
10
|
function registerArmProfileTools(server, client, readOnly) {
|
|
11
11
|
server.registerTool("protect_list_arm_profiles", {
|
|
12
|
-
description: "List all arm profiles (only available when using the local alarm manager — the standalone NVR alarm system, not Protect cloud alerts). Returns array
|
|
12
|
+
description: "List all arm profiles (only available when using the local alarm manager — the standalone NVR alarm system, not Protect cloud alerts). Returns array; each profile (7.1.83 docs): id, name, automations[], creator, schedules[], recordEverything, activationDelay (0 | 60000 | 300000 | 600000), createdAt, updatedAt.",
|
|
13
13
|
outputSchema: misc_js_1.armProfileListOutputSchema,
|
|
14
14
|
annotations: safety_js_1.READ_ONLY,
|
|
15
15
|
}, async () => {
|
package/dist/tools/cameras.js
CHANGED
|
@@ -9,7 +9,7 @@ const cameras_js_1 = require("../schemas/cameras.js");
|
|
|
9
9
|
function registerCameraTools(server, client, readOnly) {
|
|
10
10
|
// --- Read-only tools (always registered) ---
|
|
11
11
|
server.registerTool("protect_list_cameras", {
|
|
12
|
-
description: "List all cameras managed by UniFi Protect. Returns array; each camera includes (Integration API 7.1.
|
|
12
|
+
description: "List all cameras managed by UniFi Protect. Returns array; each camera includes (Integration API 7.1.83-verified fields): id, mac, name, modelKey, state (CONNECTED/DISCONNECTED), activePatrolSlot, hasPackageCamera, hdrType, isMicEnabled, micVolume, videoMode, featureFlags (hasHdr, hasMic, hasSpeaker, hasLedStatus, smartDetectTypes[], smartDetectAudioTypes[], videoModes[], supportFullHdSnapshot), lcdMessage (type, resetAt, text), ledSettings (isEnabled, floodLed, welcomeLed), osdSettings (isNameEnabled, isDateEnabled, isLogoEnabled, isDebugEnabled, overlayLocation), smartDetectSettings (objectTypes[], audioTypes[]). The Integration API does NOT expose recording state, motion timestamps, connection/last-seen, firmware, host, or per-channel stream config.",
|
|
13
13
|
outputSchema: cameras_js_1.cameraListOutputSchema,
|
|
14
14
|
annotations: safety_js_1.READ_ONLY,
|
|
15
15
|
}, async () => {
|
|
@@ -22,7 +22,7 @@ function registerCameraTools(server, client, readOnly) {
|
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
server.registerTool("protect_get_camera", {
|
|
25
|
-
description: "Get details for a specific camera by ID. The Protect Integration API returns the SAME field set as protect_list_cameras entries (id, mac, name, modelKey, state, activePatrolSlot, hasPackageCamera, hdrType, isMicEnabled, micVolume, videoMode, featureFlags, lcdMessage, ledSettings, osdSettings, smartDetectSettings) — there is no extended/by-id-only payload. Recording state, motion events, zones, and channel/RTSP config are NOT exposed by this API surface.",
|
|
25
|
+
description: "Get details for a specific camera by ID. The Protect Integration API returns the SAME field set as protect_list_cameras entries (id, mac, name, modelKey, state, activePatrolSlot, hasPackageCamera, hdrType, isMicEnabled, micVolume, videoMode, featureFlags, lcdMessage, ledSettings, osdSettings, smartDetectSettings) — there is no extended/by-id-only payload (confirmed live on 7.1.83). Recording state, motion events, zones, and channel/RTSP config are NOT exposed by this API surface.",
|
|
26
26
|
inputSchema: { id: zod_1.z.string().describe("Camera ID") },
|
|
27
27
|
outputSchema: cameras_js_1.cameraOutputSchema,
|
|
28
28
|
annotations: safety_js_1.READ_ONLY,
|
|
@@ -36,19 +36,29 @@ function registerCameraTools(server, client, readOnly) {
|
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
38
|
server.registerTool("protect_get_snapshot", {
|
|
39
|
-
description: "Get a JPEG snapshot from a camera. Returns a base64-encoded image/jpeg (rendered directly by MCP clients). Use highQuality=true for full-resolution capture from the
|
|
39
|
+
description: "Get a JPEG snapshot from a camera. Returns a base64-encoded image/jpeg (rendered directly by MCP clients). Use highQuality=true for full-resolution capture; set channel=package to capture from the secondary package camera on doorbells with hasPackageCamera=true.",
|
|
40
40
|
inputSchema: {
|
|
41
41
|
id: zod_1.z.string().describe("Camera ID"),
|
|
42
42
|
highQuality: zod_1.z
|
|
43
43
|
.boolean()
|
|
44
44
|
.optional()
|
|
45
45
|
.describe("If true, request a high-quality snapshot"),
|
|
46
|
+
channel: zod_1.z
|
|
47
|
+
.enum(["main", "package"])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe('Camera channel to capture. Use "package" for cameras with hasPackageCamera=true (defaults to main)'),
|
|
46
50
|
},
|
|
47
51
|
annotations: safety_js_1.READ_ONLY,
|
|
48
|
-
}, async ({ id, highQuality }) => {
|
|
52
|
+
}, async ({ id, highQuality, channel }) => {
|
|
49
53
|
try {
|
|
50
|
-
const
|
|
51
|
-
|
|
54
|
+
const params = new URLSearchParams();
|
|
55
|
+
if (highQuality)
|
|
56
|
+
params.set("highQuality", "true");
|
|
57
|
+
if (channel)
|
|
58
|
+
params.set("channel", channel);
|
|
59
|
+
const query = params.toString();
|
|
60
|
+
const url = query
|
|
61
|
+
? (0, url_js_1.safePath) `/cameras/${id}/snapshot` + `?${query}`
|
|
52
62
|
: (0, url_js_1.safePath) `/cameras/${id}/snapshot`;
|
|
53
63
|
const { data, mimeType } = await client.getBinary(url);
|
|
54
64
|
return {
|
|
@@ -88,7 +98,7 @@ function registerCameraTools(server, client, readOnly) {
|
|
|
88
98
|
id: zod_1.z.string().describe("Camera ID"),
|
|
89
99
|
settings: zod_1.z
|
|
90
100
|
.record(zod_1.z.string(), zod_1.z.unknown())
|
|
91
|
-
.describe("Partial camera settings to update. Known fields: name (string), osdSettings (object), ledSettings (object), lcdMessage (object), videoMode (string: \"default\" | \"highFps\" | \"
|
|
101
|
+
.describe("Partial camera settings to update. Known fields: name (string), osdSettings (object), ledSettings (object), lcdMessage (object: type, resetAt, text), videoMode (string: \"default\" | \"highFps\" | \"sport\" | \"slowShutter\" | \"lprReflex\" | \"lprNoneReflex\"), hdrType (string: \"auto\" | \"on\" | \"off\"), micVolume (number 0-100), smartDetectSettings (object)"),
|
|
92
102
|
dryRun: zod_1.z
|
|
93
103
|
.boolean()
|
|
94
104
|
.optional()
|
|
@@ -162,7 +172,7 @@ function registerCameraTools(server, client, readOnly) {
|
|
|
162
172
|
}
|
|
163
173
|
});
|
|
164
174
|
server.registerTool("protect_create_talkback", {
|
|
165
|
-
description: "Create a talkback (two-way audio) session for a camera",
|
|
175
|
+
description: "Create a talkback (two-way audio) session for a camera. Returns: url, codec, samplingRate, bitsPerSample (the audio config for encoding outbound audio).",
|
|
166
176
|
inputSchema: {
|
|
167
177
|
id: zod_1.z.string().describe("Camera ID"),
|
|
168
178
|
dryRun: zod_1.z
|
package/dist/tools/devices.js
CHANGED
|
@@ -7,6 +7,10 @@ const responses_js_1 = require("../utils/responses.js");
|
|
|
7
7
|
const safety_js_1 = require("../utils/safety.js");
|
|
8
8
|
const url_js_1 = require("../utils/url.js");
|
|
9
9
|
const devices_js_1 = require("../schemas/devices.js");
|
|
10
|
+
// Field lists below are from the UniFi Protect Integration API 7.1.83 docs.
|
|
11
|
+
// Chimes are additionally verified against live console responses; the rest
|
|
12
|
+
// have no instances on the available console (top-level fields documented,
|
|
13
|
+
// nested shapes intentionally left opaque in the output schema).
|
|
10
14
|
const DEVICES = [
|
|
11
15
|
{
|
|
12
16
|
urlPath: "lights",
|
|
@@ -14,31 +18,31 @@ const DEVICES = [
|
|
|
14
18
|
plural: "lights",
|
|
15
19
|
label: "Light",
|
|
16
20
|
hint: "Known fields: name (string), isLightForceEnabled (boolean), lightModeSettings (object with mode, enableAt), lightDeviceSettings (object with isIndicatorEnabled, pirDuration, pirSensitivity, ledLevel)",
|
|
17
|
-
returns: "id, modelKey, name, mac, state (
|
|
21
|
+
returns: "id, modelKey, name, mac, state, lightModeSettings (mode, enableAt), lightDeviceSettings (isIndicatorEnabled, pirDuration, pirSensitivity, ledLevel), isDark, isLightOn, isLightForceEnabled, lastMotion, isPirMotionDetected, camera (7.1.83 docs)",
|
|
18
22
|
},
|
|
19
23
|
{
|
|
20
24
|
urlPath: "sensors",
|
|
21
25
|
singular: "sensor",
|
|
22
26
|
plural: "sensors",
|
|
23
27
|
label: "Sensor",
|
|
24
|
-
hint: "Known fields: name (string), motionSettings (object), humiditySettings (object), temperatureSettings (object), lightSettings (object), alarmSettings (object)",
|
|
25
|
-
returns: "id, modelKey, name, mac, state (
|
|
28
|
+
hint: "Known fields: name (string), motionSettings (object), humiditySettings (object), temperatureSettings (object), lightSettings (object), alarmSettings (object), glassBreakSettings (object), leakSettings (object), scheduleMode (\"always\" | \"when_armed\"), armProfileIds (array of up to 32 strings), hasCustomSensitivityWhenArmed (boolean)",
|
|
29
|
+
returns: "id, modelKey, name, mac, state, mountType, batteryStatus (percentage, isLow), stats (light, humidity, temperature), lightSettings, humiditySettings, temperatureSettings, isOpened, openStatusChangedAt, isMotionDetected, motionDetectedAt, motionSettings, glassBreakSettings, scheduleMode, armProfileIds, hasCustomSensitivityWhenArmed, alarmTriggeredAt, alarmSettings, leakDetectedAt, externalLeakDetectedAt, leakSettings, tamperingDetectedAt, wirelessConnectionState (7.1.83 docs)",
|
|
26
30
|
},
|
|
27
31
|
{
|
|
28
32
|
urlPath: "chimes",
|
|
29
33
|
singular: "chime",
|
|
30
34
|
plural: "chimes",
|
|
31
35
|
label: "Chime",
|
|
32
|
-
hint: "Known fields: name (string), cameraIds (array of camera IDs linked to the chime), ringSettings (array of
|
|
33
|
-
returns: "id, modelKey, name, mac, state (
|
|
36
|
+
hint: "Known fields: name (string), cameraIds (array of camera IDs linked to the chime), ringSettings (array of objects: cameraId, volume, ringtoneId, repeatTimes)",
|
|
37
|
+
returns: "id, modelKey, name, mac, state, cameraIds (array of camera IDs), ringSettings (array of objects: cameraId, volume, ringtoneId, repeatTimes) — verified live 7.1.83",
|
|
34
38
|
},
|
|
35
39
|
{
|
|
36
40
|
urlPath: "viewers",
|
|
37
41
|
singular: "viewer",
|
|
38
42
|
plural: "viewers",
|
|
39
43
|
label: "Viewer",
|
|
40
|
-
hint: "Known fields: name (string), liveview (string, liveview ID to display)",
|
|
41
|
-
returns: "id, modelKey, name, mac, state
|
|
44
|
+
hint: "Known fields: name (string), liveview (string, liveview ID to display, or null)",
|
|
45
|
+
returns: "id, modelKey, name, mac, state, liveview, streamLimit (7.1.83 docs)",
|
|
42
46
|
},
|
|
43
47
|
{
|
|
44
48
|
urlPath: "sirens",
|
|
@@ -46,7 +50,7 @@ const DEVICES = [
|
|
|
46
50
|
plural: "sirens",
|
|
47
51
|
label: "Siren",
|
|
48
52
|
hint: "Known fields: name (string), volume (integer 1-100), ledSettings (object with isEnabled boolean)",
|
|
49
|
-
returns: "id, modelKey, name, mac, state (
|
|
53
|
+
returns: "id, modelKey, name, mac, state, volume, ledSettings (isEnabled), sirenStatus (isActive, activatedAt, duration), connectionType, wirelessConnectionState (7.1.83 docs)",
|
|
50
54
|
},
|
|
51
55
|
{
|
|
52
56
|
urlPath: "fobs",
|
|
@@ -54,7 +58,7 @@ const DEVICES = [
|
|
|
54
58
|
plural: "fobs",
|
|
55
59
|
label: "Fob",
|
|
56
60
|
hint: "Known fields: name (string)",
|
|
57
|
-
returns: "id, modelKey, name, mac, state
|
|
61
|
+
returns: "id, modelKey, name, mac, state, awayState, buttonLabels, featureFlags (buttons[]), wirelessConnectionState (7.1.83 docs)",
|
|
58
62
|
},
|
|
59
63
|
{
|
|
60
64
|
urlPath: "relays",
|
|
@@ -62,7 +66,7 @@ const DEVICES = [
|
|
|
62
66
|
plural: "relays",
|
|
63
67
|
label: "Relay",
|
|
64
68
|
hint: "Known fields: name (string), ledSettings (object with isEnabled boolean)",
|
|
65
|
-
returns: "id, modelKey, name, mac, state (
|
|
69
|
+
returns: "id, modelKey, name, mac, state, ledSettings (isEnabled), outputs (array), inputs (array), wirelessConnectionState (7.1.83 docs)",
|
|
66
70
|
},
|
|
67
71
|
{
|
|
68
72
|
urlPath: "speakers",
|
|
@@ -70,7 +74,7 @@ const DEVICES = [
|
|
|
70
74
|
plural: "speakers",
|
|
71
75
|
label: "Speaker",
|
|
72
76
|
hint: "Known fields: name (string), volume (integer 0-100), micVolume (integer 0-100), isMicEnabled (boolean)",
|
|
73
|
-
returns: "id, modelKey, name, mac, state (
|
|
77
|
+
returns: "id, modelKey, name, mac, state, volume, micVolume, isMicEnabled, speakerState (status, mode), featureFlags (hasMic) (7.1.83 docs)",
|
|
74
78
|
},
|
|
75
79
|
{
|
|
76
80
|
urlPath: "bridges",
|
|
@@ -78,7 +82,7 @@ const DEVICES = [
|
|
|
78
82
|
plural: "bridges",
|
|
79
83
|
label: "Bridge",
|
|
80
84
|
hint: "Known fields: name (string)",
|
|
81
|
-
returns: "id, modelKey, name, mac, state (
|
|
85
|
+
returns: "id, modelKey, name, mac, state, platform, clients (array of MACs), maxClients (7.1.83 docs)",
|
|
82
86
|
},
|
|
83
87
|
{
|
|
84
88
|
urlPath: "link-stations",
|
|
@@ -86,7 +90,7 @@ const DEVICES = [
|
|
|
86
90
|
plural: "link_stations",
|
|
87
91
|
label: "Link station",
|
|
88
92
|
hint: "Known fields: name (string)",
|
|
89
|
-
returns: "id, modelKey, name, mac, state
|
|
93
|
+
returns: "id, modelKey (\"linkstation\"), name, mac, state, isAlarmHub, ledSettings (isEnabled), lastEvent, alarmHub (object) (7.1.83 docs)",
|
|
90
94
|
},
|
|
91
95
|
{
|
|
92
96
|
urlPath: "alarm-hubs",
|
|
@@ -94,7 +98,7 @@ const DEVICES = [
|
|
|
94
98
|
plural: "alarm_hubs",
|
|
95
99
|
label: "Alarm hub",
|
|
96
100
|
hint: "Known fields: name (string)",
|
|
97
|
-
returns: "id, modelKey, name, mac, state
|
|
101
|
+
returns: "id, modelKey (\"linkstation\"), name, mac, state, isAlarmHub, ledSettings (isEnabled), lastEvent, alarmHub (object) (7.1.83 docs)",
|
|
98
102
|
},
|
|
99
103
|
];
|
|
100
104
|
function registerDeviceCrud(server, client, cfg, readOnly) {
|
package/dist/tools/files.js
CHANGED
|
@@ -8,7 +8,7 @@ const url_js_1 = require("../utils/url.js");
|
|
|
8
8
|
const misc_js_1 = require("../schemas/misc.js");
|
|
9
9
|
function registerFileTools(server, client, readOnly) {
|
|
10
10
|
server.registerTool("protect_list_files", {
|
|
11
|
-
description: "List files of a given type (only 'animations' is currently supported —
|
|
11
|
+
description: "List files of a given type (only 'animations' is currently supported — clips uploadable to G4 Doorbell screens). Returns array; each record (7.1.83 docs): name (server-generated), type, originalName, path.",
|
|
12
12
|
inputSchema: {
|
|
13
13
|
fileType: zod_1.z
|
|
14
14
|
.string()
|
|
@@ -56,7 +56,7 @@ function registerFileTools(server, client, readOnly) {
|
|
|
56
56
|
contentType: zod_1.z
|
|
57
57
|
.string()
|
|
58
58
|
.default("image/gif")
|
|
59
|
-
.describe("MIME type of the file (accepted: image/gif, image/
|
|
59
|
+
.describe("MIME type of the file (accepted: image/gif, image/jpeg, image/png, audio/mpeg, audio/mp4, audio/wave, audio/x-caf)"),
|
|
60
60
|
dryRun: zod_1.z
|
|
61
61
|
.boolean()
|
|
62
62
|
.optional()
|
package/dist/tools/liveviews.js
CHANGED
|
@@ -8,7 +8,7 @@ const url_js_1 = require("../utils/url.js");
|
|
|
8
8
|
const misc_js_1 = require("../schemas/misc.js");
|
|
9
9
|
function registerLiveviewTools(server, client, readOnly) {
|
|
10
10
|
server.registerTool("protect_list_liveviews", {
|
|
11
|
-
description: "List all live views (camera grid layouts shown on viewers / in the Protect UI). Returns array; each liveview includes (Integration API 7.1.
|
|
11
|
+
description: "List all live views (camera grid layouts shown on viewers / in the Protect UI). Returns array; each liveview includes (Integration API 7.1.83-verified): id, modelKey, name, isDefault, isGlobal, layout (number, slot count), owner (user ID), slots (array of {cameras: string[], cycleMode, cycleInterval}). NOTE: slots use a `cameras` string-array, not a single `cameraId`.",
|
|
12
12
|
outputSchema: misc_js_1.liveviewListOutputSchema,
|
|
13
13
|
annotations: safety_js_1.READ_ONLY,
|
|
14
14
|
}, async () => {
|
package/dist/tools/users.js
CHANGED
|
@@ -8,7 +8,7 @@ const url_js_1 = require("../utils/url.js");
|
|
|
8
8
|
const misc_js_1 = require("../schemas/misc.js");
|
|
9
9
|
function registerUserTools(server, client) {
|
|
10
10
|
server.registerTool("protect_list_users", {
|
|
11
|
-
description: "List all Protect users (filtered by the API key's access permissions). Returns array; each user includes (Integration API 7.1.
|
|
11
|
+
description: "List all Protect users (filtered by the API key's access permissions). Returns array; each user includes (Integration API 7.1.83-verified): id, modelKey, name, firstName, lastName, email, ucoreUserId. The Integration API does NOT expose roles, permissions, login history, groups, or notification settings.",
|
|
12
12
|
outputSchema: misc_js_1.userListOutputSchema,
|
|
13
13
|
annotations: safety_js_1.READ_ONLY,
|
|
14
14
|
}, async () => {
|
|
@@ -35,7 +35,7 @@ function registerUserTools(server, client) {
|
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
server.registerTool("protect_list_ulp_users", {
|
|
38
|
-
description: "List all UniFi Identity (ULP) users. Returns array; each ULP user includes (Integration API 7.1.
|
|
38
|
+
description: "List all UniFi Identity (ULP) users. Returns array; each ULP user includes (Integration API 7.1.83-verified): id, modelKey, firstName, lastName, fullName, status (e.g. ACTIVE). Enrolled-credential detail (NFC cards, fingerprints) is NOT exposed by this API surface.",
|
|
39
39
|
outputSchema: misc_js_1.ulpUserListOutputSchema,
|
|
40
40
|
annotations: safety_js_1.READ_ONLY,
|
|
41
41
|
}, async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owine/unifi-protect-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "MCP server for UniFi Protect Integration 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-protect-mcp.git"
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"@eslint/js": "10.0.1",
|
|
46
46
|
"@types/node": "24.13.2",
|
|
47
47
|
"@types/ws": "8.18.1",
|
|
48
|
-
"@vitest/coverage-v8": "4.1.
|
|
48
|
+
"@vitest/coverage-v8": "4.1.9",
|
|
49
49
|
"eslint": "10.5.0",
|
|
50
50
|
"lefthook": "2.1.9",
|
|
51
51
|
"typescript": "6.0.3",
|
|
52
|
-
"typescript-eslint": "8.61.
|
|
53
|
-
"vitest": "4.1.
|
|
52
|
+
"typescript-eslint": "8.61.1",
|
|
53
|
+
"vitest": "4.1.9"
|
|
54
54
|
}
|
|
55
55
|
}
|