FastMCP server for local UniFi controller management. Exposes a single unifi action router and a unifi_help reference tool covering devices, clients, network configuration, and controller monitoring.
The server connects to a self-hosted UniFi controller (including UDM Pro) and proxies all operations through a single MCP tool. A unified action parameter replaces 31 individual tools while preserving every capability. Destructive operations require explicit confirmation. Bearer token auth protects the HTTP endpoint in production.
unifi_mcp/: server, client, config, formatters, models, services, resources, and toolsskills/unifi/: client-facing skilldocs/: API notes, action-pattern rationale, testing notes.claude-plugin/,.codex-plugin/,gemini-extension.json: client manifestsdocker-compose.yaml,Dockerfile: container deploymenttests/: unit, resource, and integration tests
Single action router for all UniFi operations.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
action |
string | yes | — | Action to perform (see below) |
site_name |
string | no | "default" |
UniFi site name. Ignored by get_sites, get_controller_status, get_user_info |
mac |
string | no | null |
Device or client MAC address (any format: aa:bb:cc, AA-BB-CC, aabb.cc) |
limit |
int | no | varies | Maximum results to return |
connected_only |
bool | no | true |
get_clients: return only currently connected clients |
active_only |
bool | no | true |
get_alarms: return only unarchived alarms |
by_filter |
string | no | "by_app" |
get_dpi_stats: "by_app" or "by_cat" |
name |
string | no | null |
set_client_name: new display name (empty string removes it) |
note |
string | no | null |
set_client_note: note text (empty string removes it) |
minutes |
int | no | 480 |
authorize_guest: access duration in minutes |
up_bandwidth |
int | no | null |
authorize_guest: upload limit in Kbps |
down_bandwidth |
int | no | null |
authorize_guest: download limit in Kbps |
quota |
int | no | null |
authorize_guest: data quota in MB |
confirm |
bool | no | null |
Set to true to confirm destructive operations |
Returns full markdown reference for all actions and parameters. No parameters needed.
| Action | MAC Required | Description |
|---|---|---|
get_devices |
no | List all devices on a site with status, model, IP, and uptime |
get_device_by_mac |
yes | Get full details for one device by MAC |
restart_device |
yes | Destructive — reboot the device |
locate_device |
yes | Activate the locate LED on the device |
get_devices response fields per device: name, model, type (Access Point / Gateway / Switch), status (Online / Offline), ip, mac, uptime, version
restart_device example:
unifi action=restart_device mac=aa:bb:cc:dd:ee:ff confirm=true
| Action | MAC Required | Description |
|---|---|---|
get_clients |
no | List clients. connected_only=true (default) filters offline entries |
reconnect_client |
yes | Destructive — force a client to reconnect (kick-sta) |
block_client |
yes | Destructive — block a client from network access |
unblock_client |
yes | Re-allow a previously blocked client |
forget_client |
yes | Destructive — remove all historical data for a client (GDPR) |
set_client_name |
yes | Set or update the display alias for a client |
set_client_note |
yes | Set or update the note for a client |
set_client_name / set_client_note: Both resolve the client by MAC against the controller user list (/list/user), then POST to /upd/user/{id}. Pass an empty string (name="") to remove the value.
Workflow — block a client:
# Step 1: find the MAC
unifi action=get_clients connected_only=true
# Step 2: block it
unifi action=block_client mac=aa:bb:cc:dd:ee:ff confirm=true
# Step 3: verify
unifi action=get_clients connected_only=false
| Action | Description |
|---|---|
get_sites |
List all sites on the controller with health info |
get_wlan_configs |
WLAN profiles: SSID, security, VLAN, guest flag, band steering |
get_network_configs |
Network/VLAN configs: subnet, DHCP range, purpose, guest flag |
get_port_configs |
Switch port profiles: native VLAN, tagged VLANs, PoE mode, port security |
get_port_forwarding_rules |
Port forwarding rules: protocol, external port, internal IP/port |
get_firewall_rules |
Firewall rules: action, protocol, source/destination, ruleset, index |
get_firewall_groups |
Firewall groups: type, member IPs or MACs, member count |
get_static_routes |
Static routes: destination network, gateway (nexthop), distance, interface |
All network configuration actions accept site_name. get_sites does not use site_name.
| Action | Key Parameters | Description |
|---|---|---|
get_controller_status |
— | Controller version and up/down status |
get_events |
limit (default 100), site_name |
Recent controller events sorted newest-first. On UDM Pro tries the v2 API (/proxy/network/v2/api/site/{site}/events) then falls back to /stat/event. Returns an informative error if the firmware has removed the legacy endpoint. |
get_alarms |
active_only (default true), site_name |
Active or all alarms. Severity comes from catname. |
get_dpi_stats |
by_filter (default "by_app"), site_name |
Deep Packet Inspection usage by application or category. Bandwidth values are in bytes (raw) in the structured response; the text summary formats them as human-readable (KB / MB / GB). |
get_rogue_aps |
limit (default 20, max 50), site_name |
Detected foreign APs sorted by signal strength. Threat level: High > -60 dBm, Medium > -80 dBm, Low otherwise. |
start_spectrum_scan |
mac (AP), site_name |
Trigger an RF spectrum scan on an access point. |
get_spectrum_scan_state |
mac (AP), site_name |
Poll scan state and results for an AP. |
authorize_guest |
mac, minutes, up_bandwidth, down_bandwidth, quota, site_name |
Grant a guest client timed network access. up_bandwidth / down_bandwidth are in Kbps. quota is in MB (converted to bytes before sending). Default duration is 480 minutes (8 hours). |
get_speedtest_results |
limit (default 20), site_name |
Historical speed tests from the last 30 days. Download/upload fields are in Mbps; latency and jitter in ms. |
get_ips_events |
limit (default 50), site_name |
IPS/IDS threat events from the last 7 days: source/destination IP, protocol, signature, category, severity, action. |
Workflow — authorize a guest with bandwidth cap:
unifi action=authorize_guest mac=aa:bb:cc:dd:ee:ff minutes=120 down_bandwidth=5000 up_bandwidth=2000 quota=500
Workflow — view DPI stats by category:
unifi action=get_dpi_stats by_filter=by_cat site_name=default
| Action | Description |
|---|---|
get_user_info |
Return the MCP OAuth token claims (email, name, picture, expiry) |
get_user_info requires MCP OAuth (e.g. Google provider). UniFi controller credentials are separate. Returns an error if OAuth is not configured.
Four actions require confirmation before executing:
| Action | What it does |
|---|---|
restart_device |
Reboots the device — causes brief network outage |
block_client |
Denies network access to the client |
reconnect_client |
Forces disconnect/reconnect (kick-sta) |
forget_client |
Permanently removes all historical data for the client |
Confirmation is checked in this order. The first matching rule wins:
UNIFI_MCP_ALLOW_DESTRUCTIVE=true— all destructive actions run without prompting (CI / automation)UNIFI_MCP_ALLOW_YOLO=true— same bypass, broader semantics (skips all elicitation prompts)confirm=trueparameter — per-call confirmation in the tool invocation
If none of the above apply, the tool returns error: confirmation_required with instructions to add confirm=true.
/plugin marketplace add jmagar/claude-homelab
/plugin install unifi-mcp @jmagar-claude-homelabuv sync
uv run python -m unifi_mcp.mainConsole script entrypoints:
uv run unifi-mcp
uv run unifi-local-mcpjust up
just logsCopy .env.example to .env and fill in the values:
cp .env.example .env| Variable | Required | Default | Description |
|---|---|---|---|
UNIFI_URL |
yes | — | Controller URL, e.g. https://192.168.1.1:443. No trailing slash. UNIFI_CONTROLLER_URL accepted as legacy fallback. |
UNIFI_USERNAME |
yes | — | Controller admin username |
UNIFI_PASSWORD |
yes | — | Controller admin password |
UNIFI_IS_UDM_PRO |
no | true |
Set to true for UDM Pro / UniFi OS controllers. Changes the API base path and auth flow (see below). |
UNIFI_VERIFY_SSL |
no | false |
Set to true to verify TLS certificates. Most self-hosted controllers use self-signed certs; leave false unless you have a valid cert. |
UNIFI_MCP_HOST |
no | 0.0.0.0 |
Server bind address |
UNIFI_MCP_PORT |
no | 8001 |
Server port. UNIFI_LOCAL_MCP_PORT is a legacy fallback. |
UNIFI_MCP_TRANSPORT |
no | http |
"http" or "stdio" |
UNIFI_MCP_TOKEN |
no* | — | Bearer token for HTTP auth. Generate with openssl rand -hex 32. *Required unless UNIFI_MCP_NO_AUTH=true. |
UNIFI_MCP_NO_AUTH |
no | false |
Disable Bearer auth (use only behind a trusted reverse proxy) |
UNIFI_MCP_LOG_LEVEL |
no | INFO |
Log verbosity: DEBUG, INFO, WARNING, ERROR |
UNIFI_MCP_LOG_FILE |
no | /tmp/unifi-mcp.log |
Log file path. File is cleared when it reaches 10 MB. |
UNIFI_MCP_ALLOW_DESTRUCTIVE |
no | false |
Skip confirmation for all destructive actions |
UNIFI_MCP_ALLOW_YOLO |
no | false |
Skip all elicitation prompts including destructive confirmation |
PUID / PGID |
no | 1000 / 1000 |
UID/GID for Docker container process |
DOCKER_NETWORK |
no | jakenet |
Docker network name |
UDM Pro (true) |
Traditional (false) |
|
|---|---|---|
| API base path | /proxy/network/api |
/api |
| Login endpoint | /api/auth/login |
{api_base}/login |
| CSRF token | Extracted from JWT TOKEN cookie and sent as X-CSRF-Token |
Not required |
| Events API | Tries v2 (/proxy/network/v2/api/site/{site}/events) first |
Legacy /stat/event only |
Self-hosted controllers typically use self-signed TLS certificates. Setting UNIFI_VERIFY_SSL=false disables certificate validation so the client can connect without a CA bundle. This is the safe choice for internal-network deployments. If your controller has a certificate issued by a public CA (e.g. via Let's Encrypt), set this to true.
Every action that is site-scoped accepts a site_name parameter (default: "default"). Use get_sites to list available site names. Pass the name field (not the description) as site_name.
unifi action=get_sites
# returns: name="default", name="branch-office", ...
unifi action=get_devices site_name=branch-office
unifi action=get_clients connected_only=true
unifi action=get_device_by_mac mac=aa:bb:cc:dd:ee:ff
unifi action=block_client mac=aa:bb:cc:dd:ee:ff confirm=true
unifi action=unblock_client mac=aa:bb:cc:dd:ee:ff
unifi action=set_client_name mac=aa:bb:cc:dd:ee:ff name="Living Room TV"
unifi action=set_client_note mac=aa:bb:cc:dd:ee:ff note="Guest device, 2026-04"
unifi action=authorize_guest mac=aa:bb:cc:dd:ee:ff minutes=120 down_bandwidth=5000 quota=500
unifi action=get_dpi_stats by_filter=by_app site_name=default
unifi action=get_ips_events limit=20 site_name=default
unifi action=get_controller_status
unifi action=get_speedtest_results limit=5
unifi_help
just dev # Start server with auto-reload
just lint # Lint with ruff
just fmt # Format with ruff
just typecheck # Type-check with ty
just check # lint + typecheck
just build # Editable install (uv pip install -e .)
just test # Run unit tests
just test-live # Health check against running server
just up # docker compose up -d
just down # docker compose down
just logs # Tail container logs
just health # curl /health endpoint
just gen-token # Generate a secure random token
just check-contract # Lint skill/server contract
just clean # Remove build artifacts and cachesjust gen-token
# or
openssl rand -hex 32just lint
just typecheck
just testFor a running-server check:
just health
just test-live| Plugin | Category | Description |
|---|---|---|
| homelab-core | core | Core agents, commands, skills, and setup/health workflows for homelab management. |
| overseerr-mcp | media | Search movies and TV shows, submit requests, and monitor failed requests via Overseerr. |
| unraid-mcp | infrastructure | Query, monitor, and manage Unraid servers: Docker, VMs, array, parity, and live telemetry. |
| gotify-mcp | utilities | Send and manage push notifications via a self-hosted Gotify server. |
| swag-mcp | infrastructure | Create, edit, and manage SWAG nginx reverse proxy configurations. |
| synapse-mcp | infrastructure | Docker management (Flux) and SSH remote operations (Scout) across homelab hosts. |
| arcane-mcp | infrastructure | Manage Docker environments, containers, images, volumes, networks, and GitOps via Arcane. |
| syslog-mcp | infrastructure | Receive, index, and search syslog streams from all homelab hosts via SQLite FTS5. |
| plugin-lab | dev-tools | Scaffold, review, align, and deploy homelab MCP plugins with agents and canonical templates. |
MIT