@owine/unifi-protect-mcp 2.14.2 → 3.0.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.
Files changed (60) hide show
  1. package/README.md +248 -92
  2. package/dist/client.d.ts +43 -3
  3. package/dist/client.js +255 -85
  4. package/dist/config.d.ts +7 -1
  5. package/dist/config.js +114 -6
  6. package/dist/index.js +14 -6
  7. package/dist/instructions.d.ts +6 -0
  8. package/dist/instructions.js +24 -0
  9. package/dist/schemas/cameras.d.ts +32 -30
  10. package/dist/schemas/cameras.js +7 -6
  11. package/dist/schemas/common.d.ts +31 -6
  12. package/dist/schemas/common.js +45 -6
  13. package/dist/schemas/devices.d.ts +2 -10
  14. package/dist/schemas/devices.js +35 -26
  15. package/dist/schemas/misc.d.ts +50 -18
  16. package/dist/schemas/misc.js +23 -15
  17. package/dist/schemas/pos.d.ts +1 -1
  18. package/dist/schemas/pos.js +1 -1
  19. package/dist/tls.d.ts +41 -0
  20. package/dist/tls.js +75 -0
  21. package/dist/tools/alarm-manager.d.ts +3 -0
  22. package/dist/tools/alarm-manager.js +32 -0
  23. package/dist/tools/arm-profiles.d.ts +8 -2
  24. package/dist/tools/arm-profiles.js +98 -95
  25. package/dist/tools/cameras.d.ts +2 -2
  26. package/dist/tools/cameras.js +156 -181
  27. package/dist/tools/device-actions.d.ts +2 -2
  28. package/dist/tools/device-actions.js +84 -97
  29. package/dist/tools/devices.d.ts +6 -3
  30. package/dist/tools/devices.js +50 -51
  31. package/dist/tools/files.d.ts +2 -2
  32. package/dist/tools/files.js +82 -57
  33. package/dist/tools/index.d.ts +2 -2
  34. package/dist/tools/index.js +15 -10
  35. package/dist/tools/liveviews.d.ts +2 -2
  36. package/dist/tools/liveviews.js +52 -52
  37. package/dist/tools/pos.d.ts +6 -4
  38. package/dist/tools/pos.js +15 -20
  39. package/dist/tools/registrar.d.ts +34 -0
  40. package/dist/tools/registrar.js +103 -0
  41. package/dist/tools/subscriptions.d.ts +13 -2
  42. package/dist/tools/subscriptions.js +148 -27
  43. package/dist/tools/system.d.ts +2 -2
  44. package/dist/tools/system.js +5 -15
  45. package/dist/tools/users.d.ts +2 -2
  46. package/dist/tools/users.js +18 -35
  47. package/dist/utils/limits.d.ts +33 -0
  48. package/dist/utils/limits.js +74 -0
  49. package/dist/utils/paging.d.ts +15 -0
  50. package/dist/utils/paging.js +28 -0
  51. package/dist/utils/params.d.ts +13 -0
  52. package/dist/utils/params.js +40 -0
  53. package/dist/utils/responses.js +2 -3
  54. package/dist/utils/safety.d.ts +39 -3
  55. package/dist/utils/safety.js +45 -3
  56. package/dist/utils/url.d.ts +4 -1
  57. package/dist/utils/url.js +4 -1
  58. package/dist/version.d.ts +6 -0
  59. package/dist/version.js +11 -0
  60. package/package.json +14 -12
@@ -12,7 +12,7 @@ export declare const cameraSchema: import("zod").ZodObject<{
12
12
  isMicEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
13
13
  micVolume: import("zod").ZodOptional<import("zod").ZodUnknown>;
14
14
  videoMode: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
15
- featureFlags: import("zod").ZodOptional<import("zod").ZodObject<{
15
+ featureFlags: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
16
16
  hasHdr: import("zod").ZodOptional<import("zod").ZodUnknown>;
17
17
  hasLedStatus: import("zod").ZodOptional<import("zod").ZodUnknown>;
18
18
  hasMic: import("zod").ZodOptional<import("zod").ZodUnknown>;
@@ -21,28 +21,28 @@ export declare const cameraSchema: import("zod").ZodObject<{
21
21
  smartDetectTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
22
22
  supportFullHdSnapshot: import("zod").ZodOptional<import("zod").ZodUnknown>;
23
23
  videoModes: import("zod").ZodOptional<import("zod").ZodUnknown>;
24
- }, import("zod/v4/core").$loose>>;
25
- lcdMessage: import("zod").ZodOptional<import("zod").ZodObject<{
24
+ }, import("zod/v4/core").$loose>>>;
25
+ lcdMessage: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
26
26
  type: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
27
27
  resetAt: import("zod").ZodOptional<import("zod").ZodUnknown>;
28
28
  text: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
29
- }, import("zod/v4/core").$loose>>;
30
- ledSettings: import("zod").ZodOptional<import("zod").ZodObject<{
29
+ }, import("zod/v4/core").$loose>>>;
30
+ ledSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
31
31
  isEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
32
32
  floodLed: import("zod").ZodOptional<import("zod").ZodUnknown>;
33
33
  welcomeLed: import("zod").ZodOptional<import("zod").ZodUnknown>;
34
- }, import("zod/v4/core").$loose>>;
35
- osdSettings: import("zod").ZodOptional<import("zod").ZodObject<{
34
+ }, import("zod/v4/core").$loose>>>;
35
+ osdSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
36
36
  isNameEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
