@owine/unifi-network-mcp 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tools/acl.js CHANGED
@@ -6,26 +6,30 @@ const responses_js_1 = require("../utils/responses.js");
6
6
  const query_js_1 = require("../utils/query.js");
7
7
  const safety_js_1 = require("../utils/safety.js");
8
8
  function registerAclTools(server, client, readOnly = false) {
9
- server.tool("unifi_list_acl_rules", "List all ACL (firewall) rules at a site", {
10
- siteId: zod_1.z.string().describe("Site ID"),
11
- offset: zod_1.z
12
- .number()
13
- .int()
14
- .nonnegative()
15
- .optional()
16
- .describe("Number of records to skip (default: 0)"),
17
- limit: zod_1.z
18
- .number()
19
- .int()
20
- .min(1)
21
- .max(200)
22
- .optional()
23
- .describe("Number of records to return (default: 25, max: 200)"),
24
- filter: zod_1.z
25
- .string()
26
- .optional()
27
- .describe("Filter expression"),
28
- }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit, filter }) => {
9
+ server.registerTool("unifi_list_acl_rules", {
10
+ description: "List all ACL (firewall) rules at a site",
11
+ inputSchema: {
12
+ siteId: zod_1.z.string().describe("Site ID"),
13
+ offset: zod_1.z
14
+ .number()
15
+ .int()
16
+ .nonnegative()
17
+ .optional()
18
+ .describe("Number of records to skip (default: 0)"),
19
+ limit: zod_1.z
20
+ .number()
21
+ .int()
22
+ .min(1)
23
+ .max(200)
24
+ .optional()
25
+ .describe("Number of records to return (default: 25, max: 200)"),
26
+ filter: zod_1.z
27
+ .string()
28
+ .optional()
29
+ .describe("Filter expression"),
30
+ },
31
+ annotations: safety_js_1.READ_ONLY,
32
+ }, async ({ siteId, offset, limit, filter }) => {
29
33
  try {
30
34
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
31
35
  const data = await client.get(`/sites/${siteId}/acl-rules${query}`);
@@ -35,10 +39,14 @@ function registerAclTools(server, client, readOnly = false) {
35
39
  return (0, responses_js_1.formatError)(err);
36
40
  }
37
41
  });
38
- server.tool("unifi_get_acl_rule", "Get a specific ACL rule by ID", {
39
- siteId: zod_1.z.string().describe("Site ID"),
40
- aclRuleId: zod_1.z.string().describe("ACL rule ID"),
41
- }, safety_js_1.READ_ONLY, async ({ siteId, aclRuleId }) => {
42
+ server.registerTool("unifi_get_acl_rule", {
43
+ description: "Get a specific ACL rule by ID",
44
+ inputSchema: {
45
+ siteId: zod_1.z.string().describe("Site ID"),
46
+ aclRuleId: zod_1.z.string().describe("ACL rule ID"),
47
+ },
48
+ annotations: safety_js_1.READ_ONLY,
49
+ }, async ({ siteId, aclRuleId }) => {
42
50
  try {
43
51
  const data = await client.get(`/sites/${siteId}/acl-rules/${aclRuleId}`);
44
52
  return (0, responses_js_1.formatSuccess)(data);
@@ -47,9 +55,13 @@ function registerAclTools(server, client, readOnly = false) {
47
55
  return (0, responses_js_1.formatError)(err);
48
56
  }
49
57
  });
50
- server.tool("unifi_get_acl_rule_ordering", "Get user-defined ACL rule ordering", {
51
- siteId: zod_1.z.string().describe("Site ID"),
52
- }, safety_js_1.READ_ONLY, async ({ siteId }) => {
58
+ server.registerTool("unifi_get_acl_rule_ordering", {
59
+ description: "Get user-defined ACL rule ordering",
60
+ inputSchema: {
61
+ siteId: zod_1.z.string().describe("Site ID"),
62
+ },
63
+ annotations: safety_js_1.READ_ONLY,
64
+ }, async ({ siteId }) => {
53
65
  try {
54
66
  const data = await client.get(`/sites/${siteId}/acl-rules/ordering`);
55
67
  return (0, responses_js_1.formatSuccess)(data);
@@ -60,25 +72,29 @@ function registerAclTools(server, client, readOnly = false) {
60
72
  });
61
73
  if (readOnly)
62
74
  return;
63
- server.tool("unifi_create_acl_rule", "Create a new ACL (firewall) rule", {
64
- siteId: zod_1.z.string().describe("Site ID"),
65
- type: zod_1.z.enum(["IPV4", "MAC"]).describe("Rule type"),
66
- name: zod_1.z.string().describe("Rule name"),
67
- enabled: zod_1.z.boolean().describe("Enable the rule"),
68
- action: zod_1.z.enum(["ALLOW", "BLOCK"]).describe("Rule action"),
69
- description: zod_1.z
70
- .string()
71
- .optional()
72
- .describe("Rule description"),
73
- protocolFilter: zod_1.z
74
- .array(zod_1.z.string())
75
- .optional()
76
- .describe("Protocols: TCP, UDP"),
77
- dryRun: zod_1.z
78
- .boolean()
79
- .optional()
80
- .describe("Preview this action without executing it"),
81
- }, safety_js_1.WRITE_NOT_IDEMPOTENT, async ({ siteId, type, name, enabled, action, description, protocolFilter, dryRun }) => {
75
+ server.registerTool("unifi_create_acl_rule", {
76
+ description: "Create a new ACL (firewall) rule",
77
+ inputSchema: {
78
+ siteId: zod_1.z.string().describe("Site ID"),
79
+ type: zod_1.z.enum(["IPV4", "MAC"]).describe("Rule type"),
80
+ name: zod_1.z.string().describe("Rule name"),
81
+ enabled: zod_1.z.boolean().describe("Enable the rule"),
82
+ action: zod_1.z.enum(["ALLOW", "BLOCK"]).describe("Rule action"),
83
+ description: zod_1.z
84
+ .string()
85
+ .optional()
86
+ .describe("Rule description"),
87
+ protocolFilter: zod_1.z
88
+ .array(zod_1.z.string())
89
+ .optional()
90
+ .describe("Protocols: TCP, UDP"),
91
+ dryRun: zod_1.z
92
+ .boolean()
93
+ .optional()
94
+ .describe("Preview this action without executing it"),
95
+ },
96
+ annotations: safety_js_1.WRITE_NOT_IDEMPOTENT,
97
+ }, async ({ siteId, type, name, enabled, action, description, protocolFilter, dryRun }) => {
82
98
  try {
83
99
  const body = { type, name, enabled, action };
84
100
  if (description !== undefined)
@@ -94,17 +110,21 @@ function registerAclTools(server, client, readOnly = false) {
94
110
  return (0, responses_js_1.formatError)(err);
95
111
  }
96
112
  });
97
- server.tool("unifi_update_acl_rule", "Update an ACL rule", {
98
- siteId: zod_1.z.string().describe("Site ID"),
99
- aclRuleId: zod_1.z.string().describe("ACL rule ID"),
100
- rule: zod_1.z
101
- .record(zod_1.z.string(), zod_1.z.unknown())
102
- .describe("ACL rule configuration (JSON object)"),
103
- dryRun: zod_1.z
104
- .boolean()
105
- .optional()
106
- .describe("Preview this action without executing it"),
107
- }, safety_js_1.WRITE, async ({ siteId, aclRuleId, rule, dryRun }) => {
113
+ server.registerTool("unifi_update_acl_rule", {
114
+ description: "Update an ACL rule",
115
+ inputSchema: {
116
+ siteId: zod_1.z.string().describe("Site ID"),
117
+ aclRuleId: zod_1.z.string().describe("ACL rule ID"),
118
+ rule: zod_1.z
119
+ .record(zod_1.z.string(), zod_1.z.unknown())
120
+ .describe("ACL rule configuration (JSON object)"),
121
+ dryRun: zod_1.z
122
+ .boolean()
123
+ .optional()
124
+ .describe("Preview this action without executing it"),
125
+ },
126
+ annotations: safety_js_1.WRITE,
127
+ }, async ({ siteId, aclRuleId, rule, dryRun }) => {
108
128
  try {
109
129
  if (dryRun)
110
130
  return (0, safety_js_1.formatDryRun)("PUT", `/sites/${siteId}/acl-rules/${aclRuleId}`, rule);
@@ -115,18 +135,22 @@ function registerAclTools(server, client, readOnly = false) {
115
135
  return (0, responses_js_1.formatError)(err);
116
136
  }
117
137
  });
118
- server.tool("unifi_delete_acl_rule", "DESTRUCTIVE: Delete an ACL rule", {
119
- siteId: zod_1.z.string().describe("Site ID"),
120
- aclRuleId: zod_1.z.string().describe("ACL rule ID"),
121
- confirm: zod_1.z
122
- .boolean()
123
- .optional()
124
- .describe("Must be true to execute this destructive action"),
125
- dryRun: zod_1.z
126
- .boolean()
127
- .optional()
128
- .describe("Preview this action without executing it"),
129
- }, safety_js_1.DESTRUCTIVE, async ({ siteId, aclRuleId, confirm, dryRun }) => {
138
+ server.registerTool("unifi_delete_acl_rule", {
139
+ description: "DESTRUCTIVE: Delete an ACL rule",
140
+ inputSchema: {
141
+ siteId: zod_1.z.string().describe("Site ID"),
142
+ aclRuleId: zod_1.z.string().describe("ACL rule ID"),
143
+ confirm: zod_1.z
144
+ .boolean()
145
+ .optional()
146
+ .describe("Must be true to execute this destructive action"),
147
+ dryRun: zod_1.z
148
+ .boolean()
149
+ .optional()
150
+ .describe("Preview this action without executing it"),
151
+ },
152
+ annotations: safety_js_1.DESTRUCTIVE,
153
+ }, async ({ siteId, aclRuleId, confirm, dryRun }) => {
130
154
  const guard = (0, safety_js_1.requireConfirmation)(confirm, "This will delete the ACL rule");
131
155
  if (guard)
132
156
  return guard;
@@ -140,16 +164,20 @@ function registerAclTools(server, client, readOnly = false) {
140
164
  return (0, responses_js_1.formatError)(err);
141
165
  }
142
166
  });
143
- server.tool("unifi_reorder_acl_rules", "Reorder user-defined ACL rules", {
144
- siteId: zod_1.z.string().describe("Site ID"),
145
- orderedAclRuleIds: zod_1.z
146
- .array(zod_1.z.string())
147
- .describe("Ordered ACL rule IDs"),
148
- dryRun: zod_1.z
149
- .boolean()
150
- .optional()
151
- .describe("Preview this action without executing it"),
152
- }, safety_js_1.WRITE, async ({ siteId, orderedAclRuleIds, dryRun }) => {
167
+ server.registerTool("unifi_reorder_acl_rules", {
168
+ description: "Reorder user-defined ACL rules",
169
+ inputSchema: {
170
+ siteId: zod_1.z.string().describe("Site ID"),
171
+ orderedAclRuleIds: zod_1.z
172
+ .array(zod_1.z.string())
173
+ .describe("Ordered ACL rule IDs"),
174
+ dryRun: zod_1.z
175
+ .boolean()
176
+ .optional()
177
+ .describe("Preview this action without executing it"),
178
+ },
179
+ annotations: safety_js_1.WRITE,
180
+ }, async ({ siteId, orderedAclRuleIds, dryRun }) => {
153
181
  try {
154
182
  const body = { orderedAclRuleIds };
155
183
  if (dryRun)
@@ -6,23 +6,27 @@ const responses_js_1 = require("../utils/responses.js");
6
6
  const query_js_1 = require("../utils/query.js");
7
7
  const safety_js_1 = require("../utils/safety.js");
8
8
  function registerClientTools(server, client, readOnly = false) {
9
- server.tool("unifi_list_clients", "List all connected clients (wired, wireless, VPN) at a site", {
10
- siteId: zod_1.z.string().describe("Site ID"),
11
- offset: zod_1.z
12
- .number()
13
- .int()
14
- .nonnegative()
15
- .optional()
16
- .describe("Number of records to skip (default: 0)"),
17
- limit: zod_1.z
18
- .number()
19
- .int()
20
- .min(1)
21
- .max(200)
22
- .optional()
23
- .describe("Number of records to return (default: 25, max: 200)"),
24
- filter: zod_1.z.string().optional().describe("Filter expression"),
25
- }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit, filter }) => {
9
+ server.registerTool("unifi_list_clients", {
10
+ description: "List all connected clients (wired, wireless, VPN) at a site",
11
+ inputSchema: {
12
+ siteId: zod_1.z.string().describe("Site ID"),
13
+ offset: zod_1.z
14
+ .number()
15
+ .int()
16
+ .nonnegative()
17
+ .optional()
18
+ .describe("Number of records to skip (default: 0)"),
19
+ limit: zod_1.z
20
+ .number()
21
+ .int()
22
+ .min(1)
23
+ .max(200)
24
+ .optional()
25
+ .describe("Number of records to return (default: 25, max: 200)"),
26
+ filter: zod_1.z.string().optional().describe("Filter expression"),
27
+ },
28
+ annotations: safety_js_1.READ_ONLY,
29
+ }, async ({ siteId, offset, limit, filter }) => {
26
30
  try {
27
31
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
28
32
  const data = await client.get(`/sites/${siteId}/clients${query}`);
@@ -32,10 +36,14 @@ function registerClientTools(server, client, readOnly = false) {
32
36
  return (0, responses_js_1.formatError)(err);
33
37
  }
34
38
  });
35
- server.tool("unifi_get_client", "Get a specific client by ID", {
36
- siteId: zod_1.z.string().describe("Site ID"),
37
- clientId: zod_1.z.string().describe("Client ID"),
38
- }, safety_js_1.READ_ONLY, async ({ siteId, clientId }) => {
39
+ server.registerTool("unifi_get_client", {
40
+ description: "Get a specific client by ID",
41
+ inputSchema: {
42
+ siteId: zod_1.z.string().describe("Site ID"),
43
+ clientId: zod_1.z.string().describe("Client ID"),
44
+ },
45
+ annotations: safety_js_1.READ_ONLY,
46
+ }, async ({ siteId, clientId }) => {
39
47
  try {
40
48
  const data = await client.get(`/sites/${siteId}/clients/${clientId}`);
41
49
  return (0, responses_js_1.formatSuccess)(data);
@@ -46,39 +54,43 @@ function registerClientTools(server, client, readOnly = false) {
46
54
  });
47
55
  if (readOnly)
48
56
  return;
49
- server.tool("unifi_authorize_guest", "Authorize a guest client on a hotspot network", {
50
- siteId: zod_1.z.string().describe("Site ID"),
51
- clientId: zod_1.z.string().describe("Client ID"),
52
- timeLimitMinutes: zod_1.z
53
- .number()
54
- .int()
55
- .min(1)
56
- .max(1000000)
57
- .optional()
58
- .describe("How long (in minutes) the guest will be authorized (1-1000000)"),
59
- dataUsageLimitMBytes: zod_1.z
60
- .number()
61
- .int()
62
- .min(1)
63
- .max(1048576)
64
- .optional()
65
- .describe("Data usage limit in megabytes (1-1048576)"),
66
- rxRateLimitKbps: zod_1.z
67
- .number()
68
- .int()
69
- .min(2)
70
- .max(100000)
71
- .optional()
72
- .describe("Download rate limit in kilobits per second (2-100000)"),
73
- txRateLimitKbps: zod_1.z
74
- .number()
75
- .int()
76
- .min(2)
77
- .max(100000)
78
- .optional()
79
- .describe("Upload rate limit in kilobits per second (2-100000)"),
80
- dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
81
- }, safety_js_1.WRITE_NOT_IDEMPOTENT, async ({ siteId, clientId, timeLimitMinutes, dataUsageLimitMBytes, rxRateLimitKbps, txRateLimitKbps, dryRun, }) => {
57
+ server.registerTool("unifi_authorize_guest", {
58
+ description: "Authorize a guest client on a hotspot network",
59
+ inputSchema: {
60
+ siteId: zod_1.z.string().describe("Site ID"),
61
+ clientId: zod_1.z.string().describe("Client ID"),
62
+ timeLimitMinutes: zod_1.z
63
+ .number()
64
+ .int()
65
+ .min(1)
66
+ .max(1000000)
67
+ .optional()
68
+ .describe("How long (in minutes) the guest will be authorized (1-1000000)"),
69
+ dataUsageLimitMBytes: zod_1.z
70
+ .number()
71
+ .int()
72
+ .min(1)
73
+ .max(1048576)
74
+ .optional()
75
+ .describe("Data usage limit in megabytes (1-1048576)"),
76
+ rxRateLimitKbps: zod_1.z
77
+ .number()
78
+ .int()
79
+ .min(2)
80
+ .max(100000)
81
+ .optional()
82
+ .describe("Download rate limit in kilobits per second (2-100000)"),
83
+ txRateLimitKbps: zod_1.z
84
+ .number()
85
+ .int()
86
+ .min(2)
87
+ .max(100000)
88
+ .optional()
89
+ .describe("Upload rate limit in kilobits per second (2-100000)"),
90
+ dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
91
+ },
92
+ annotations: safety_js_1.WRITE_NOT_IDEMPOTENT,
93
+ }, async ({ siteId, clientId, timeLimitMinutes, dataUsageLimitMBytes, rxRateLimitKbps, txRateLimitKbps, dryRun, }) => {
82
94
  const body = {
83
95
  action: "AUTHORIZE_GUEST_ACCESS",
84
96
  };
@@ -100,11 +112,15 @@ function registerClientTools(server, client, readOnly = false) {
100
112
  return (0, responses_js_1.formatError)(err);
101
113
  }
102
114
  });
103
- server.tool("unifi_unauthorize_guest", "Unauthorize a guest client", {
104
- siteId: zod_1.z.string().describe("Site ID"),
105
- clientId: zod_1.z.string().describe("Client ID"),
106
- dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
107
- }, safety_js_1.WRITE, async ({ siteId, clientId, dryRun }) => {
115
+ server.registerTool("unifi_unauthorize_guest", {
116
+ description: "Unauthorize a guest client",
117
+ inputSchema: {
118
+ siteId: zod_1.z.string().describe("Site ID"),
119
+ clientId: zod_1.z.string().describe("Client ID"),
120
+ dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
121
+ },
122
+ annotations: safety_js_1.WRITE,
123
+ }, async ({ siteId, clientId, dryRun }) => {
108
124
  const body = { action: "UNAUTHORIZE_GUEST_ACCESS" };
109
125
  if (dryRun)
110
126
  return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/clients/${clientId}/actions`, body);
@@ -6,23 +6,27 @@ const responses_js_1 = require("../utils/responses.js");
6
6
  const query_js_1 = require("../utils/query.js");
7
7
  const safety_js_1 = require("../utils/safety.js");
8
8
  function registerDeviceTools(server, client, readOnly = false) {
9
- server.tool("unifi_list_devices", "List all adopted devices at a site", {
10
- siteId: zod_1.z.string().describe("Site ID"),
11
- offset: zod_1.z
12
- .number()
13
- .int()
14
- .nonnegative()
15
- .optional()
16
- .describe("Number of records to skip (default: 0)"),
17
- limit: zod_1.z
18
- .number()
19
- .int()
20
- .min(1)
21
- .max(200)
22
- .optional()
23
- .describe("Number of records to return (default: 25, max: 200)"),
24
- filter: zod_1.z.string().optional().describe("Filter expression"),
25
- }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit, filter }) => {
9
+ server.registerTool("unifi_list_devices", {
10
+ description: "List all adopted devices at a site",
11
+ inputSchema: {
12
+ siteId: zod_1.z.string().describe("Site ID"),
13
+ offset: zod_1.z
14
+ .number()
15
+ .int()
16
+ .nonnegative()
17
+ .optional()
18
+ .describe("Number of records to skip (default: 0)"),
19
+ limit: zod_1.z
20
+ .number()
21
+ .int()
22
+ .min(1)
23
+ .max(200)
24
+ .optional()
25
+ .describe("Number of records to return (default: 25, max: 200)"),
26
+ filter: zod_1.z.string().optional().describe("Filter expression"),
27
+ },
28
+ annotations: safety_js_1.READ_ONLY,
29
+ }, async ({ siteId, offset, limit, filter }) => {
26
30
  try {
27
31
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
28
32
  const data = await client.get(`/sites/${siteId}/devices${query}`);
@@ -32,10 +36,14 @@ function registerDeviceTools(server, client, readOnly = false) {
32
36
  return (0, responses_js_1.formatError)(err);
33
37
  }
34
38
  });
35
- server.tool("unifi_get_device", "Get a specific device by ID", {
36
- siteId: zod_1.z.string().describe("Site ID"),
37
- deviceId: zod_1.z.string().describe("Device ID"),
38
- }, safety_js_1.READ_ONLY, async ({ siteId, deviceId }) => {
39
+ server.registerTool("unifi_get_device", {
40
+ description: "Get a specific device by ID",
41
+ inputSchema: {
42
+ siteId: zod_1.z.string().describe("Site ID"),
43
+ deviceId: zod_1.z.string().describe("Device ID"),
44
+ },
45
+ annotations: safety_js_1.READ_ONLY,
46
+ }, async ({ siteId, deviceId }) => {
39
47
  try {
40
48
  const data = await client.get(`/sites/${siteId}/devices/${deviceId}`);
41
49
  return (0, responses_js_1.formatSuccess)(data);
@@ -44,10 +52,14 @@ function registerDeviceTools(server, client, readOnly = false) {
44
52
  return (0, responses_js_1.formatError)(err);
45
53
  }
46
54
  });
47
- server.tool("unifi_get_device_statistics", "Get latest statistics for a device", {
48
- siteId: zod_1.z.string().describe("Site ID"),
49
- deviceId: zod_1.z.string().describe("Device ID"),
50
- }, safety_js_1.READ_ONLY, async ({ siteId, deviceId }) => {
55
+ server.registerTool("unifi_get_device_statistics", {
56
+ description: "Get latest statistics for a device",
57
+ inputSchema: {
58
+ siteId: zod_1.z.string().describe("Site ID"),
59
+ deviceId: zod_1.z.string().describe("Device ID"),
60
+ },
61
+ annotations: safety_js_1.READ_ONLY,
62
+ }, async ({ siteId, deviceId }) => {
51
63
  try {
52
64
  const data = await client.get(`/sites/${siteId}/devices/${deviceId}/statistics/latest`);
53
65
  return (0, responses_js_1.formatSuccess)(data);
@@ -56,22 +68,26 @@ function registerDeviceTools(server, client, readOnly = false) {
56
68
  return (0, responses_js_1.formatError)(err);
57
69
  }
58
70
  });
59
- server.tool("unifi_list_pending_devices", "List devices pending adoption (global, not site-specific)", {
60
- offset: zod_1.z
61
- .number()
62
- .int()
63
- .nonnegative()
64
- .optional()
65
- .describe("Number of records to skip (default: 0)"),
66
- limit: zod_1.z
67
- .number()
68
- .int()
69
- .min(1)
70
- .max(200)
71
- .optional()
72
- .describe("Number of records to return (default: 25, max: 200)"),
73
- filter: zod_1.z.string().optional().describe("Filter expression"),
74
- }, safety_js_1.READ_ONLY, async ({ offset, limit, filter }) => {
71
+ server.registerTool("unifi_list_pending_devices", {
72
+ description: "List devices pending adoption (global, not site-specific)",
73
+ inputSchema: {
74
+ offset: zod_1.z
75
+ .number()
76
+ .int()
77
+ .nonnegative()
78
+ .optional()
79
+ .describe("Number of records to skip (default: 0)"),
80
+ limit: zod_1.z
81
+ .number()
82
+ .int()
83
+ .min(1)
84
+ .max(200)
85
+ .optional()
86
+ .describe("Number of records to return (default: 25, max: 200)"),
87
+ filter: zod_1.z.string().optional().describe("Filter expression"),
88
+ },
89
+ annotations: safety_js_1.READ_ONLY,
90
+ }, async ({ offset, limit, filter }) => {
75
91
  try {
76
92
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
77
93
  const data = await client.get(`/pending-devices${query}`);
@@ -83,11 +99,15 @@ function registerDeviceTools(server, client, readOnly = false) {
83
99
  });
84
100
  if (readOnly)
85
101
  return;
86
- server.tool("unifi_adopt_device", "Adopt a pending device", {
87
- siteId: zod_1.z.string().describe("Site ID"),
88
- macAddress: zod_1.z.string().describe("MAC address of the device"),
89
- dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
90
- }, safety_js_1.WRITE_NOT_IDEMPOTENT, async ({ siteId, macAddress, dryRun }) => {
102
+ server.registerTool("unifi_adopt_device", {
103
+ description: "Adopt a pending device",
104
+ inputSchema: {
105
+ siteId: zod_1.z.string().describe("Site ID"),
106
+ macAddress: zod_1.z.string().describe("MAC address of the device"),
107
+ dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
108
+ },
109
+ annotations: safety_js_1.WRITE_NOT_IDEMPOTENT,
110
+ }, async ({ siteId, macAddress, dryRun }) => {
91
111
  const body = {
92
112
  macAddress,
93
113
  ignoreDeviceLimit: false,
@@ -102,12 +122,16 @@ function registerDeviceTools(server, client, readOnly = false) {
102
122
  return (0, responses_js_1.formatError)(err);
103
123
  }
104
124
  });
105
- server.tool("unifi_remove_device", "DESTRUCTIVE: Remove (unadopt) a device from a site. If the device is online, it will be reset to factory defaults", {
106
- siteId: zod_1.z.string().describe("Site ID"),
107
- deviceId: zod_1.z.string().describe("Device ID"),
108
- confirm: zod_1.z.boolean().optional().describe("Must be true to execute this destructive action"),
109
- dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
110
- }, safety_js_1.DESTRUCTIVE, async ({ siteId, deviceId, confirm, dryRun }) => {
125
+ server.registerTool("unifi_remove_device", {
126
+ description: "DESTRUCTIVE: Remove (unadopt) a device from a site. If the device is online, it will be reset to factory defaults",
127
+ inputSchema: {
128
+ siteId: zod_1.z.string().describe("Site ID"),
129
+ deviceId: zod_1.z.string().describe("Device ID"),
130
+ confirm: zod_1.z.boolean().optional().describe("Must be true to execute this destructive action"),
131
+ dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
132
+ },
133
+ annotations: safety_js_1.DESTRUCTIVE,
134
+ }, async ({ siteId, deviceId, confirm, dryRun }) => {
111
135
  const guard = (0, safety_js_1.requireConfirmation)(confirm, "This will remove the device from the site. If the device is online, it will be reset to factory defaults");
112
136
  if (guard)
113
137
  return guard;
@@ -121,11 +145,15 @@ function registerDeviceTools(server, client, readOnly = false) {
121
145
  return (0, responses_js_1.formatError)(err);
122
146
  }
123
147
  });
124
- server.tool("unifi_restart_device", "Restart a device", {
125
- siteId: zod_1.z.string().describe("Site ID"),
126
- deviceId: zod_1.z.string().describe("Device ID"),
127
- dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
128
- }, safety_js_1.WRITE, async ({ siteId, deviceId, dryRun }) => {
148
+ server.registerTool("unifi_restart_device", {
149
+ description: "Restart a device",
150
+ inputSchema: {
151
+ siteId: zod_1.z.string().describe("Site ID"),
152
+ deviceId: zod_1.z.string().describe("Device ID"),
153
+ dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
154
+ },
155
+ annotations: safety_js_1.WRITE,
156
+ }, async ({ siteId, deviceId, dryRun }) => {
129
157
  const body = { action: "RESTART" };
130
158
  if (dryRun)
131
159
  return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/devices/${deviceId}/actions`, body);
@@ -137,12 +165,16 @@ function registerDeviceTools(server, client, readOnly = false) {
137
165
  return (0, responses_js_1.formatError)(err);
138
166
  }
139
167
  });
140
- server.tool("unifi_power_cycle_port", "Power cycle a specific port on a device (PoE restart)", {
141
- siteId: zod_1.z.string().describe("Site ID"),
142
- deviceId: zod_1.z.string().describe("Device ID"),
143
- portIdx: zod_1.z.number().int().describe("Port index number"),
144
- dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
145
- }, safety_js_1.WRITE, async ({ siteId, deviceId, portIdx, dryRun }) => {
168
+ server.registerTool("unifi_power_cycle_port", {
169
+ description: "Power cycle a specific port on a device (PoE restart)",
170
+ inputSchema: {
171
+ siteId: zod_1.z.string().describe("Site ID"),
172
+ deviceId: zod_1.z.string().describe("Device ID"),
173
+ portIdx: zod_1.z.number().int().describe("Port index number"),
174
+ dryRun: zod_1.z.boolean().optional().describe("Preview this action without executing it"),
175
+ },
176
+ annotations: safety_js_1.WRITE,
177
+ }, async ({ siteId, deviceId, portIdx, dryRun }) => {
146
178
  const body = { action: "POWER_CYCLE" };
147
179
  if (dryRun)
148
180
  return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/devices/${deviceId}/interfaces/ports/${portIdx}/actions`, body);