@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
@@ -1,151 +1,154 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ARM_PROFILE_CONFIRM_FIELDS = void 0;
3
4
  exports.registerArmProfileTools = registerArmProfileTools;
4
5
  const zod_1 = require("zod");
5
6
  const responses_js_1 = require("../utils/responses.js");
6
7
  const safety_js_1 = require("../utils/safety.js");
7
8
  const url_js_1 = require("../utils/url.js");
9
+ const paging_js_1 = require("../utils/paging.js");
10
+ const params_js_1 = require("../utils/params.js");
8
11
  const misc_js_1 = require("../schemas/misc.js");
9
- const ACTIVATION_DELAY_VALUES = [0, 60000, 300000, 600000];
12
+ const activationDelay = zod_1.z
13
+ .union([zod_1.z.literal(0), zod_1.z.literal(60000), zod_1.z.literal(300000), zod_1.z.literal(600000)])
14
+ .describe("Activation delay in milliseconds. Allowed: 0 (none), 60000 (1m), 300000 (5m), 600000 (10m).");
15
+ const armProfileFields = {
16
+ name: zod_1.z.string().min(1).max(255).describe("Name of the arm profile"),
17
+ automations: zod_1.z.array(zod_1.z.string()).describe("List of automation IDs associated with this arm profile"),
18
+ schedules: zod_1.z
19
+ .array(zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()))
20
+ .describe('List of arm schedules, e.g. [{ start: "0 22 * * *", end: "0 6 * * *" }] (cron strings)'),
21
+ recordEverything: zod_1.z.boolean().describe("Whether to record everything when this arm profile is active"),
22
+ activationDelay,
23
+ };
24
+ /**
25
+ * Arm profile settings that change what the alarm protects, and when: a
26
+ * longer activationDelay leaves the site unprotected for longer after arming,
27
+ * and recordEverything decides whether an armed site records at all.
28
+ */
29
+ exports.ARM_PROFILE_CONFIRM_FIELDS = ["schedules", "automations", "activationDelay", "recordEverything"];
30
+ // Each gate's confirm input and handler check share one action text.
31
+ const DELETE_PROFILE = (0, safety_js_1.confirmGate)("delete the arm profile");
32
+ const SET_CURRENT_PROFILE = (0, safety_js_1.confirmGate)("change which arm profile the alarm uses");
33
+ const DISARM = (0, safety_js_1.confirmGate)("disarm the alarm");
10
34
  function registerArmProfileTools(server, client, readOnly) {
11
35
  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; each profile (7.1.83 docs): id, name, automations[], creator, schedules[], recordEverything, activationDelay (0 | 60000 | 300000 | 600000), createdAt, updatedAt.",
36
+ 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.3.70 docs): id, name, automations[], creator, schedules[], recordEverything, activationDelay (0 | 60000 | 300000 | 600000), createdAt, updatedAt." + paging_js_1.PAGING_NOTE,
37
+ inputSchema: paging_js_1.pagingInput,
13
38
  outputSchema: misc_js_1.armProfileListOutputSchema,
14
39
  annotations: safety_js_1.READ_ONLY,
15
- }, async () => {
16
- try {
17
- const data = await client.get("/arm-profiles");
18
- return (0, responses_js_1.formatSuccess)(data);
19
- }
20
- catch (err) {
21
- return (0, responses_js_1.formatError)(err);
22
- }
40
+ }, async ({ limit, offset }) => {
41
+ const data = await client.get("/arm-profiles");
42
+ return (0, responses_js_1.formatSuccess)((0, paging_js_1.pageOf)(data, limit, offset));
23
43
  });
24
44
  if (readOnly)
25
45
  return;
