@owine/unifi-network-mcp 0.9.0 → 2.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.
package/README.md CHANGED
@@ -15,15 +15,7 @@ An MCP (Model Context Protocol) server that exposes the UniFi Network Integratio
15
15
  Add to Claude Code with a single command — no clone or build needed:
16
16
 
17
17
  ```bash
18
- claude mcp add-json unifi-network '{
19
- "command": "npx",
20
- "args": ["-y", "@owine/unifi-network-mcp"],
21
- "env": {
22
- "UNIFI_NETWORK_HOST": "192.168.1.1",
23
- "UNIFI_NETWORK_API_KEY": "your-api-key",
24
- "UNIFI_NETWORK_VERIFY_SSL": "false"
25
- }
26
- }' -s user
18
+ claude mcp add-json unifi-network '{"command":"npx","args":["-y","@owine/unifi-network-mcp@latest"],"env":{"UNIFI_NETWORK_HOST":"192.168.1.1","UNIFI_NETWORK_API_KEY":"your-api-key","UNIFI_NETWORK_VERIFY_SSL":"false"}}' -s user
27
19
  ```
28
20
 
29
21
  Use `-s user` for global availability across all projects, or `-s project` for the current project only.
@@ -42,15 +34,7 @@ npm run build
42
34
  Then add to Claude Code:
43
35
 
44
36
  ```bash
45
- claude mcp add-json unifi-network '{
46
- "command": "node",
47
- "args": ["/path/to/unifi-network-mcp/dist/index.js"],
48
- "env": {
49
- "UNIFI_NETWORK_HOST": "192.168.1.1",
50
- "UNIFI_NETWORK_API_KEY": "your-api-key",
51
- "UNIFI_NETWORK_VERIFY_SSL": "false"
52
- }
53
- }' -s user
37
+ claude mcp add-json unifi-network '{"command":"node","args":["/path/to/unifi-network-mcp/dist/index.js"],"env":{"UNIFI_NETWORK_HOST":"192.168.1.1","UNIFI_NETWORK_API_KEY":"your-api-key","UNIFI_NETWORK_VERIFY_SSL":"false"}}' -s user
54
38
  ```
55
39
 
56
40
  ### Environment Variables
