Initial implementation of UniFi MCP Light server
Some checks failed
Build and Push Docker Image / build (push) Failing after 12s

Implements Hybrid MCP Light pattern with:
- 4 specific tools: list_clients, list_devices, get_system_info, get_network_health
- Meta tools: tool_index, api_call (raw API pass-through)
- API documentation resource at unifi://api-reference
- Starlette wrapper with /health endpoint
- Write protection (UNIFI_ALLOW_WRITES env var)
- UniFi OS auto-detection (proxy vs direct paths)
- Docker multi-stage build
- Gitea CI workflow

Closes #1, #2, #3, #4, #5, #7
This commit is contained in:
Ben
2026-01-02 02:21:10 +00:00
commit cb57b8f537
12 changed files with 1660 additions and 0 deletions

357
api_docs.py Normal file
View File

@@ -0,0 +1,357 @@
"""
UniFi API Documentation Resource
Curated API reference for the UniFi Network Controller.
Exposed as an MCP Resource to help AI agents use the raw API pass-through tool.
"""
API_DOCS = """
# UniFi Network Controller API Reference
This document provides a curated reference for the UniFi Network Controller API.
Use this with the `unifi_api_call` tool to execute raw API requests.
## Important Notes
- All endpoints use the site variable `{site}` which defaults to `default`
- The server automatically handles UniFi OS path prefixing (`/proxy/network`)
- Authentication is handled automatically via session cookies
- Write operations (POST, PUT, DELETE) require `UNIFI_ALLOW_WRITES=true`
## Response Format
All UniFi API responses follow this structure:
```json
{
"meta": {"rc": "ok"},
"data": [...]
}
```
The `unifi_api_call` tool returns only the `data` array.
---
## Clients (Stations)
### GET /api/s/{site}/stat/sta
List all currently connected clients.
**Response fields:**
- `mac`: Client MAC address
- `ip`: IP address
- `hostname`: Client hostname
- `name`: Friendly name (if set)
- `oui`: Manufacturer OUI
- `is_wired`: Boolean, true if wired connection
- `is_guest`: Boolean, true if on guest network
- `rx_bytes`, `tx_bytes`: Traffic counters
- `uptime`: Connection uptime in seconds
- `last_seen`: Unix timestamp of last activity
- `essid`: Connected SSID (wireless only)
- `channel`: WiFi channel (wireless only)
- `signal`: Signal strength in dBm (wireless only)
### GET /api/s/{site}/stat/alluser
List all known clients (including offline/historical).
### GET /api/s/{site}/stat/user/{mac}
Get details for a specific client by MAC address.
### POST /api/s/{site}/cmd/stamgr
Client management commands. Requires `UNIFI_ALLOW_WRITES=true`.
**Block a client:**
```json
{"cmd": "block-sta", "mac": "aa:bb:cc:dd:ee:ff"}
```
**Unblock a client:**
```json
{"cmd": "unblock-sta", "mac": "aa:bb:cc:dd:ee:ff"}
```
**Force reconnect:**
```json
{"cmd": "kick-sta", "mac": "aa:bb:cc:dd:ee:ff"}
```
---
## Devices
### GET /api/s/{site}/stat/device
List all UniFi network devices (APs, switches, gateways).
**Response fields:**
- `mac`: Device MAC address
- `ip`: Management IP address
- `name`: Device name
- `model`: Model identifier (e.g., "U7PG2")
- `model_in_lts`: Long-term support model name
- `type`: Device type (uap, usw, ugw, udm)
- `version`: Firmware version
- `adopted`: Boolean, true if adopted
- `state`: Device state (1=connected, 0=disconnected)
- `uptime`: Uptime in seconds
- `num_sta`: Number of connected clients (APs)
- `port_table`: Port status array (switches)
### GET /api/s/{site}/stat/device/{mac}
Get details for a specific device by MAC address.
### POST /api/s/{site}/cmd/devmgr
Device management commands. Requires `UNIFI_ALLOW_WRITES=true`.
**Restart device:**
```json
{"cmd": "restart", "mac": "aa:bb:cc:dd:ee:ff"}
```
**Adopt device:**
```json
{"cmd": "adopt", "mac": "aa:bb:cc:dd:ee:ff"}
```
**Upgrade firmware:**
```json
{"cmd": "upgrade", "mac": "aa:bb:cc:dd:ee:ff"}
```
**Locate device (flash LED):**
```json
{"cmd": "set-locate", "mac": "aa:bb:cc:dd:ee:ff"}
```
**Stop locating:**
```json
{"cmd": "unset-locate", "mac": "aa:bb:cc:dd:ee:ff"}
```
---
## Networks & VLANs
### GET /api/s/{site}/rest/networkconf
List all network configurations.
**Response fields:**
- `_id`: Network ID
- `name`: Network name
- `purpose`: Network purpose (corporate, guest, vlan-only)
- `vlan`: VLAN ID
- `subnet`: Network subnet (CIDR)
- `dhcpd_enabled`: DHCP server enabled
- `dhcpd_start`, `dhcpd_stop`: DHCP range
- `domain_name`: DHCP domain name
- `igmp_snooping`: IGMP snooping enabled
### GET /api/s/{site}/rest/networkconf/{id}
Get specific network by ID.
### POST /api/s/{site}/rest/networkconf
Create new network. Requires `UNIFI_ALLOW_WRITES=true`.
### PUT /api/s/{site}/rest/networkconf/{id}
Update network. Requires `UNIFI_ALLOW_WRITES=true`.
### DELETE /api/s/{site}/rest/networkconf/{id}
Delete network. Requires `UNIFI_ALLOW_WRITES=true`.
---
## Wireless Networks (WLANs)
### GET /api/s/{site}/rest/wlanconf
List all wireless network configurations.
**Response fields:**
- `_id`: WLAN ID
- `name`: SSID name
- `enabled`: Boolean
- `security`: Security type (wpapsk, open)
- `wpa_mode`: WPA mode (wpa2, wpa3)
- `x_passphrase`: WiFi password
- `hide_ssid`: Hidden network
- `is_guest`: Guest network
- `vlan`: VLAN ID
- `schedule`: Scheduling configuration
### POST /api/s/{site}/rest/wlanconf
Create new WLAN. Requires `UNIFI_ALLOW_WRITES=true`.
### PUT /api/s/{site}/rest/wlanconf/{id}
Update WLAN. Requires `UNIFI_ALLOW_WRITES=true`.
---
## Firewall
### GET /api/s/{site}/rest/firewallrule
List firewall rules (legacy format).
### GET /api/s/{site}/rest/firewallgroup
List firewall groups (IP groups, port groups).
**Response fields (group):**
- `_id`: Group ID
- `name`: Group name
- `group_type`: Type (address-group, port-group)
- `group_members`: Array of IPs or ports
---
## Port Forwarding
### GET /api/s/{site}/rest/portforward
List port forwarding rules.
**Response fields:**
- `_id`: Rule ID
- `name`: Rule name
- `enabled`: Boolean
- `src`: Source restriction (any, limited)
- `dst_port`: Destination port(s)
- `fwd`: Forward to IP address
- `fwd_port`: Forward to port
- `proto`: Protocol (tcp, udp, tcp_udp)
### POST /api/s/{site}/rest/portforward
Create port forward. Requires `UNIFI_ALLOW_WRITES=true`.
---
## Routing
### GET /api/s/{site}/rest/routing
List static routes.
**Response fields:**
- `_id`: Route ID
- `name`: Route name
- `enabled`: Boolean
- `type`: Route type (static, interface)
- `network`: Destination network
- `gateway`: Next hop gateway
- `interface`: Interface name
---
## Statistics & Health
### GET /api/s/{site}/stat/health
Get overall network health status.
**Response fields:**
- `subsystem`: Subsystem name (wan, lan, wlan, vpn)
- `status`: Status (ok, warning, error)
- `num_user`: Connected user count
- `num_guest`: Connected guest count
- `tx_bytes-r`, `rx_bytes-r`: Transfer rates
### GET /api/s/{site}/stat/sysinfo
Get system information.
**Response fields:**
- `version`: Controller version
- `build`: Build number
- `hostname`: Controller hostname
- `name`: Site name
- `timezone`: Timezone
### GET /api/s/{site}/stat/event
List recent events.
**Query parameters:**
- `within`: Time range in hours (default: 24)
- `_limit`: Maximum events to return
**Example:** `/api/s/default/stat/event?within=24&_limit=100`
### GET /api/s/{site}/stat/alarm
List active alarms.
---
## VPN
### GET /api/s/{site}/rest/vpnclient
List VPN client configurations.
### GET /api/s/{site}/rest/vpnserver
List VPN server configurations.
---
## Hotspot & Vouchers
### GET /api/s/{site}/stat/voucher
List hotspot vouchers.
**Response fields:**
- `code`: Voucher code
- `create_time`: Creation timestamp
- `duration`: Duration in minutes
- `quota`: Usage quota (0=unlimited)
- `used`: Times used
- `qos_overwrite`: QoS override enabled
### POST /api/s/{site}/cmd/hotspot
Hotspot commands. Requires `UNIFI_ALLOW_WRITES=true`.
**Create vouchers:**
```json
{
"cmd": "create-voucher",
"n": 5,
"quota": 1,
"expire": 1440,
"note": "Event guests"
}
```
**Revoke voucher:**
```json
{"cmd": "delete-voucher", "_id": "voucher_id_here"}
```
---
## User Groups (Bandwidth Profiles)
### GET /api/s/{site}/rest/usergroup
List user groups / bandwidth profiles.
**Response fields:**
- `_id`: Group ID
- `name`: Group name
- `qos_rate_max_up`: Upload limit (kbps, -1=unlimited)
- `qos_rate_max_down`: Download limit (kbps, -1=unlimited)
---
## DPI (Deep Packet Inspection)
### GET /api/s/{site}/stat/dpi
Get DPI statistics (application usage).
**Response fields:**
- `by_app`: Usage by application
- `by_cat`: Usage by category
---
## Common Query Parameters
Many list endpoints support:
- `_limit`: Maximum results
- `_start`: Offset for pagination
- `_sort`: Sort field (prefix with - for descending)
**Example:** `/api/s/default/stat/sta?_limit=50&_sort=-rx_bytes`
"""
def get_api_docs() -> str:
"""Return the curated UniFi API documentation."""
return API_DOCS