37
37
  isDateEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
38
38
  isLogoEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
39
39
  isDebugEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
40
40
  overlayLocation: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
41
- }, import("zod/v4/core").$loose>>;
42
- smartDetectSettings: import("zod").ZodOptional<import("zod").ZodObject<{
41
+ }, import("zod/v4/core").$loose>>>;
42
+ smartDetectSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
43
43
  objectTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
44
44
  audioTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
45
- }, import("zod/v4/core").$loose>>;
45
+ }, import("zod/v4/core").$loose>>>;
46
46
  }, import("zod/v4/core").$loose>;
47
47
  export declare const cameraOutputSchema: {
48
48
  type: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
@@ -58,7 +58,7 @@ export declare const cameraOutputSchema: {
58
58
  isMicEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
59
59
  micVolume: import("zod").ZodOptional<import("zod").ZodUnknown>;
60
60
  videoMode: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
61
- featureFlags: import("zod").ZodOptional<import("zod").ZodObject<{
61
+ featureFlags: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
62
62
  hasHdr: import("zod").ZodOptional<import("zod").ZodUnknown>;
63
63
  hasLedStatus: import("zod").ZodOptional<import("zod").ZodUnknown>;
64
64
  hasMic: import("zod").ZodOptional<import("zod").ZodUnknown>;
@@ -67,28 +67,28 @@ export declare const cameraOutputSchema: {
67
67
  smartDetectTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
68
68
  supportFullHdSnapshot: import("zod").ZodOptional<import("zod").ZodUnknown>;
69
69
  videoModes: import("zod").ZodOptional<import("zod").ZodUnknown>;
70
- }, import("zod/v4/core").$loose>>;
71
- lcdMessage: import("zod").ZodOptional<import("zod").ZodObject<{
70
+ }, import("zod/v4/core").$loose>>>;
71
+ lcdMessage: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
72
72
  type: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
73
73
  resetAt: import("zod").ZodOptional<import("zod").ZodUnknown>;
74
74
  text: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
75
- }, import("zod/v4/core").$loose>>;
76
- ledSettings: import("zod").ZodOptional<import("zod").ZodObject<{
75
+ }, import("zod/v4/core").$loose>>>;
76
+ ledSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
77
77
  isEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
78
78
  floodLed: import("zod").ZodOptional<import("zod").ZodUnknown>;
79
79
  welcomeLed: import("zod").ZodOptional<import("zod").ZodUnknown>;
80
- }, import("zod/v4/core").$loose>>;
81
- osdSettings: import("zod").ZodOptional<import("zod").ZodObject<{
80
+ }, import("zod/v4/core").$loose>>>;
81
+ osdSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
82
82
  isNameEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
83
83
  isDateEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
84
84
  isLogoEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
85
85
  isDebugEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
86
86
  overlayLocation: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
87
- }, import("zod/v4/core").$loose>>;
88
- smartDetectSettings: import("zod").ZodOptional<import("zod").ZodObject<{
87
+ }, import("zod/v4/core").$loose>>>;
88
+ smartDetectSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
89
89
  objectTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
90
90
  audioTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
91
- }, import("zod/v4/core").$loose>>;
91
+ }, import("zod/v4/core").$loose>>>;
92
92
  };
