unifi-network-mcp 0.2.0__tar.gz → 0.3.1__tar.gz
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.
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/.well-known/mcp-server.json +8 -4
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/PKG-INFO +133 -50
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/README.md +132 -49
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/pyproject.toml +13 -1
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/bootstrap.py +8 -20
- unifi_network_mcp-0.3.1/src/config/config.yaml +120 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/jobs.py +1 -3
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/main.py +73 -64
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/managers/client_manager.py +112 -40
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/managers/connection_manager.py +40 -121
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/managers/device_manager.py +9 -19
- unifi_network_mcp-0.3.1/src/managers/event_manager.py +182 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/managers/firewall_manager.py +63 -186
- unifi_network_mcp-0.3.1/src/managers/hotspot_manager.py +196 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/managers/network_manager.py +28 -81
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/managers/qos_manager.py +10 -25
- unifi_network_mcp-0.3.1/src/managers/routing_manager.py +232 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/managers/stats_manager.py +24 -65
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/managers/system_manager.py +50 -143
- unifi_network_mcp-0.3.1/src/managers/traffic_route_manager.py +191 -0
- unifi_network_mcp-0.3.1/src/managers/usergroup_manager.py +186 -0
- unifi_network_mcp-0.3.1/src/managers/vpn_manager.py +304 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/runtime.py +41 -6
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/schemas.py +2 -4
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tool_index.py +4 -8
- unifi_network_mcp-0.3.1/src/tools/clients.py +578 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/config.py +4 -9
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/devices.py +149 -48
- unifi_network_mcp-0.3.1/src/tools/events.py +181 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/firewall.py +89 -138
- unifi_network_mcp-0.3.1/src/tools/hotspot.py +232 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/network.py +85 -100
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/port_forwards.py +64 -125
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/qos.py +79 -95
- unifi_network_mcp-0.3.1/src/tools/routing.py +332 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/stats.py +18 -55
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/system.py +2 -4
- unifi_network_mcp-0.3.1/src/tools/traffic_routes.py +242 -0
- unifi_network_mcp-0.3.1/src/tools/usergroups.py +238 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools/vpn.py +14 -38
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/tools_manifest.json +397 -34
- unifi_network_mcp-0.3.1/src/utils/confirmation.py +218 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/utils/diagnostics.py +15 -13
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/utils/lazy_tool_loader.py +37 -7
- unifi_network_mcp-0.3.1/src/utils/meta_tools.py +350 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/utils/permissions.py +6 -4
- unifi_network_mcp-0.3.1/src/utils/tool_loader.py +103 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/validator_registry.py +22 -35
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/src/validators.py +5 -8
- unifi_network_mcp-0.2.0/src/config/config.yaml +0 -74
- unifi_network_mcp-0.2.0/src/managers/vpn_manager.py +0 -206
- unifi_network_mcp-0.2.0/src/tools/clients.py +0 -342
- unifi_network_mcp-0.2.0/src/tools/traffic_routes.py +0 -705
- unifi_network_mcp-0.2.0/src/utils/meta_tools.py +0 -215
- unifi_network_mcp-0.2.0/src/utils/tool_loader.py +0 -37
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/.gitignore +0 -0
- {unifi_network_mcp-0.2.0 → unifi_network_mcp-0.3.1}/LICENSE +0 -0
|
@@ -46,10 +46,14 @@
|
|
|
46
46
|
"tool": "unifi_tool_index",
|
|
47
47
|
"description": "Returns machine-readable list of all available tools with schemas"
|
|
48
48
|
},
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
49
|
+
"execution": {
|
|
50
|
+
"tool": "unifi_execute",
|
|
51
|
+
"description": "Execute any discovered tool synchronously"
|
|
52
|
+
},
|
|
53
|
+
"batch_operations": {
|
|
54
|
+
"start_tool": "unifi_batch",
|
|
55
|
+
"status_tool": "unifi_batch_status",
|
|
56
|
+
"description": "Parallel batch execution for bulk operations"
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: unifi-network-mcp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Unifi Network MCP Server
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.13
|
|
@@ -45,7 +45,7 @@ A self-hosted [Model Context Protocol](https://github.com/modelcontextprotocol)
|
|
|
45
45
|
* [Overview](#overview)
|
|
46
46
|
* [Context Optimization](#context-optimization)
|
|
47
47
|
* [Tool Index](#tool-index)
|
|
48
|
-
* [
|
|
48
|
+
* [Tool Execution](#tool-execution)
|
|
49
49
|
* [Runtime Configuration](#runtime-configuration)
|
|
50
50
|
* [Diagnostics (Advanced Logging)](#diagnostics-advanced-logging)
|
|
51
51
|
* [Developer Console (Local Tool Tester)](#developer-console-local-tool-tester)
|
|
@@ -62,6 +62,7 @@ A self-hosted [Model Context Protocol](https://github.com/modelcontextprotocol)
|
|
|
62
62
|
|
|
63
63
|
* Full catalog of UniFi controller operations – firewall, traffic-routes, port-forwards, QoS, VPN, WLANs, stats, devices, clients **and more**.
|
|
64
64
|
* All mutating tools require `confirm=true` so nothing can change your network by accident.
|
|
65
|
+
* **Workflow automation friendly** – set `UNIFI_AUTO_CONFIRM=true` to skip confirmation prompts (ideal for n8n, Make, Zapier).
|
|
65
66
|
* Works over **stdio** (FastMCP). Optional SSE HTTP endpoint can be enabled via config.
|
|
66
67
|
* **Code execution mode** with tool index, async operations, and TypeScript examples.
|
|
67
68
|
* One-liner launch via the console-script **`unifi-network-mcp`**.
|
|
@@ -267,73 +268,73 @@ The server exposes a special `unifi_tool_index` tool that returns a complete lis
|
|
|
267
268
|
- Dynamic client configuration
|
|
268
269
|
- IDE autocomplete support
|
|
269
270
|
|
|
270
|
-
###
|
|
271
|
+
### Tool Execution
|
|
271
272
|
|
|
272
|
-
|
|
273
|
+
The server provides two execution modes for discovered tools:
|
|
273
274
|
|
|
274
|
-
**
|
|
275
|
+
**Single Tool Execution (synchronous):**
|
|
275
276
|
```json
|
|
276
277
|
{
|
|
277
|
-
"name": "
|
|
278
|
+
"name": "unifi_execute",
|
|
278
279
|
"arguments": {
|
|
279
|
-
"tool": "
|
|
280
|
-
"arguments": {
|
|
281
|
-
"mac_address": "aa:bb:cc:dd:ee:ff",
|
|
282
|
-
"confirm": true
|
|
283
|
-
}
|
|
280
|
+
"tool": "unifi_list_clients",
|
|
281
|
+
"arguments": {}
|
|
284
282
|
}
|
|
285
283
|
}
|
|
286
284
|
```
|
|
287
285
|
|
|
288
|
-
**
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
"jobId": "af33b233cbdc860c"
|
|
292
|
-
}
|
|
293
|
-
```
|
|
286
|
+
**Batch Execution (parallel, async):**
|
|
287
|
+
|
|
288
|
+
For bulk operations or long-running tasks, use batch mode:
|
|
294
289
|
|
|
295
|
-
**Check job status:**
|
|
296
290
|
```json
|
|
297
291
|
{
|
|
298
|
-
"name": "
|
|
292
|
+
"name": "unifi_batch",
|
|
299
293
|
"arguments": {
|
|
300
|
-
"
|
|
294
|
+
"operations": [
|
|
295
|
+
{"tool": "unifi_get_client_details", "arguments": {"mac": "aa:bb:cc:dd:ee:ff"}},
|
|
296
|
+
{"tool": "unifi_get_client_details", "arguments": {"mac": "11:22:33:44:55:66"}}
|
|
297
|
+
]
|
|
301
298
|
}
|
|
302
299
|
}
|
|
303
300
|
```
|
|
304
301
|
|
|
305
|
-
**Response
|
|
302
|
+
**Response:**
|
|
306
303
|
```json
|
|
307
304
|
{
|
|
308
|
-
"
|
|
309
|
-
|
|
305
|
+
"jobs": [
|
|
306
|
+
{"index": 0, "tool": "unifi_get_client_details", "jobId": "af33b233cbdc860c"},
|
|
307
|
+
{"index": 1, "tool": "unifi_get_client_details", "jobId": "bf44c344dcde971d"}
|
|
308
|
+
],
|
|
309
|
+
"message": "Started 2 operation(s). Use unifi_batch_status to check progress."
|
|
310
310
|
}
|
|
311
311
|
```
|
|
312
312
|
|
|
313
|
-
**
|
|
313
|
+
**Check batch status:**
|
|
314
314
|
```json
|
|
315
315
|
{
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
|
|
319
|
-
|
|
316
|
+
"name": "unifi_batch_status",
|
|
317
|
+
"arguments": {
|
|
318
|
+
"jobIds": ["af33b233cbdc860c", "bf44c344dcde971d"]
|
|
319
|
+
}
|
|
320
320
|
}
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
**Response
|
|
323
|
+
**Response:**
|
|
324
324
|
```json
|
|
325
325
|
{
|
|
326
|
-
"
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
326
|
+
"jobs": [
|
|
327
|
+
{"jobId": "af33b233cbdc860c", "status": "done", "result": {...}},
|
|
328
|
+
{"jobId": "bf44c344dcde971d", "status": "done", "result": {...}}
|
|
329
|
+
]
|
|
330
330
|
}
|
|
331
331
|
```
|
|
332
332
|
|
|
333
333
|
**Notes:**
|
|
334
|
+
- Use `unifi_execute` for single operations (returns result directly)
|
|
335
|
+
- Use `unifi_batch` + `unifi_batch_status` for parallel/bulk operations
|
|
334
336
|
- Jobs are stored in-memory only (no persistence)
|
|
335
337
|
- Job IDs are unique per server session
|
|
336
|
-
- Failed jobs capture exception details in the `error` field
|
|
337
338
|
|
|
338
339
|
### Using with Claude Desktop
|
|
339
340
|
|
|
@@ -360,22 +361,32 @@ Practical examples showing programmatic usage:
|
|
|
360
361
|
```python
|
|
361
362
|
from mcp import ClientSession, stdio_client
|
|
362
363
|
|
|
363
|
-
#
|
|
364
|
-
|
|
364
|
+
# Discover tools
|
|
365
|
+
tools = await session.call_tool("unifi_tool_index", {})
|
|
366
|
+
|
|
367
|
+
# Execute a single tool (returns result directly)
|
|
368
|
+
result = await session.call_tool("unifi_execute", {
|
|
369
|
+
"tool": "unifi_list_clients",
|
|
370
|
+
"arguments": {}
|
|
371
|
+
})
|
|
365
372
|
|
|
366
|
-
#
|
|
367
|
-
|
|
368
|
-
"
|
|
369
|
-
|
|
373
|
+
# Batch execution for parallel operations
|
|
374
|
+
batch = await session.call_tool("unifi_batch", {
|
|
375
|
+
"operations": [
|
|
376
|
+
{"tool": "unifi_get_client_details", "arguments": {"mac": "..."}},
|
|
377
|
+
{"tool": "unifi_get_device_details", "arguments": {"mac": "..."}}
|
|
378
|
+
]
|
|
370
379
|
})
|
|
371
380
|
|
|
372
|
-
# Check
|
|
373
|
-
status = await session.call_tool("
|
|
381
|
+
# Check batch status
|
|
382
|
+
status = await session.call_tool("unifi_batch_status", {
|
|
383
|
+
"jobIds": [j["jobId"] for j in batch["jobs"]]
|
|
384
|
+
})
|
|
374
385
|
```
|
|
375
386
|
|
|
376
387
|
**Three complete examples:**
|
|
377
388
|
- `query_tool_index.py` - Discover available tools
|
|
378
|
-
- `use_async_jobs.py` -
|
|
389
|
+
- `use_async_jobs.py` - Batch operations and status checking
|
|
379
390
|
- `programmatic_client.py` - Build custom Python clients
|
|
380
391
|
|
|
381
392
|
See [`examples/python/README.md`](examples/python/README.md) for complete examples.
|
|
@@ -392,15 +403,18 @@ The server advertises its capabilities via an MCP identity file at [`.well-known
|
|
|
392
403
|
"capabilities": {
|
|
393
404
|
"tools": true,
|
|
394
405
|
"tool_index": true,
|
|
395
|
-
"
|
|
406
|
+
"batch_operations": true
|
|
396
407
|
},
|
|
397
408
|
"features": {
|
|
398
409
|
"tool_index": {
|
|
399
410
|
"tool": "unifi_tool_index"
|
|
400
411
|
},
|
|
401
|
-
"
|
|
402
|
-
"
|
|
403
|
-
|
|
412
|
+
"execution": {
|
|
413
|
+
"tool": "unifi_execute"
|
|
414
|
+
},
|
|
415
|
+
"batch_operations": {
|
|
416
|
+
"start_tool": "unifi_batch",
|
|
417
|
+
"status_tool": "unifi_batch_status"
|
|
404
418
|
}
|
|
405
419
|
}
|
|
406
420
|
}
|
|
@@ -520,6 +534,44 @@ The server merges settings from **environment variables**, an optional `.env` fi
|
|
|
520
534
|
| `UNIFI_VERIFY_SSL` | Set to `false` if using self-signed certs |
|
|
521
535
|
| `UNIFI_CONTROLLER_TYPE` | Controller API path type: `auto` (detect), `proxy` (UniFi OS), `direct` (standalone). Default `auto` |
|
|
522
536
|
| `UNIFI_MCP_HTTP_ENABLED` | Set `true` to enable optional HTTP SSE server (default `false`) |
|
|
537
|
+
| `UNIFI_AUTO_CONFIRM` | Set `true` to auto-confirm all mutating operations (skips preview step). Ideal for workflow automation (n8n, Make, Zapier). Default `false` |
|
|
538
|
+
| `UNIFI_TOOL_REGISTRATION_MODE` | Tool loading mode: `lazy` (default), `eager`, or `meta_only`. See [Context Optimization](#context-optimization) |
|
|
539
|
+
| `UNIFI_ENABLED_CATEGORIES` | Comma-separated list of tool categories to load (eager mode). See table below |
|
|
540
|
+
| `UNIFI_ENABLED_TOOLS` | Comma-separated list of specific tool names to register (eager mode) |
|
|
541
|
+
|
|
542
|
+
### Tool Categories (for UNIFI_ENABLED_CATEGORIES)
|
|
543
|
+
|
|
544
|
+
When using eager mode with category filtering, these are the valid category names:
|
|
545
|
+
|
|
546
|
+
| Category | Description | Example Tools |
|
|
547
|
+
|----------|-------------|---------------|
|
|
548
|
+
| `clients` | Client listing, blocking, guest auth | `unifi_list_clients`, `unifi_block_client` |
|
|
549
|
+
| `config` | Configuration management | - |
|
|
550
|
+
| `devices` | Device listing, reboot, locate, upgrade | `unifi_list_devices`, `unifi_reboot_device` |
|
|
551
|
+
| `events` | Events and alarms | `unifi_list_events`, `unifi_list_alarms` |
|
|
552
|
+
| `firewall` | Firewall rules and groups | `unifi_list_firewall_rules`, `unifi_create_firewall_rule` |
|
|
553
|
+
| `hotspot` | Vouchers for guest network | `unifi_list_vouchers`, `unifi_create_voucher` |
|
|
554
|
+
| `network` | Network/VLAN management | `unifi_list_networks`, `unifi_create_network` |
|
|
555
|
+
| `port_forwards` | Port forwarding rules | `unifi_list_port_forwards` |
|
|
556
|
+
| `qos` | QoS/traffic shaping rules | `unifi_list_qos_rules`, `unifi_create_qos_rule` |
|
|
557
|
+
| `routing` | Static routes (V1 API) | `unifi_list_routes`, `unifi_create_route` |
|
|
558
|
+
| `stats` | Statistics and metrics | `unifi_get_client_stats`, `unifi_get_device_stats` |
|
|
559
|
+
| `system` | System info, health, settings | `unifi_get_system_info`, `unifi_get_network_health` |
|
|
560
|
+
| `traffic_routes` | Policy-based routing (V2 API) | `unifi_list_traffic_routes` |
|
|
561
|
+
| `usergroups` | Bandwidth profiles/user groups | `unifi_list_usergroups`, `unifi_create_usergroup` |
|
|
562
|
+
| `vpn` | VPN servers and clients | `unifi_list_vpn_servers`, `unifi_list_vpn_clients` |
|
|
563
|
+
|
|
564
|
+
**Example usage:**
|
|
565
|
+
```bash
|
|
566
|
+
# Load only client and system tools
|
|
567
|
+
export UNIFI_TOOL_REGISTRATION_MODE=eager
|
|
568
|
+
export UNIFI_ENABLED_CATEGORIES=clients,system
|
|
569
|
+
|
|
570
|
+
# Or load specific tools only
|
|
571
|
+
export UNIFI_ENABLED_TOOLS=unifi_list_clients,unifi_list_devices,unifi_get_system_info
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
**Note:** Tools may also be filtered by the `permissions` section in config.yaml (e.g., `clients.update: false` blocks mutating client tools).
|
|
523
575
|
|
|
524
576
|
### Controller Type Detection
|
|
525
577
|
|
|
@@ -842,6 +894,37 @@ See [docs/permissions.md](docs/permissions.md) for complete documentation includ
|
|
|
842
894
|
* `unifi_force_reconnect_client`
|
|
843
895
|
* `unifi_authorize_guest`
|
|
844
896
|
* `unifi_unauthorize_guest`
|
|
897
|
+
* `unifi_set_client_ip_settings`
|
|
898
|
+
|
|
899
|
+
### Events & Alarms
|
|
900
|
+
|
|
901
|
+
* `unifi_list_events`
|
|
902
|
+
* `unifi_list_alarms`
|
|
903
|
+
* `unifi_archive_alarm`
|
|
904
|
+
* `unifi_archive_all_alarms`
|
|
905
|
+
* `unifi_get_event_types`
|
|
906
|
+
|
|
907
|
+
### Routing (Static Routes)
|
|
908
|
+
|
|
909
|
+
* `unifi_list_routes`
|
|
910
|
+
* `unifi_get_route_details`
|
|
911
|
+
* `unifi_create_route`
|
|
912
|
+
* `unifi_update_route`
|
|
913
|
+
* `unifi_list_active_routes`
|
|
914
|
+
|
|
915
|
+
### Hotspot (Vouchers)
|
|
916
|
+
|
|
917
|
+
* `unifi_list_vouchers`
|
|
918
|
+
* `unifi_get_voucher_details`
|
|
919
|
+
* `unifi_create_voucher`
|
|
920
|
+
* `unifi_revoke_voucher`
|
|
921
|
+
|
|
922
|
+
### User Groups
|
|
923
|
+
|
|
924
|
+
* `unifi_list_usergroups`
|
|
925
|
+
* `unifi_get_usergroup_details`
|
|
926
|
+
* `unifi_create_usergroup`
|
|
927
|
+
* `unifi_update_usergroup`
|
|
845
928
|
|
|
846
929
|
### Statistics & Alerts
|
|
847
930
|
|
|
@@ -1018,9 +1101,9 @@ uv run python devtools/dev_console.py
|
|
|
1018
1101
|
|
|
1019
1102
|
# You'll see a menu of all tools including:
|
|
1020
1103
|
# - unifi_tool_index (list all tools with schemas)
|
|
1021
|
-
# -
|
|
1022
|
-
# -
|
|
1023
|
-
# - All
|
|
1104
|
+
# - unifi_execute (run any discovered tool)
|
|
1105
|
+
# - unifi_batch / unifi_batch_status (parallel operations)
|
|
1106
|
+
# - All 80+ UniFi tools (clients, devices, networks, etc.)
|
|
1024
1107
|
```
|
|
1025
1108
|
|
|
1026
1109
|
**4. Test with Python examples:**
|
|
@@ -29,7 +29,7 @@ A self-hosted [Model Context Protocol](https://github.com/modelcontextprotocol)
|
|
|
29
29
|
* [Overview](#overview)
|
|
30
30
|
* [Context Optimization](#context-optimization)
|
|
31
31
|
* [Tool Index](#tool-index)
|
|
32
|
-
* [
|
|
32
|
+
* [Tool Execution](#tool-execution)
|
|
33
33
|
* [Runtime Configuration](#runtime-configuration)
|
|
34
34
|
* [Diagnostics (Advanced Logging)](#diagnostics-advanced-logging)
|
|
35
35
|
* [Developer Console (Local Tool Tester)](#developer-console-local-tool-tester)
|
|
@@ -46,6 +46,7 @@ A self-hosted [Model Context Protocol](https://github.com/modelcontextprotocol)
|
|
|
46
46
|
|
|
47
47
|
* Full catalog of UniFi controller operations – firewall, traffic-routes, port-forwards, QoS, VPN, WLANs, stats, devices, clients **and more**.
|
|
48
48
|
* All mutating tools require `confirm=true` so nothing can change your network by accident.
|
|
49
|
+
* **Workflow automation friendly** – set `UNIFI_AUTO_CONFIRM=true` to skip confirmation prompts (ideal for n8n, Make, Zapier).
|
|
49
50
|
* Works over **stdio** (FastMCP). Optional SSE HTTP endpoint can be enabled via config.
|
|
50
51
|
* **Code execution mode** with tool index, async operations, and TypeScript examples.
|
|
51
52
|
* One-liner launch via the console-script **`unifi-network-mcp`**.
|
|
@@ -251,73 +252,73 @@ The server exposes a special `unifi_tool_index` tool that returns a complete lis
|
|
|
251
252
|
- Dynamic client configuration
|
|
252
253
|
- IDE autocomplete support
|
|
253
254
|
|
|
254
|
-
###
|
|
255
|
+
### Tool Execution
|
|
255
256
|
|
|
256
|
-
|
|
257
|
+
The server provides two execution modes for discovered tools:
|
|
257
258
|
|
|
258
|
-
**
|
|
259
|
+
**Single Tool Execution (synchronous):**
|
|
259
260
|
```json
|
|
260
261
|
{
|
|
261
|
-
"name": "
|
|
262
|
+
"name": "unifi_execute",
|
|
262
263
|
"arguments": {
|
|
263
|
-
"tool": "
|
|
264
|
-
"arguments": {
|
|
265
|
-
"mac_address": "aa:bb:cc:dd:ee:ff",
|
|
266
|
-
"confirm": true
|
|
267
|
-
}
|
|
264
|
+
"tool": "unifi_list_clients",
|
|
265
|
+
"arguments": {}
|
|
268
266
|
}
|
|
269
267
|
}
|
|
270
268
|
```
|
|
271
269
|
|
|
272
|
-
**
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"jobId": "af33b233cbdc860c"
|
|
276
|
-
}
|
|
277
|
-
```
|
|
270
|
+
**Batch Execution (parallel, async):**
|
|
271
|
+
|
|
272
|
+
For bulk operations or long-running tasks, use batch mode:
|
|
278
273
|
|
|
279
|
-
**Check job status:**
|
|
280
274
|
```json
|
|
281
275
|
{
|
|
282
|
-
"name": "
|
|
276
|
+
"name": "unifi_batch",
|
|
283
277
|
"arguments": {
|
|
284
|
-
"
|
|
278
|
+
"operations": [
|
|
279
|
+
{"tool": "unifi_get_client_details", "arguments": {"mac": "aa:bb:cc:dd:ee:ff"}},
|
|
280
|
+
{"tool": "unifi_get_client_details", "arguments": {"mac": "11:22:33:44:55:66"}}
|
|
281
|
+
]
|
|
285
282
|
}
|
|
286
283
|
}
|
|
287
284
|
```
|
|
288
285
|
|
|
289
|
-
**Response
|
|
286
|
+
**Response:**
|
|
290
287
|
```json
|
|
291
288
|
{
|
|
292
|
-
"
|
|
293
|
-
|
|
289
|
+
"jobs": [
|
|
290
|
+
{"index": 0, "tool": "unifi_get_client_details", "jobId": "af33b233cbdc860c"},
|
|
291
|
+
{"index": 1, "tool": "unifi_get_client_details", "jobId": "bf44c344dcde971d"}
|
|
292
|
+
],
|
|
293
|
+
"message": "Started 2 operation(s). Use unifi_batch_status to check progress."
|
|
294
294
|
}
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
-
**
|
|
297
|
+
**Check batch status:**
|
|
298
298
|
```json
|
|
299
299
|
{
|
|
300
|
-
"
|
|
301
|
-
"
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
"name": "unifi_batch_status",
|
|
301
|
+
"arguments": {
|
|
302
|
+
"jobIds": ["af33b233cbdc860c", "bf44c344dcde971d"]
|
|
303
|
+
}
|
|
304
304
|
}
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
-
**Response
|
|
307
|
+
**Response:**
|
|
308
308
|
```json
|
|
309
309
|
{
|
|
310
|
-
"
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
310
|
+
"jobs": [
|
|
311
|
+
{"jobId": "af33b233cbdc860c", "status": "done", "result": {...}},
|
|
312
|
+
{"jobId": "bf44c344dcde971d", "status": "done", "result": {...}}
|
|
313
|
+
]
|
|
314
314
|
}
|
|
315
315
|
```
|
|
316
316
|
|
|
317
317
|
**Notes:**
|
|
318
|
+
- Use `unifi_execute` for single operations (returns result directly)
|
|
319
|
+
- Use `unifi_batch` + `unifi_batch_status` for parallel/bulk operations
|
|
318
320
|
- Jobs are stored in-memory only (no persistence)
|
|
319
321
|
- Job IDs are unique per server session
|
|
320
|
-
- Failed jobs capture exception details in the `error` field
|
|
321
322
|
|
|
322
323
|
### Using with Claude Desktop
|
|
323
324
|
|
|
@@ -344,22 +345,32 @@ Practical examples showing programmatic usage:
|
|
|
344
345
|
```python
|
|
345
346
|
from mcp import ClientSession, stdio_client
|
|
346
347
|
|
|
347
|
-
#
|
|
348
|
-
|
|
348
|
+
# Discover tools
|
|
349
|
+
tools = await session.call_tool("unifi_tool_index", {})
|
|
350
|
+
|
|
351
|
+
# Execute a single tool (returns result directly)
|
|
352
|
+
result = await session.call_tool("unifi_execute", {
|
|
353
|
+
"tool": "unifi_list_clients",
|
|
354
|
+
"arguments": {}
|
|
355
|
+
})
|
|
349
356
|
|
|
350
|
-
#
|
|
351
|
-
|
|
352
|
-
"
|
|
353
|
-
|
|
357
|
+
# Batch execution for parallel operations
|
|
358
|
+
batch = await session.call_tool("unifi_batch", {
|
|
359
|
+
"operations": [
|
|
360
|
+
{"tool": "unifi_get_client_details", "arguments": {"mac": "..."}},
|
|
361
|
+
{"tool": "unifi_get_device_details", "arguments": {"mac": "..."}}
|
|
362
|
+
]
|
|
354
363
|
})
|
|
355
364
|
|
|
356
|
-
# Check
|
|
357
|
-
status = await session.call_tool("
|
|
365
|
+
# Check batch status
|
|
366
|
+
status = await session.call_tool("unifi_batch_status", {
|
|
367
|
+
"jobIds": [j["jobId"] for j in batch["jobs"]]
|
|
368
|
+
})
|
|
358
369
|
```
|
|
359
370
|
|
|
360
371
|
**Three complete examples:**
|
|
361
372
|
- `query_tool_index.py` - Discover available tools
|
|
362
|
-
- `use_async_jobs.py` -
|
|
373
|
+
- `use_async_jobs.py` - Batch operations and status checking
|
|
363
374
|
- `programmatic_client.py` - Build custom Python clients
|
|
364
375
|
|
|
365
376
|
See [`examples/python/README.md`](examples/python/README.md) for complete examples.
|
|
@@ -376,15 +387,18 @@ The server advertises its capabilities via an MCP identity file at [`.well-known
|
|
|
376
387
|
"capabilities": {
|
|
377
388
|
"tools": true,
|
|
378
389
|
"tool_index": true,
|
|
379
|
-
"
|
|
390
|
+
"batch_operations": true
|
|
380
391
|
},
|
|
381
392
|
"features": {
|
|
382
393
|
"tool_index": {
|
|
383
394
|
"tool": "unifi_tool_index"
|
|
384
395
|
},
|
|
385
|
-
"
|
|
386
|
-
"
|
|
387
|
-
|
|
396
|
+
"execution": {
|
|
397
|
+
"tool": "unifi_execute"
|
|
398
|
+
},
|
|
399
|
+
"batch_operations": {
|
|
400
|
+
"start_tool": "unifi_batch",
|
|
401
|
+
"status_tool": "unifi_batch_status"
|
|
388
402
|
}
|
|
389
403
|
}
|
|
390
404
|
}
|
|
@@ -504,6 +518,44 @@ The server merges settings from **environment variables**, an optional `.env` fi
|
|
|
504
518
|
| `UNIFI_VERIFY_SSL` | Set to `false` if using self-signed certs |
|
|
505
519
|
| `UNIFI_CONTROLLER_TYPE` | Controller API path type: `auto` (detect), `proxy` (UniFi OS), `direct` (standalone). Default `auto` |
|
|
506
520
|
| `UNIFI_MCP_HTTP_ENABLED` | Set `true` to enable optional HTTP SSE server (default `false`) |
|
|
521
|
+
| `UNIFI_AUTO_CONFIRM` | Set `true` to auto-confirm all mutating operations (skips preview step). Ideal for workflow automation (n8n, Make, Zapier). Default `false` |
|
|
522
|
+
| `UNIFI_TOOL_REGISTRATION_MODE` | Tool loading mode: `lazy` (default), `eager`, or `meta_only`. See [Context Optimization](#context-optimization) |
|
|
523
|
+
| `UNIFI_ENABLED_CATEGORIES` | Comma-separated list of tool categories to load (eager mode). See table below |
|
|
524
|
+
| `UNIFI_ENABLED_TOOLS` | Comma-separated list of specific tool names to register (eager mode) |
|
|
525
|
+
|
|
526
|
+
### Tool Categories (for UNIFI_ENABLED_CATEGORIES)
|
|
527
|
+
|
|
528
|
+
When using eager mode with category filtering, these are the valid category names:
|
|
529
|
+
|
|
530
|
+
| Category | Description | Example Tools |
|
|
531
|
+
|----------|-------------|---------------|
|
|
532
|
+
| `clients` | Client listing, blocking, guest auth | `unifi_list_clients`, `unifi_block_client` |
|
|
533
|
+
| `config` | Configuration management | - |
|
|
534
|
+
| `devices` | Device listing, reboot, locate, upgrade | `unifi_list_devices`, `unifi_reboot_device` |
|
|
535
|
+
| `events` | Events and alarms | `unifi_list_events`, `unifi_list_alarms` |
|
|
536
|
+
| `firewall` | Firewall rules and groups | `unifi_list_firewall_rules`, `unifi_create_firewall_rule` |
|
|
537
|
+
| `hotspot` | Vouchers for guest network | `unifi_list_vouchers`, `unifi_create_voucher` |
|
|
538
|
+
| `network` | Network/VLAN management | `unifi_list_networks`, `unifi_create_network` |
|
|
539
|
+
| `port_forwards` | Port forwarding rules | `unifi_list_port_forwards` |
|
|
540
|
+
| `qos` | QoS/traffic shaping rules | `unifi_list_qos_rules`, `unifi_create_qos_rule` |
|
|
541
|
+
| `routing` | Static routes (V1 API) | `unifi_list_routes`, `unifi_create_route` |
|
|
542
|
+
| `stats` | Statistics and metrics | `unifi_get_client_stats`, `unifi_get_device_stats` |
|
|
543
|
+
| `system` | System info, health, settings | `unifi_get_system_info`, `unifi_get_network_health` |
|
|
544
|
+
| `traffic_routes` | Policy-based routing (V2 API) | `unifi_list_traffic_routes` |
|
|
545
|
+
| `usergroups` | Bandwidth profiles/user groups | `unifi_list_usergroups`, `unifi_create_usergroup` |
|
|
546
|
+
| `vpn` | VPN servers and clients | `unifi_list_vpn_servers`, `unifi_list_vpn_clients` |
|
|
547
|
+
|
|
548
|
+
**Example usage:**
|
|
549
|
+
```bash
|
|
550
|
+
# Load only client and system tools
|
|
551
|
+
export UNIFI_TOOL_REGISTRATION_MODE=eager
|
|
552
|
+
export UNIFI_ENABLED_CATEGORIES=clients,system
|
|
553
|
+
|
|
554
|
+
# Or load specific tools only
|
|
555
|
+
export UNIFI_ENABLED_TOOLS=unifi_list_clients,unifi_list_devices,unifi_get_system_info
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
**Note:** Tools may also be filtered by the `permissions` section in config.yaml (e.g., `clients.update: false` blocks mutating client tools).
|
|
507
559
|
|
|
508
560
|
### Controller Type Detection
|
|
509
561
|
|
|
@@ -826,6 +878,37 @@ See [docs/permissions.md](docs/permissions.md) for complete documentation includ
|
|
|
826
878
|
* `unifi_force_reconnect_client`
|
|
827
879
|
* `unifi_authorize_guest`
|
|
828
880
|
* `unifi_unauthorize_guest`
|
|
881
|
+
* `unifi_set_client_ip_settings`
|
|
882
|
+
|
|
883
|
+
### Events & Alarms
|
|
884
|
+
|
|
885
|
+
* `unifi_list_events`
|
|
886
|
+
* `unifi_list_alarms`
|
|
887
|
+
* `unifi_archive_alarm`
|
|
888
|
+
* `unifi_archive_all_alarms`
|
|
889
|
+
* `unifi_get_event_types`
|
|
890
|
+
|
|
891
|
+
### Routing (Static Routes)
|
|
892
|
+
|
|
893
|
+
* `unifi_list_routes`
|
|
894
|
+
* `unifi_get_route_details`
|
|
895
|
+
* `unifi_create_route`
|
|
896
|
+
* `unifi_update_route`
|
|
897
|
+
* `unifi_list_active_routes`
|
|
898
|
+
|
|
899
|
+
### Hotspot (Vouchers)
|
|
900
|
+
|
|
901
|
+
* `unifi_list_vouchers`
|
|
902
|
+
* `unifi_get_voucher_details`
|
|
903
|
+
* `unifi_create_voucher`
|
|
904
|
+
* `unifi_revoke_voucher`
|
|
905
|
+
|
|
906
|
+
### User Groups
|
|
907
|
+
|
|
908
|
+
* `unifi_list_usergroups`
|
|
909
|
+
* `unifi_get_usergroup_details`
|
|
910
|
+
* `unifi_create_usergroup`
|
|
911
|
+
* `unifi_update_usergroup`
|
|
829
912
|
|
|
830
913
|
### Statistics & Alerts
|
|
831
914
|
|
|
@@ -1002,9 +1085,9 @@ uv run python devtools/dev_console.py
|
|
|
1002
1085
|
|
|
1003
1086
|
# You'll see a menu of all tools including:
|
|
1004
1087
|
# - unifi_tool_index (list all tools with schemas)
|
|
1005
|
-
# -
|
|
1006
|
-
# -
|
|
1007
|
-
# - All
|
|
1088
|
+
# - unifi_execute (run any discovered tool)
|
|
1089
|
+
# - unifi_batch / unifi_batch_status (parallel operations)
|
|
1090
|
+
# - All 80+ UniFi tools (clients, devices, networks, etc.)
|
|
1008
1091
|
```
|
|
1009
1092
|
|
|
1010
1093
|
**4. Test with Python examples:**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unifi-network-mcp"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "Unifi Network MCP Server"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -58,3 +58,15 @@ dev-dependencies = [
|
|
|
58
58
|
[project.scripts]
|
|
59
59
|
# CLI entrypoint that launches the UniFi Network MCP server
|
|
60
60
|
unifi-network-mcp = "src.main:main"
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 120
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
66
|
+
select = ["E", "F", "I"]
|
|
67
|
+
ignore = [
|
|
68
|
+
"E501", # Line too long - handled by line-length setting above
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[tool.ruff.lint.isort]
|
|
72
|
+
known-first-party = ["src"]
|
|
@@ -12,13 +12,13 @@ Importing it early guarantees deterministic side‑effects (env + logging) and
|
|
|
12
12
|
exposes a `load_config()` helper that the rest of the codebase can share.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
import importlib.resources
|
|
16
16
|
import logging
|
|
17
17
|
import os
|
|
18
18
|
import sys
|
|
19
|
+
from dataclasses import dataclass
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
from typing import Any
|
|
21
|
-
import importlib.resources
|
|
22
22
|
|
|
23
23
|
from dotenv import load_dotenv
|
|
24
24
|
from omegaconf import OmegaConf
|
|
@@ -118,32 +118,20 @@ def load_config(path_override: str | Path | None = None) -> OmegaConf:
|
|
|
118
118
|
relative_path = Path("config/config.yaml")
|
|
119
119
|
if relative_path.exists() and relative_path.is_file():
|
|
120
120
|
resolved_path = relative_path
|
|
121
|
-
logger.info(
|
|
122
|
-
"Using configuration file from relative path: %s", relative_path
|
|
123
|
-
)
|
|
121
|
+
logger.info("Using configuration file from relative path: %s", relative_path)
|
|
124
122
|
else:
|
|
125
123
|
# 3. Use bundled default config
|
|
126
124
|
try:
|
|
127
125
|
# Use importlib.resources to safely access package data
|
|
128
|
-
config_file_ref = importlib.resources.files("src.config").joinpath(
|
|
129
|
-
"config.yaml"
|
|
130
|
-
)
|
|
126
|
+
config_file_ref = importlib.resources.files("src.config").joinpath("config.yaml")
|
|
131
127
|
if config_file_ref.is_file():
|
|
132
|
-
resolved_path = Path(
|
|
133
|
-
|
|
134
|
-
) # Convert Traversable to Path
|
|
135
|
-
logger.info(
|
|
136
|
-
"Using bundled default configuration: %s", resolved_path
|
|
137
|
-
)
|
|
128
|
+
resolved_path = Path(str(config_file_ref)) # Convert Traversable to Path
|
|
129
|
+
logger.info("Using bundled default configuration: %s", resolved_path)
|
|
138
130
|
else:
|
|
139
|
-
logger.error(
|
|
140
|
-
"Bundled default configuration file could not be accessed (not a file)."
|
|
141
|
-
)
|
|
131
|
+
logger.error("Bundled default configuration file could not be accessed (not a file).")
|
|
142
132
|
raise SystemExit(3) # Exit if bundled config isn't a file
|
|
143
133
|
except (ModuleNotFoundError, FileNotFoundError, Exception) as e:
|
|
144
|
-
logger.error(
|
|
145
|
-
"Could not find or access bundled default configuration: %s", e
|
|
146
|
-
)
|
|
134
|
+
logger.error("Could not find or access bundled default configuration: %s", e)
|
|
147
135
|
raise SystemExit(3) # Exit if bundled config cannot be loaded
|
|
148
136
|
|
|
149
137
|
if resolved_path is None:
|