@@ -71,7 +55,7 @@ Alternatively, add to your `~/.claude.json` under the top-level `"mcpServers"` k
71
55
  "mcpServers": {
72
56
  "unifi-network": {
73
57
  "command": "npx",
74
- "args": ["-y", "@owine/unifi-network-mcp"],
58
+ "args": ["-y", "@owine/unifi-network-mcp@latest"],
75
59
  "env": {
76
60
  "UNIFI_NETWORK_HOST": "192.168.1.1",
77
61
  "UNIFI_NETWORK_API_KEY": "your-api-key",
package/dist/config.js CHANGED
@@ -11,7 +11,7 @@ const configSchema = zod_1.z.object({
11
11
  .describe("Verify SSL certificates"),
12
12
  readOnly: zod_1.z
13
13
  .boolean()
14
- .default(false)
14
+ .default(true)
15
15
  .describe("When true, only read-only tools are registered"),
16
16
  });
17
17
  function loadConfig() {
@@ -19,7 +19,7 @@ function loadConfig() {
19
19
  host: process.env.UNIFI_NETWORK_HOST,
20
20
  apiKey: process.env.UNIFI_NETWORK_API_KEY,
21
21
  verifySsl: process.env.UNIFI_NETWORK_VERIFY_SSL?.toLowerCase() !== "false",
22
- readOnly: process.env.UNIFI_NETWORK_READ_ONLY?.toLowerCase() === "true",
22
+ readOnly: process.env.UNIFI_NETWORK_READ_ONLY?.toLowerCase() !== "false",
23
23
  });
24
24
  if (!result.success) {
25
25
  console.error("Configuration error:", result.error.format());
@@ -49,7 +49,7 @@ function registerDeviceTools(server, client, readOnly = false) {
49
49
  deviceId: zod_1.z.string().describe("Device ID"),
50
50
  }, safety_js_1.READ_ONLY, async ({ siteId, deviceId }) => {
51
51
  try {
52
- const data = await client.get(`/sites/${siteId}/devices/${deviceId}/statistics`);
52
+ const data = await client.get(`/sites/${siteId}/devices/${deviceId}/statistics/latest`);
53
53
  return (0, responses_js_1.formatSuccess)(data);
54
54
  }
55
55
  catch (err) {
@@ -74,7 +74,7 @@ function registerDeviceTools(server, client, readOnly = false) {
74
74
  }, safety_js_1.READ_ONLY, async ({ offset, limit, filter }) => {
75
75
  try {
76
76
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
77
- const data = await client.get(`/devices/pending${query}`);
77
+ const data = await client.get(`/pending-devices${query}`);
78
78
  return (0, responses_js_1.formatSuccess)(data);
79
79
  }
80
80
  catch (err) {
@@ -145,9 +145,9 @@ function registerDeviceTools(server, client, readOnly = false) {
145
145
  }, safety_js_1.WRITE, async ({ siteId, deviceId, portIdx, dryRun }) => {
146
146
  const body = { action: "POWER_CYCLE" };
147
147
  if (dryRun)
148
- return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/devices/${deviceId}/ports/${portIdx}/actions`, body);
148
+ return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/devices/${deviceId}/interfaces/ports/${portIdx}/actions`, body);
149
149
  try {
150
- const data = await client.post(`/sites/${siteId}/devices/${deviceId}/ports/${portIdx}/actions`, body);
150
+ const data = await client.post(`/sites/${siteId}/devices/${deviceId}/interfaces/ports/${portIdx}/actions`, body);
151
151
  return (0, responses_js_1.formatSuccess)(data);
152
152
  }
153
153
  catch (err) {
@@ -28,7 +28,7 @@ function registerDnsPolicyTools(server, client, readOnly = false) {
28
28
  }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit, filter }) => {
29
29
  try {
30
30
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
31
- const data = await client.get(`/sites/${siteId}/dns-policies${query}`);
31
+ const data = await client.get(`/sites/${siteId}/dns/policies${query}`);
32
32
  return (0, responses_js_1.formatSuccess)(data);
33
33
  }
34
34
  catch (err) {
@@ -40,7 +40,7 @@ function registerDnsPolicyTools(server, client, readOnly = false) {
40
40
  dnsPolicyId: zod_1.z.string().describe("DNS policy ID"),
41
41
  }, safety_js_1.READ_ONLY, async ({ siteId, dnsPolicyId }) => {
42
42
  try {
43
- const data = await client.get(`/sites/${siteId}/dns-policies/${dnsPolicyId}`);
43
+ const data = await client.get(`/sites/${siteId}/dns/policies/${dnsPolicyId}`);
44
44
  return (0, responses_js_1.formatSuccess)(data);
45
45
  }
46
46
  catch (err) {
@@ -61,8 +61,8 @@ function registerDnsPolicyTools(server, client, readOnly = false) {
61
61
  }, safety_js_1.WRITE_NOT_IDEMPOTENT, async ({ siteId, policy, dryRun }) => {
62
62
  try {
63
63
  if (dryRun)
64
- return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/dns-policies`, policy);
65
- const data = await client.post(`/sites/${siteId}/dns-policies`, policy);
64
+ return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/dns/policies`, policy);
65
+ const data = await client.post(`/sites/${siteId}/dns/policies`, policy);
66
66
  return (0, responses_js_1.formatSuccess)(data);
67
67
  }
68
68
  catch (err) {
@@ -82,8 +82,8 @@ function registerDnsPolicyTools(server, client, readOnly = false) {
82
82
  }, safety_js_1.WRITE, async ({ siteId, dnsPolicyId, policy, dryRun }) => {
83
83
  try {
84
84
  if (dryRun)
85
- return (0, safety_js_1.formatDryRun)("PUT", `/sites/${siteId}/dns-policies/${dnsPolicyId}`, policy);
86
- const data = await client.put(`/sites/${siteId}/dns-policies/${dnsPolicyId}`, policy);
85
+ return (0, safety_js_1.formatDryRun)("PUT", `/sites/${siteId}/dns/policies/${dnsPolicyId}`, policy);
86
+ const data = await client.put(`/sites/${siteId}/dns/policies/${dnsPolicyId}`, policy);
87
87
  return (0, responses_js_1.formatSuccess)(data);
88
88
  }
89
89
  catch (err) {
@@ -107,8 +107,8 @@ function registerDnsPolicyTools(server, client, readOnly = false) {
107
107
  return guard;
108
108
  try {
109
109
  if (dryRun)
110
- return (0, safety_js_1.formatDryRun)("DELETE", `/sites/${siteId}/dns-policies/${dnsPolicyId}`, {});
111
- const data = await client.delete(`/sites/${siteId}/dns-policies/${dnsPolicyId}`);
110
+ return (0, safety_js_1.formatDryRun)("DELETE", `/sites/${siteId}/dns/policies/${dnsPolicyId}`, {});
111
+ const data = await client.delete(`/sites/${siteId}/dns/policies/${dnsPolicyId}`);
112
112
  return (0, responses_js_1.formatSuccess)(data);
113
113
  }
114
114
  catch (err) {
@@ -217,6 +217,27 @@ function registerFirewallTools(server, client, readOnly = false) {
217
217
  return (0, responses_js_1.formatError)(err);
218
218
  }
219
219
  });
220
+ server.tool("unifi_patch_firewall_policy", "Partially update a firewall policy (e.g. toggle logging)", {
221
+ siteId: zod_1.z.string().describe("Site ID"),
222
+ firewallPolicyId: zod_1.z.string().describe("Firewall policy ID"),
223
+ policy: zod_1.z
224
+ .record(zod_1.z.string(), zod_1.z.unknown())
225
+ .describe("Partial firewall policy fields to update (e.g. { loggingEnabled: true })"),
226
+ dryRun: zod_1.z
227
+ .boolean()
228
+ .optional()
229
+ .describe("Preview this action without executing it"),
230
+ }, safety_js_1.WRITE, async ({ siteId, firewallPolicyId, policy, dryRun }) => {
231
+ try {
232
+ if (dryRun)
233
+ return (0, safety_js_1.formatDryRun)("PATCH", `/sites/${siteId}/firewall/policies/${firewallPolicyId}`, policy);
234
+ const data = await client.patch(`/sites/${siteId}/firewall/policies/${firewallPolicyId}`, policy);
235
+ return (0, responses_js_1.formatSuccess)(data);
236
+ }
237
+ catch (err) {
238
+ return (0, responses_js_1.formatError)(err);
239
+ }
240
+ });
220
241
  server.tool("unifi_delete_firewall_policy", "DESTRUCTIVE: Delete a firewall policy", {
221
242
  siteId: zod_1.z.string().describe("Site ID"),
222
243
  firewallPolicyId: zod_1.z.string().describe("Firewall policy ID"),
@@ -24,7 +24,7 @@ function registerSupportingTools(server, client) {
24
24
  }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit }) => {
25
25
  try {
26
26
  const query = (0, query_js_1.buildQuery)({ offset, limit });
27
- const data = await client.get(`/sites/${siteId}/wan-interfaces${query}`);
27
+ const data = await client.get(`/sites/${siteId}/wans${query}`);
28
28
  return (0, responses_js_1.formatSuccess)(data);
29
29
  }
30
30
  catch (err) {
@@ -53,7 +53,7 @@ function registerSupportingTools(server, client) {
53
53
  }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit, filter }) => {
54
54
  try {
55
55
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
56
- const data = await client.get(`/sites/${siteId}/vpn-tunnels${query}`);
56
+ const data = await client.get(`/sites/${siteId}/vpn/site-to-site-tunnels${query}`);
57
57
  return (0, responses_js_1.formatSuccess)(data);
58
58
  }
59
59
  catch (err) {
@@ -82,7 +82,7 @@ function registerSupportingTools(server, client) {
82
82
  }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit, filter }) => {
83
83
  try {
84
84
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
85
- const data = await client.get(`/sites/${siteId}/vpn-servers${query}`);
85
+ const data = await client.get(`/sites/${siteId}/vpn/servers${query}`);
86
86
  return (0, responses_js_1.formatSuccess)(data);
87
87
  }
88
88
  catch (err) {
@@ -111,7 +111,7 @@ function registerSupportingTools(server, client) {
111
111
  }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit, filter }) => {
112
112
  try {
113
113
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
114
- const data = await client.get(`/sites/${siteId}/radius-profiles${query}`);
114
+ const data = await client.get(`/sites/${siteId}/radius/profiles${query}`);
115
115
  return (0, responses_js_1.formatSuccess)(data);
116
116
  }
117
117
  catch (err) {
@@ -28,7 +28,7 @@ function registerWifiTools(server, client, readOnly = false) {
28
28
  }, safety_js_1.READ_ONLY, async ({ siteId, offset, limit, filter }) => {
29
29
  try {
30
30
  const query = (0, query_js_1.buildQuery)({ offset, limit, filter });
31
- const data = await client.get(`/sites/${siteId}/wifi${query}`);
31
+ const data = await client.get(`/sites/${siteId}/wifi/broadcasts${query}`);
32
32
  return (0, responses_js_1.formatSuccess)(data);
33
33
  }
34
34
  catch (err) {
@@ -40,7 +40,7 @@ function registerWifiTools(server, client, readOnly = false) {
40
40
  wifiBroadcastId: zod_1.z.string().describe("WiFi Broadcast ID"),
41
41
  }, safety_js_1.READ_ONLY, async ({ siteId, wifiBroadcastId }) => {
42
42
  try {
43
- const data = await client.get(`/sites/${siteId}/wifi/${wifiBroadcastId}`);
43
+ const data = await client.get(`/sites/${siteId}/wifi/broadcasts/${wifiBroadcastId}`);
44
44
  return (0, responses_js_1.formatSuccess)(data);
45
45
  }
46
46
  catch (err) {
@@ -70,8 +70,8 @@ function registerWifiTools(server, client, readOnly = false) {
70
70
  broadcastingFrequenciesGHz: broadcastingFrequenciesGHz.map(Number),
71
71
  };
72
72
  if (dryRun)
73
- return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/wifi`, body);
74
- const data = await client.post(`/sites/${siteId}/wifi`, body);
73
+ return (0, safety_js_1.formatDryRun)("POST", `/sites/${siteId}/wifi/broadcasts`, body);
74
+ const data = await client.post(`/sites/${siteId}/wifi/broadcasts`, body);
75
75
  return (0, responses_js_1.formatSuccess)(data);
76
76
  }
77
77
  catch (err) {
@@ -95,8 +95,8 @@ function registerWifiTools(server, client, readOnly = false) {
95
95
  if (enabled !== undefined)
96
96
  body.enabled = enabled;
97
97
  if (dryRun)
98
- return (0, safety_js_1.formatDryRun)("PUT", `/sites/${siteId}/wifi/${wifiBroadcastId}`, body);
99
- const data = await client.put(`/sites/${siteId}/wifi/${wifiBroadcastId}`, body);
98
+ return (0, safety_js_1.formatDryRun)("PUT", `/sites/${siteId}/wifi/broadcasts/${wifiBroadcastId}`, body);
99
+ const data = await client.put(`/sites/${siteId}/wifi/broadcasts/${wifiBroadcastId}`, body);
100
100
  return (0, responses_js_1.formatSuccess)(data);
101
101
  }
102
102
  catch (err) {
@@ -124,7 +124,7 @@ function registerWifiTools(server, client, readOnly = false) {
124
124
  if (guard)
125
125
  return guard;
126
126
  try {
127
- let path = `/sites/${siteId}/wifi/${wifiBroadcastId}`;
127
+ let path = `/sites/${siteId}/wifi/broadcasts/${wifiBroadcastId}`;
128
128
  if (force) {
129
129
  path += "?force=true";
130
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owine/unifi-network-mcp",
3
- "version": "0.9.0",
3
+ "version": "2.0.0",
4
4
  "description": "MCP server for the UniFi Network API",
5
5
  "main": "dist/index.js",
6
6
  "bin": {