26
46
  server.registerTool("protect_create_arm_profile", {
27
47
  description: "Create a new arm profile. Only available when using local alarm manager.",
28
48
  inputSchema: {
29
- name: zod_1.z.string().min(1).max(255).describe("Name of the arm profile"),
30
- automations: zod_1.z.array(zod_1.z.string()).describe("List of automation IDs associated with this arm profile"),
31
- schedules: zod_1.z.array(zod_1.z.record(zod_1.z.string(), zod_1.z.unknown())).describe("List of arm schedules"),
32
- recordEverything: zod_1.z.boolean().describe("Whether to record everything when this arm profile is active"),
33
- activationDelay: zod_1.z
34
- .union([zod_1.z.literal(0), zod_1.z.literal(60000), zod_1.z.literal(300000), zod_1.z.literal(600000)])
35
- .describe("Activation delay in milliseconds. Allowed: 0 (none), 60000 (1m), 300000 (5m), 600000 (10m)."),
36
- dryRun: zod_1.z.boolean().optional().describe("If true, return what would happen without making changes"),
49
+ ...armProfileFields,
50
+ dryRun: (0, safety_js_1.dryRunInput)(),
37
51
  },
38
52
  outputSchema: misc_js_1.armProfileOutputSchema,
39
53
  annotations: safety_js_1.WRITE,
40
54
  }, async ({ name, automations, schedules, recordEverything, activationDelay, dryRun }) => {
41
- try {
42
- const body = { name, automations, schedules, recordEverything, activationDelay };
43
- if (dryRun) {
44
- return (0, safety_js_1.formatDryRun)("POST", "/arm-profiles", body);
45
- }
46
- const data = await client.post("/arm-profiles", body);
47
- return (0, responses_js_1.formatSuccess)(data);
48
- }
49
- catch (err) {
50
- return (0, responses_js_1.formatError)(err);
55
+ const body = { name, automations, schedules, recordEverything, activationDelay };
56
+ if (dryRun) {
57
+ return (0, safety_js_1.formatDryRun)("POST", "/arm-profiles", body);
51
58
  }
59
+ const data = await client.post("/arm-profiles", body);
60
+ return (0, responses_js_1.formatSuccess)(data);
52
61
  });
53
62
  server.registerTool("protect_set_current_arm_profile", {
54
- description: "Set the current arm profile to be used when enabling the arm alarm. Only available when using local alarm manager.",
63
+ description: "Set the current arm profile to be used when enabling the arm alarm. Only available when using local alarm manager. Requires confirm: true: it changes what the alarm protects when armed.",
55
64
  inputSchema: {
56
- armProfileId: zod_1.z.string().describe("The arm profile ID to set as current"),
57
- dryRun: zod_1.z.boolean().optional().describe("If true, return what would happen without making changes"),
65
+ armProfileId: (0, params_js_1.armProfileId)().describe("The arm profile ID to set as current"),
66
+ confirm: SET_CURRENT_PROFILE.input,
67
+ dryRun: (0, safety_js_1.dryRunInput)(),
58
68
  },
59
- annotations: safety_js_1.WRITE,
60
- }, async ({ armProfileId, dryRun }) => {
61
- try {
62
- const body = { armProfileId };
63
- if (dryRun) {
64
- return (0, safety_js_1.formatDryRun)("PATCH", "/arm-profiles/settings", body);
65
- }
66
- const data = await client.patch("/arm-profiles/settings", body);
67
- return (0, responses_js_1.formatSuccess)(data);
68
- }
69
- catch (err) {
70
- return (0, responses_js_1.formatError)(err);
69
+ annotations: safety_js_1.DESTRUCTIVE,
70
+ }, async ({ armProfileId, confirm, dryRun }) => {
71
+ const body = { armProfileId };
72
+ if (dryRun) {
73
+ return (0, safety_js_1.formatDryRun)("PATCH", "/arm-profiles/settings", body);
71
74
  }
75
+ const denied = SET_CURRENT_PROFILE.check(confirm);
76
+ if (denied)
77
+ return denied;
78
+ const data = await client.patch("/arm-profiles/settings", body);
79
+ return (0, responses_js_1.formatSuccess)(data);
72
80
  });
73
81
  server.registerTool("protect_update_arm_profile", {
74
- description: "Update an existing arm profile (partial update via PATCH). Only available when using local alarm manager.",
82
+ description: `Update an existing arm profile (partial update via PATCH). Changing ${exports.ARM_PROFILE_CONFIRM_FIELDS.join(", ")} affects alarm coverage and requires confirm: true. Only available when using local alarm manager.`,
75
83
  inputSchema: {
76
- id: zod_1.z.string().describe("Arm profile ID"),
84
+ id: (0, params_js_1.armProfileId)().describe("Arm profile ID"),
77
85
  settings: zod_1.z
78
- .record(zod_1.z.string(), zod_1.z.unknown())
79
- .describe(`Partial arm profile settings. Known fields: name (string 1-255 chars), automations (array of strings), schedules (array of objects), recordEverything (boolean), activationDelay (number, allowed: ${ACTIVATION_DELAY_VALUES.join(", ")})`),
80
- dryRun: zod_1.z.boolean().optional().describe("If true, return what would happen without making changes"),
86
+ .object(armProfileFields)
87
+ .partial()
88
+ .loose()
89
+ .refine((o) => Object.keys(o).length > 0, "settings must include at least one field")
90
+ .describe("Partial arm profile settings to update"),
91
+ confirm: (0, safety_js_1.confirmInput)(`change ${exports.ARM_PROFILE_CONFIRM_FIELDS.join(", ")}`),
92
+ dryRun: (0, safety_js_1.dryRunInput)(),
81
93
  },
82
94
  outputSchema: misc_js_1.armProfileOutputSchema,
83
- annotations: safety_js_1.WRITE,
84
- }, async ({ id, settings, dryRun }) => {
85
- try {
86
- if (dryRun) {
87
- return (0, safety_js_1.formatDryRun)("PATCH", (0, url_js_1.safePath) `/arm-profiles/${id}`, settings);
88
- }
89
- const data = await client.patch((0, url_js_1.safePath) `/arm-profiles/${id}`, settings);
90
- return (0, responses_js_1.formatSuccess)(data);
91
- }
92
- catch (err) {
93
- return (0, responses_js_1.formatError)(err);
95
+ annotations: safety_js_1.DESTRUCTIVE,
96
+ }, async ({ id, settings, confirm, dryRun }) => {
97
+ if (dryRun) {
98
+ return (0, safety_js_1.formatDryRun)("PATCH", (0, url_js_1.safePath) `/arm-profiles/${id}`, settings);
94
99
  }
100
+ const denied = (0, safety_js_1.requireConfirmationForFields)(settings, exports.ARM_PROFILE_CONFIRM_FIELDS, confirm);
101
+ if (denied)
102
+ return denied;
103
+ const data = await client.patch((0, url_js_1.safePath) `/arm-profiles/${id}`, settings);
104
+ return (0, responses_js_1.formatSuccess)(data);
95
105
  });
96
106
  server.registerTool("protect_delete_arm_profile", {
97
- description: "Delete an arm profile by ID. Only available when using local alarm manager.",
107
+ description: "Delete an arm profile by ID. Only available when using local alarm manager. Requires confirm: true.",
98
108
  inputSchema: {
99
- id: zod_1.z.string().describe("Arm profile ID"),
100
- dryRun: zod_1.z.boolean().optional().describe("If true, return what would happen without making changes"),
109
+ id: (0, params_js_1.armProfileId)().describe("Arm profile ID"),
110
+ confirm: DELETE_PROFILE.input,
111
+ dryRun: (0, safety_js_1.dryRunInput)(),
101
112
  },
102
113
  annotations: safety_js_1.DESTRUCTIVE,
103
- }, async ({ id, dryRun }) => {
104
- try {
105
- if (dryRun) {
106
- return (0, safety_js_1.formatDryRun)("DELETE", (0, url_js_1.safePath) `/arm-profiles/${id}`);
107
- }
108
- const data = await client.delete((0, url_js_1.safePath) `/arm-profiles/${id}`);
109
- return (0, responses_js_1.formatSuccess)(data);
110
- }
111
- catch (err) {
112
- return (0, responses_js_1.formatError)(err);
114
+ }, async ({ id, confirm, dryRun }) => {
115
+ if (dryRun) {
116
+ return (0, safety_js_1.formatDryRun)("DELETE", (0, url_js_1.safePath) `/arm-profiles/${id}`);
113
117
  }
118
+ const denied = DELETE_PROFILE.check(confirm);
119
+ if (denied)
120
+ return denied;
121
+ const data = await client.delete((0, url_js_1.safePath) `/arm-profiles/${id}`);
122
+ return (0, responses_js_1.formatSuccess)(data);
114
123
  });
115
124
  server.registerTool("protect_enable_arm_alarm", {
116
125
  description: "Enable the arm alarm using the currently selected arm profile. Only available when using local alarm manager.",
117
126
  inputSchema: {
118
- dryRun: zod_1.z.boolean().optional().describe("If true, return what would happen without making changes"),
127
+ dryRun: (0, safety_js_1.dryRunInput)(),
119
128
  },
120
129
  annotations: safety_js_1.WRITE,
121
130
  }, async ({ dryRun }) => {
122
- try {
123
- if (dryRun) {
124
- return (0, safety_js_1.formatDryRun)("POST", "/arm-profiles/enable");
125
- }
126
- const data = await client.post("/arm-profiles/enable");
127
- return (0, responses_js_1.formatSuccess)(data);
128
- }
129
- catch (err) {
130
- return (0, responses_js_1.formatError)(err);
131
+ if (dryRun) {
132
+ return (0, safety_js_1.formatDryRun)("POST", "/arm-profiles/enable");
131
133
  }
134
+ const data = await client.post("/arm-profiles/enable");
135
+ return (0, responses_js_1.formatSuccess)(data);
132
136
  });
133
137
  server.registerTool("protect_disable_arm_alarm", {
134
- description: "Disable the arm alarm. Only available when using local alarm manager.",
138
+ description: "Disable (disarm) the arm alarm, leaving the site unprotected until it is re-armed; requires confirm: true. Only available when using local alarm manager.",
135
139
  inputSchema: {
136
- dryRun: zod_1.z.boolean().optional().describe("If true, return what would happen without making changes"),
140
+ confirm: DISARM.input,
141
+ dryRun: (0, safety_js_1.dryRunInput)(),
137
142
  },
138
- annotations: safety_js_1.WRITE,
139
- }, async ({ dryRun }) => {
140
- try {
141
- if (dryRun) {
142
- return (0, safety_js_1.formatDryRun)("POST", "/arm-profiles/disable");
143
- }
144
- const data = await client.post("/arm-profiles/disable");
145
- return (0, responses_js_1.formatSuccess)(data);
146
- }
147
- catch (err) {
148
- return (0, responses_js_1.formatError)(err);
143
+ annotations: safety_js_1.DESTRUCTIVE,
144
+ }, async ({ confirm, dryRun }) => {
145
+ if (dryRun) {
146
+ return (0, safety_js_1.formatDryRun)("POST", "/arm-profiles/disable");
149
147
  }
148
+ const denied = DISARM.check(confirm);
149
+ if (denied)
150
+ return denied;
151
+ const data = await client.post("/arm-profiles/disable");
152
+ return (0, responses_js_1.formatSuccess)(data);
150
153
  });
151
154
  }
@@ -1,3 +1,3 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1
+ import type { ToolRegistrar } from "./registrar.js";
2
2
  import { ProtectClient } from "../client.js";
3
- export declare function registerCameraTools(server: McpServer, client: ProtectClient, readOnly: boolean): void;
3
+ export declare function registerCameraTools(server: ToolRegistrar, client: ProtectClient, readOnly: boolean): void;