93
93
  export declare const cameraListOutputSchema: {
94
94
  result: import("zod").ZodArray<import("zod").ZodObject<{
@@ -105,7 +105,7 @@ export declare const cameraListOutputSchema: {
105
105
  isMicEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
106
106
  micVolume: import("zod").ZodOptional<import("zod").ZodUnknown>;
107
107
  videoMode: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
108
- featureFlags: import("zod").ZodOptional<import("zod").ZodObject<{
108
+ featureFlags: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
109
109
  hasHdr: import("zod").ZodOptional<import("zod").ZodUnknown>;
110
110
  hasLedStatus: import("zod").ZodOptional<import("zod").ZodUnknown>;
111
111
  hasMic: import("zod").ZodOptional<import("zod").ZodUnknown>;
@@ -114,29 +114,31 @@ export declare const cameraListOutputSchema: {
114
114
  smartDetectTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
115
115
  supportFullHdSnapshot: import("zod").ZodOptional<import("zod").ZodUnknown>;
116
116
  videoModes: import("zod").ZodOptional<import("zod").ZodUnknown>;
117
- }, import("zod/v4/core").$loose>>;
118
- lcdMessage: import("zod").ZodOptional<import("zod").ZodObject<{
117
+ }, import("zod/v4/core").$loose>>>;
118
+ lcdMessage: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
119
119
  type: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
120
120
  resetAt: import("zod").ZodOptional<import("zod").ZodUnknown>;
121
121
  text: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
122
- }, import("zod/v4/core").$loose>>;
123
- ledSettings: import("zod").ZodOptional<import("zod").ZodObject<{
122
+ }, import("zod/v4/core").$loose>>>;
123
+ ledSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
124
124
  isEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
125
125
  floodLed: import("zod").ZodOptional<import("zod").ZodUnknown>;
126
126
  welcomeLed: import("zod").ZodOptional<import("zod").ZodUnknown>;
127
- }, import("zod/v4/core").$loose>>;
128
- osdSettings: import("zod").ZodOptional<import("zod").ZodObject<{
127
+ }, import("zod/v4/core").$loose>>>;
128
+ osdSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
129
129
  isNameEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
130
130
  isDateEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
131
131
  isLogoEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
132
132
  isDebugEnabled: import("zod").ZodOptional<import("zod").ZodUnknown>;
133
133
  overlayLocation: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
134
- }, import("zod/v4/core").$loose>>;
135
- smartDetectSettings: import("zod").ZodOptional<import("zod").ZodObject<{
134
+ }, import("zod/v4/core").$loose>>>;
135
+ smartDetectSettings: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
136
136
  objectTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
137
137
  audioTypes: import("zod").ZodOptional<import("zod").ZodUnknown>;
138
- }, import("zod/v4/core").$loose>>;
138
+ }, import("zod/v4/core").$loose>>>;
139
139
  }, import("zod/v4/core").$loose>>;
140
+ totalCount: import("zod").ZodOptional<import("zod").ZodNumber>;
141
+ offset: import("zod").ZodOptional<import("zod").ZodNumber>;
140
142
  };
141
143
  export declare const rtspStreamSchema: import("zod").ZodObject<{
142
144
  high: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.talkbackSessionOutputSchema = exports.rtspStreamOutputSchema = exports.rtspStreamSchema = exports.cameraListOutputSchema = exports.cameraOutputSchema = exports.cameraSchema = void 0;
4
4
  const common_js_1 = require("./common.js");
5
5
  /**
6
- * Verified against live UniFi Protect Integration API 7.3.47 (2026-09-14).
6
+ * Verified against live UniFi Protect Integration API 7.3.47 (2026-09-14) and
7
+ * re-verified against live 7.3.70 (2026-10-12).
7
8
  * The Integration API camera object is far smaller than the internal Protect
8
9
  * app's camera model — no isRecording/lastMotion/channels/firmwareVersion/etc.
9
10
  * `list` and `get-by-id` return the same field set (confirmed live).
@@ -54,11 +55,11 @@ exports.cameraSchema = (0, common_js_1.passthroughObject)({
54
55
  isMicEnabled: (0, common_js_1.unknownField)("Microphone enabled (boolean)"),
55
56
  micVolume: (0, common_js_1.unknownField)("Microphone volume 0-100 (number)"),
56
57
  videoMode: (0, common_js_1.nullableString)('Video mode, e.g. "default"'),
57
- featureFlags: featureFlagsSchema.optional(),
58
- lcdMessage: lcdMessageSchema.optional(),
59
- ledSettings: ledSettingsSchema.optional(),
60
- osdSettings: osdSettingsSchema.optional(),
61
- smartDetectSettings: smartDetectSettingsSchema.optional(),
58
+ featureFlags: featureFlagsSchema.nullable().optional(),
59
+ lcdMessage: lcdMessageSchema.nullable().optional(),
60
+ ledSettings: ledSettingsSchema.nullable().optional(),
61
+ osdSettings: osdSettingsSchema.nullable().optional(),
62
+ smartDetectSettings: smartDetectSettingsSchema.nullable().optional(),
62
63
  });
63
64
  exports.cameraOutputSchema = { ...exports.cameraSchema.shape };
64
65
  exports.cameraListOutputSchema = (0, common_js_1.listResultSchema)(exports.cameraSchema);
@@ -8,24 +8,27 @@ import { z } from "zod";
8
8
  * `undefined` but REJECTS `null`, which fails SDK output validation and breaks
9
9
  * the tool call. So:
10
10
  *
11
- * - Only `id` is strictly typed (`z.string()`) — it is always a present,
12
- * non-null string on every resource, and keeps one real validation guard.
11
+ * - `id` is typed as a string: Protect always sends one on a resource. It is
12
+ * optional like every other field, so a dry-run preview or an empty 204
13
+ * result still validates.
13
14
  * - Every other string uses `nullableString()` = string | null | undefined.
14
15
  * The LLM still sees "this is a string" in the published schema, but null
15
16
  * can never fail validation.
16
17
  * - Non-string fields (booleans, numbers, nested objects whose shape we have
17
18
  * not verified) use `unknownField()` — name + description published, no
18
19
  * runtime type constraint at all.
19
- * - Every object uses `.passthrough()` so unverified/extra fields flow through.
20
+ * - Every object uses `.loose()` so unverified/extra fields flow through. The
21
+ * top level is published loose by instrumentTools (see looseOutput), since
22
+ * a raw shape would publish `additionalProperties: false`.
20
23
  */
21
- /** Create an object schema with `.passthrough()` to allow unknown fields. */
24
+ /** Create an object schema with `.loose()` to allow unknown fields. */
22
25
  export declare function passthroughObject<T extends z.ZodRawShape>(shape: T): z.ZodObject<{ -readonly [P in keyof T]: T[P]; }, z.core.$loose>;
23
26
  /**
24
27
  * An unverified field: name + description are published to clients, but there
25
28
  * is no runtime type constraint (never fails output validation).
26
29
  */
27
30
  export declare function unknownField(description: string): z.ZodOptional<z.ZodUnknown>;
28
- /** The one strictly-typed field: `id` is always a present non-null string. */
31
+ /** A resource `id`: a string when present (optional, like every field). */
29
32
  export declare function idField(description?: string): z.ZodOptional<z.ZodString>;
30
33
  /**
31
34
  * A string field that may be string, null, or absent. Publishes the string
@@ -46,6 +49,28 @@ export declare function hardwareModelFields(): {
46
49
  guid: z.ZodOptional<z.ZodNullable<z.ZodString>>;
47
50
  };
48
51
  /** Output-schema shape for list endpoints that return a top-level array. */
49
- export declare function listResultSchema<T extends z.ZodTypeAny>(itemSchema: T): {
52
+ export declare function listResultSchema<T extends z.ZodType>(itemSchema: T): {
50
53
  result: z.ZodArray<T>;
54
+ totalCount: z.ZodOptional<z.ZodNumber>;
55
+ offset: z.ZodOptional<z.ZodNumber>;
56
+ };
57
+ /**
58
+ * The outputSchema to publish for a tool: always a loose object. Tool modules
59
+ * hand over raw shapes (`{ ...schema.shape }`), and the SDK wraps a raw shape
60
+ * in a plain `z.object`, whose JSON Schema says `additionalProperties: false`.
61
+ * SDK clients validate structuredContent against that published schema, and
62
+ * tools pass Protect's response through, so the first top-level field a
63
+ * firmware update added would fail the call for every client that validates.
64
+ */
65
+ export declare function looseOutput(schema: z.ZodRawShape | z.ZodType): z.ZodObject;
66
+ /**
67
+ * Fields of the preview a write tool returns for `dryRun: true` (see
68
+ * formatDryRun). Declared so the published outputSchema documents them; a
69
+ * resource field of the same name (a file's `path`) takes precedence.
70
+ */
71
+ export declare const dryRunPreviewFields: {
72
+ readonly dryRun: z.ZodOptional<z.ZodLiteral<true>>;
73
+ readonly action: z.ZodOptional<z.ZodString>;
74
+ readonly path: z.ZodOptional<z.ZodString>;
75
+ readonly body: z.ZodOptional<z.ZodUnknown>;
51
76
  };
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dryRunPreviewFields = void 0;
3
4
  exports.passthroughObject = passthroughObject;
4
5
  exports.unknownField = unknownField;
5
6
  exports.idField = idField;
6
7
  exports.nullableString = nullableString;
7
8
  exports.hardwareModelFields = hardwareModelFields;
8
9
  exports.listResultSchema = listResultSchema;
10
+ exports.looseOutput = looseOutput;
9
11
  const zod_1 = require("zod");
10
12
  /**
11
13
  * Schema primitives for Protect outputSchema declarations.
@@ -16,19 +18,22 @@ const zod_1 = require("zod");
16
18
  * `undefined` but REJECTS `null`, which fails SDK output validation and breaks
17
19
  * the tool call. So:
18
20
  *
19
- * - Only `id` is strictly typed (`z.string()`) — it is always a present,
20
- * non-null string on every resource, and keeps one real validation guard.
21
+ * - `id` is typed as a string: Protect always sends one on a resource. It is
22
+ * optional like every other field, so a dry-run preview or an empty 204
23
+ * result still validates.
21
24
  * - Every other string uses `nullableString()` = string | null | undefined.
22
25
  * The LLM still sees "this is a string" in the published schema, but null
23
26
  * can never fail validation.
24
27
  * - Non-string fields (booleans, numbers, nested objects whose shape we have
25
28
  * not verified) use `unknownField()` — name + description published, no
26
29
  * runtime type constraint at all.
27
- * - Every object uses `.passthrough()` so unverified/extra fields flow through.
30
+ * - Every object uses `.loose()` so unverified/extra fields flow through. The
31
+ * top level is published loose by instrumentTools (see looseOutput), since
32
+ * a raw shape would publish `additionalProperties: false`.
28
33
  */
29
- /** Create an object schema with `.passthrough()` to allow unknown fields. */
34
+ /** Create an object schema with `.loose()` to allow unknown fields. */
30
35
  function passthroughObject(shape) {
31
- return zod_1.z.object(shape).passthrough();
36
+ return zod_1.z.object(shape).loose();
32
37
  }
33
38
  /**
34
39
  * An unverified field: name + description are published to clients, but there
@@ -37,7 +42,7 @@ function passthroughObject(shape) {
37
42
  function unknownField(description) {
38
43
  return zod_1.z.unknown().optional().describe(description);
39
44
  }
40
- /** The one strictly-typed field: `id` is always a present non-null string. */
45
+ /** A resource `id`: a string when present (optional, like every field). */
41
46
  function idField(description = "Resource ID") {
42
47
  return zod_1.z.string().optional().describe(description);
43
48
  }
@@ -68,5 +73,39 @@ function hardwareModelFields() {
68
73
  function listResultSchema(itemSchema) {
69
74
  return {
70
75
  result: zod_1.z.array(itemSchema).describe("Array of items returned by the list endpoint"),
76
+ totalCount: zod_1.z.number().int().optional().describe("Paged results only: the number of items in the whole list"),
77
+ offset: zod_1.z.number().int().optional().describe("Paged results only: the index of the first item returned"),
71
78
  };
72
79
  }
80
+ /**
81
+ * The outputSchema to publish for a tool: always a loose object. Tool modules
82
+ * hand over raw shapes (`{ ...schema.shape }`), and the SDK wraps a raw shape
83
+ * in a plain `z.object`, whose JSON Schema says `additionalProperties: false`.
84
+ * SDK clients validate structuredContent against that published schema, and
85
+ * tools pass Protect's response through, so the first top-level field a
86
+ * firmware update added would fail the call for every client that validates.
87
+ */
88
+ function looseOutput(schema) {
89
+ if (schema instanceof zod_1.z.ZodObject)
90
+ return schema.loose();
91
+ if (schema instanceof zod_1.z.ZodType) {
92
+ // structuredContent is always an object; a list wraps its array under
93
+ // `result` (see listResultSchema).
94
+ throw new TypeError("An outputSchema must be a z.object or a raw shape");
95
+ }
96
+ return zod_1.z.looseObject(schema);
97
+ }
98
+ /**
99
+ * Fields of the preview a write tool returns for `dryRun: true` (see
100
+ * formatDryRun). Declared so the published outputSchema documents them; a
101
+ * resource field of the same name (a file's `path`) takes precedence.
102
+ */
103
+ exports.dryRunPreviewFields = {
104
+ dryRun: zod_1.z
105
+ .literal(true)
106
+ .optional()
107
+ .describe("Present and true when this is a dry-run preview: nothing was changed"),
108
+ action: zod_1.z.string().optional().describe("Dry run: the HTTP method that would be sent"),
109
+ path: zod_1.z.string().optional().describe("Dry run: the request path that would be sent"),
110
+ body: zod_1.z.unknown().optional().describe("Dry run: the request body that would be sent"),
111
+ };
@@ -94,7 +94,6 @@ export declare const fobSchema: import("zod").ZodObject<{
94
94
  awayState: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
95
95
  buttonLabels: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
96
96
  featureFlags: import("zod").ZodOptional<import("zod").ZodUnknown>;
97
- hasKeypad: import("zod").ZodOptional<import("zod").ZodUnknown>;
98
97
  armControlSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
99
98
  keypadSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
100
99
  wirelessConnectionState: import("zod").ZodOptional<import("zod").ZodUnknown>;
@@ -150,7 +149,6 @@ export declare const linkStationSchema: import("zod").ZodObject<{
150
149
  ledSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
151
150
  lastEvent: import("zod").ZodOptional<import("zod").ZodUnknown>;
152
151
  alarmHub: import("zod").ZodOptional<import("zod").ZodUnknown>;
153
- deviceTamperStatus: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
154
152
  threadState: import("zod").ZodOptional<import("zod").ZodUnknown>;
155
153
  }, import("zod/v4/core").$loose>;
156
154
  export declare const alarmHubSchema: import("zod").ZodObject<{
@@ -165,7 +163,6 @@ export declare const alarmHubSchema: import("zod").ZodObject<{
165
163
  ledSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
166
164
  lastEvent: import("zod").ZodOptional<import("zod").ZodUnknown>;
167
165
  alarmHub: import("zod").ZodOptional<import("zod").ZodUnknown>;
168
- deviceTamperStatus: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
169
166
  threadState: import("zod").ZodOptional<import("zod").ZodUnknown>;
170
167
  }, import("zod/v4/core").$loose>;
171
168
  export declare const DEVICE_SCHEMAS: {
@@ -265,7 +262,6 @@ export declare const DEVICE_SCHEMAS: {
265
262
  awayState: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
266
263
  buttonLabels: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
267
264
  featureFlags: import("zod").ZodOptional<import("zod").ZodUnknown>;
268
- hasKeypad: import("zod").ZodOptional<import("zod").ZodUnknown>;
269
265
  armControlSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
270
266
  keypadSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
271
267
  wirelessConnectionState: import("zod").ZodOptional<import("zod").ZodUnknown>;
@@ -321,7 +317,6 @@ export declare const DEVICE_SCHEMAS: {
321
317
  ledSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
322
318
  lastEvent: import("zod").ZodOptional<import("zod").ZodUnknown>;
323
319
  alarmHub: import("zod").ZodOptional<import("zod").ZodUnknown>;
324
- deviceTamperStatus: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
325
320
  threadState: import("zod").ZodOptional<import("zod").ZodUnknown>;
326
321
  }, import("zod/v4/core").$loose>;
327
322
  readonly alarm_hub: import("zod").ZodObject<{
@@ -336,7 +331,6 @@ export declare const DEVICE_SCHEMAS: {
336
331
  ledSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
337
332
  lastEvent: import("zod").ZodOptional<import("zod").ZodUnknown>;
338
333
  alarmHub: import("zod").ZodOptional<import("zod").ZodUnknown>;
339
- deviceTamperStatus: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
340
334
  threadState: import("zod").ZodOptional<import("zod").ZodUnknown>;
341
335
  }, import("zod/v4/core").$loose>;
342
336
  };
@@ -431,7 +425,6 @@ export declare function itemOutputShape(key: keyof typeof DEVICE_SCHEMAS): {
431
425
  awayState: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
432
426
  buttonLabels: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
433
427
  featureFlags: import("zod").ZodOptional<import("zod").ZodUnknown>;
434
- hasKeypad: import("zod").ZodOptional<import("zod").ZodUnknown>;
435
428
  armControlSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
436
429
  keypadSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
437
430
  wirelessConnectionState: import("zod").ZodOptional<import("zod").ZodUnknown>;
@@ -483,7 +476,6 @@ export declare function itemOutputShape(key: keyof typeof DEVICE_SCHEMAS): {
483
476
  ledSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
484
477
  lastEvent: import("zod").ZodOptional<import("zod").ZodUnknown>;
485
478
  alarmHub: import("zod").ZodOptional<import("zod").ZodUnknown>;
486
- deviceTamperStatus: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
487
479
  threadState: import("zod").ZodOptional<import("zod").ZodUnknown>;
488
480
  };
489
481
  export declare function listOutputShape(key: keyof typeof DEVICE_SCHEMAS): {
@@ -578,7 +570,6 @@ export declare function listOutputShape(key: keyof typeof DEVICE_SCHEMAS): {
578
570
  awayState: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
579
571
  buttonLabels: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
580
572
  featureFlags: import("zod").ZodOptional<import("zod").ZodUnknown>;
581
- hasKeypad: import("zod").ZodOptional<import("zod").ZodUnknown>;
582
573
  armControlSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
583
574
  keypadSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
584
575
  wirelessConnectionState: import("zod").ZodOptional<import("zod").ZodUnknown>;
@@ -630,7 +621,8 @@ export declare function listOutputShape(key: keyof typeof DEVICE_SCHEMAS): {
630
621
  ledSettings: import("zod").ZodOptional<import("zod").ZodUnknown>;
631
622
  lastEvent: import("zod").ZodOptional<import("zod").ZodUnknown>;
632
623
  alarmHub: import("zod").ZodOptional<import("zod").ZodUnknown>;
633
- deviceTamperStatus: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
634
624
  threadState: import("zod").ZodOptional<import("zod").ZodUnknown>;
635
625
  }, import("zod/v4/core").$loose>>;
626
+ totalCount: import("zod").ZodOptional<import("zod").ZodNumber>;
627
+ offset: import("zod").ZodOptional<import("zod").ZodNumber>;
636
628
  };
@@ -7,13 +7,13 @@ const common_js_1 = require("./common.js");
7
7
  /**
8
8
  * Device schemas for the config-driven Protect device types.
9
9
  *
10
- * Field sets are derived from the UniFi Protect Integration API 7.3.47 docs
10
+ * Field sets are derived from the UniFi Protect Integration API 7.3.70 docs
11
11
  * (per-type response samples). Chimes were additionally verified against live
12
- * console responses (2026-09-14); 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
12
+ * console responses (7.3.47 on 2026-09-14, 7.3.70 on 2026-10-12); the rest
13
+ * have no instances on the available console, so their TOP-LEVEL fields are
14
+ * typed from the published docs while every nested object/array is left as `unknownField()` (z.unknown()) — naming
15
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.
16
+ * shape we have not observed live. `.loose()` lets any extra field flow.
17
17
  *
18
18
  * Convention (see schemas/common.ts): only `id` is strictly typed; strings use
19
19
  * `nullableString()` (API returns explicit null for absent strings); booleans,
@@ -45,16 +45,16 @@ exports.sensorSchema = (0, common_js_1.passthroughObject)({
45
45
  featureFlags: (0, common_js_1.unknownField)("Sensor capability flags, added in 7.3.x (object: temperature, humidity, " +
46
46
  "light, motion, waterLeak, open, tamper, smoke — each { channelCount })"),
47
47
  batteryStatus: (0, common_js_1.unknownField)("Battery status (object: percentage, isLow)"),
48
- stats: (0, common_js_1.unknownField)("Environmental stats (object: light, humidity, temperature)"),
49
- lightSettings: (0, common_js_1.unknownField)("Light threshold settings (object)"),
50
- humiditySettings: (0, common_js_1.unknownField)("Humidity threshold settings (object)"),
51
- temperatureSettings: (0, common_js_1.unknownField)("Temperature threshold settings (object)"),
48
+ stats: (0, common_js_1.unknownField)("Environmental stats (object: light, humidity, temperature — each { value, status })"),
49
+ lightSettings: (0, common_js_1.unknownField)("Light threshold settings (object: isEnabled, margin, lowThreshold, highThreshold)"),
50
+ humiditySettings: (0, common_js_1.unknownField)("Humidity threshold settings (object: isEnabled, margin, lowThreshold, highThreshold)"),
51
+ temperatureSettings: (0, common_js_1.unknownField)("Temperature threshold settings (object: isEnabled, margin, lowThreshold, highThreshold)"),
52
52
  isOpened: (0, common_js_1.unknownField)("Open/close contact state (boolean)"),
53
53
  openStatusChangedAt: (0, common_js_1.unknownField)("Open-status change timestamp in epoch ms (number)"),
54
54
  isMotionDetected: (0, common_js_1.unknownField)("Motion currently detected (boolean)"),
55
55
  motionDetectedAt: (0, common_js_1.unknownField)("Last motion timestamp in epoch ms (number)"),
56
- motionSettings: (0, common_js_1.unknownField)("Motion detection settings (object)"),
57
- glassBreakSettings: (0, common_js_1.unknownField)("Glass-break detection settings (object)"),
56
+ motionSettings: (0, common_js_1.unknownField)("Motion detection settings (object: isEnabled, sensitivity, sensitivityWhenArmed)"),
57
+ glassBreakSettings: (0, common_js_1.unknownField)("Glass-break detection settings (object: isEnabled, sensitivity, sensitivityWhenArmed)"),
58
58
  scheduleMode: (0, common_js_1.nullableString)('Schedule mode: "always" | "when_armed"'),
59
59
  armProfileIds: (0, common_js_1.unknownField)("Arm profile IDs this sensor belongs to (array of strings)"),
60
60
  hasCustomSensitivityWhenArmed: (0, common_js_1.unknownField)("Custom armed sensitivity enabled (boolean)"),
@@ -62,12 +62,13 @@ exports.sensorSchema = (0, common_js_1.passthroughObject)({
62
62
  alarmSettings: (0, common_js_1.unknownField)("Alarm settings (object: isEnabled)"),
63
63
  leakDetectedAt: (0, common_js_1.unknownField)("Last leak timestamp in epoch ms (number)"),
64
64
  externalLeakDetectedAt: (0, common_js_1.unknownField)("Last external-leak timestamp in epoch ms (number)"),
65
- leakSettings: (0, common_js_1.unknownField)("Leak detection settings (object)"),
65
+ leakSettings: (0, common_js_1.unknownField)("Leak detection settings, read-only (object: isInternalEnabled, isExternalEnabled)"),
66
66
  tamperingDetectedAt: (0, common_js_1.unknownField)("Last tampering timestamp in epoch ms (number)"),
67
- wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object: signalState, batteryStatus, bridge)"),
67
+ wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object: signalState { signalQuality, signalStrength }, " +
68
+ "batteryStatus { percentage, isLow }, bridge)"),
68
69
  });
69
- // Verified live (7.1.83): chimes return cameraIds[] and ringSettings[] whose
70
- // items are { cameraId, volume, ringtoneId, repeatTimes }.
70
+ // Verified live (7.1.83, re-verified 7.3.70): chimes return cameraIds[] and
71
+ // ringSettings[] whose items are { cameraId, volume, ringtoneId, repeatTimes }.
71
72
  exports.chimeSchema = (0, common_js_1.passthroughObject)({
72
73
  ...deviceIdentity(),
73
74
  cameraIds: (0, common_js_1.unknownField)("Paired camera IDs (array of strings)"),
@@ -84,25 +85,29 @@ exports.sirenSchema = (0, common_js_1.passthroughObject)({
84
85
  ledSettings: (0, common_js_1.unknownField)("LED settings (object: isEnabled)"),
85
86
  sirenStatus: (0, common_js_1.unknownField)("Current siren status (object: isActive, activatedAt, duration)"),
86
87
  connectionType: (0, common_js_1.nullableString)('Connection type, e.g. "lora"'),
87
- wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object)"),
88
+ wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object: signalState { signalQuality, signalStrength }, " +
89
+ "batteryStatus { percentage, isLow }, bridge)"),
88
90
  });
89
91
  exports.fobSchema = (0, common_js_1.passthroughObject)({
90
92
  ...deviceIdentity(),
91
93
  awayState: (0, common_js_1.nullableString)('Away state, e.g. "ONLINE"'),
92
94
  buttonLabels: (0, common_js_1.nullableString)('Button label preset, e.g. "securityActions"'),
93
- featureFlags: (0, common_js_1.unknownField)("Feature flags (object: buttons[])"),
94
- hasKeypad: (0, common_js_1.unknownField)("Whether this fob has a keypad, added in 7.3.x (boolean)"),
95
+ featureFlags: (0, common_js_1.unknownField)("Feature flags (object: buttons[], hasKeypad — hasKeypad added in 7.3.x)"),
95
96
  armControlSettings: (0, common_js_1.unknownField)("Arm-control config, added in 7.3.x (object: enabled, armProfileId, nightProfileId)"),
96
- keypadSettings: (0, common_js_1.unknownField)("Keypad config, added in 7.3.x (object: beepEnabled, beepVolume, " +
97
- "backlightEnabled, backlightBrightness)"),
98
- wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object)"),
97
+ keypadSettings: (0, common_js_1.unknownField)("Keypad config, added in 7.3.x (object: beepEnabled, beepVolume). The " +
98
+ "backlightEnabled/backlightBrightness fields were dropped in 7.3.70."),
99
+ wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object: signalState { signalQuality, signalStrength }, " +
100
+ "batteryStatus { percentage, isLow }, bridge)"),
99
101
  });
100
102
  exports.relaySchema = (0, common_js_1.passthroughObject)({
101
103
  ...deviceIdentity(),
102
104
  ledSettings: (0, common_js_1.unknownField)("LED settings (object: isEnabled)"),
103
- outputs: (0, common_js_1.unknownField)("Output channels (array of objects)"),
104
- inputs: (0, common_js_1.unknownField)("Input channels (array of objects)"),
105
- wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object)"),
105
+ outputs: (0, common_js_1.unknownField)("Output channels (array of objects: id, name, type, delay, pulseDuration, " +
106
+ "state, rebootState). `id` is the outputId for protect_activate_relay_output"),
107
+ inputs: (0, common_js_1.unknownField)("Input channels (array of objects: id, name, state, actionTrigger, " +
108
+ "actionType, actionOutputId)"),
109
+ wirelessConnectionState: (0, common_js_1.unknownField)("Wireless link state (object: signalState { signalQuality, signalStrength }, " +
110
+ "batteryStatus { percentage, isLow }, bridge)"),
106
111
  });
107
112
  exports.speakerSchema = (0, common_js_1.passthroughObject)({
108
113
  ...deviceIdentity(),
@@ -125,8 +130,12 @@ const linkStationLikeSchema = () => (0, common_js_1.passthroughObject)({
125
130
  isAlarmHub: (0, common_js_1.unknownField)("Whether this device is an alarm hub (boolean)"),
126
131
  ledSettings: (0, common_js_1.unknownField)("LED settings (object: isEnabled)"),
127
132
  lastEvent: (0, common_js_1.unknownField)("Last event timestamp in epoch ms (number)"),
128
- alarmHub: (0, common_js_1.unknownField)("Alarm hub status (object: armed, battery, connector, cover, output, input, …)"),
129
- deviceTamperStatus: (0, common_js_1.nullableString)('Tamper status, added in 7.3.x, e.g. "tampered"'),
133
+ alarmHub: (0, common_js_1.unknownField)("Alarm hub status (object: armed, battery, buckboost, connector, cover, " +
134
+ 'deviceTamperStatus (e.g. "tampered", added in 7.3.x), ' +
135
+ "currentMeterChannelStatus, currentMeterStatus, inputPower, poeout, " +
136
+ "powerMeter, output and input (maps keyed by channel), " +
137
+ "inputTerminalStatus, outputTerminalStatus, emergencyTerminalStatus, " +
138
+ "auxiliaryPowerTerminalStatus)"),
130
139
  threadState: (0, common_js_1.unknownField)("Thread radio state, added in 7.3.x (object: network — status, role, " +
131
140
  "networkName, channel, panId, extendedPanId, joinedDeviceCount, " +
132
141
  "errorReason, lastUpdatedAt)"),