Skip to main content
Glama

py-unifi-mcp

A small Python MCP server for local UniFi Network inspection. Built with the same FastMCP, httpx, and Pydantic structure as py-ynab-mcp.

The initial feature uses a local API key or UniFi OS session authentication and exposes read-only inventory. See the charter and steel thread spec.

For expansion choices, see the preserved feature landscape, official API catalog, and sanitized live capability evidence linked there. Health/device detail and rename/locate are the selected expansion; other groups remain deferred.

Requires Python 3.12+ and a UniFi OS console with local Network access. This initial version uses fixed legacy Network endpoints. Standalone Network controllers, cloud accounts and MFA login are not supported. API-key access to these legacy endpoints depends on the controller version and key permissions; this is not an Integration API implementation.

Install and run

From this repository, using an external uv environment:

uv venv "$WORKSPACE_ROOT/scratch/.venvs/py-unifi-mcp" --python 3.12
uv pip install --python "$WORKSPACE_ROOT/scratch/.venvs/py-unifi-mcp/bin/python" -e . --group dev

Set UNIFI_URL to an HTTPS origin such as https://controller.example (no trailing slash, path, query, or credentials). Supply either UNIFI_API_KEY or both UNIFI_USERNAME and UNIFI_PASSWORD through your secret runner's environment injection. Mixed credentials are rejected. The credential must have access to the desired sites; prefer read-only privileges unless scoped writes are required. Do not put passwords in source files or command arguments. Optional UNIFI_SITE defaults to default. Optional UNIFI_CA_FILE is a PEM CA bundle for a private CA; otherwise the normal system trust configuration is used. TLS verification is mandatory. Redirects and environment HTTP proxies are disabled.

Configure your MCP client to execute the absolute path to $WORKSPACE_ROOT/scratch/.venvs/py-unifi-mcp/bin/py-unifi-mcp with these environment variables. The server uses stdio. API-key mode sends X-API-Key on requests, never logs in, and never falls back to username/password. Session mode keeps cookies only in memory, logs in lazily on the first inventory request, and can reauthenticate once when a read encounters an expired session. Transport failures and login are not retried.

Tools

Tool

Inventory

unifi_list_sites

Accessible sites and descriptions

unifi_list_devices

Device identity, model, version, address and numeric state

unifi_list_clients

Currently connected clients and connection identity

unifi_find_clients

Exact MAC/IP or name/hostname substring across current and retained clients

unifi_get_client

Exact MAC with separate current and last-known connection details

unifi_list_networks

Network name, purpose, VLAN and subnet

unifi_get_health

Subsystem health and available device/client metrics

unifi_get_device

Exact device_id: resources, uplink, firmware/update availability

unifi_rename_device

Preview or execute an exact-device rename with name

unifi_locate_device

Preview or execute LED locate start/stop with enabled

List tools and health accept offset (default 0) and limit (default 50, maximum 100). Device, client and network tools also accept a validated optional site identifier. Results contain items, total, offset, limit, returned, truncated, has_more, and next_offset. truncated means this page omits some items in the snapshot, including earlier pages. has_more indicates a following page. Pagination bounds MCP output; the controller still returns its full inventory snapshot, and ordering may change between requests.

Client search requires a nonblank query; valid MAC/IP queries match that exact address, otherwise matching is a case-insensitive name/hostname substring. All matches are returned, sorted by normalized MAC and paginated. Set include_offline=False to skip retained history. Detail lookup requires mac (colon, hyphen or compact hex form). Both use fixed GET stat/sta; retained history uses stat/alluser, and device names use stat/device. A failed required source fails the tool rather than presenting an incomplete search as complete.

Client current fields come only from the connected snapshot; last_known contains explicit historical metadata. not_connected means absent from the successful current controller snapshot, not proof of sleep, offline state or unreachability. Reads are sequential snapshots, not atomic. Retained history is controller-limited, not a complete lifetime inventory. Last-known IPs are not DHCP reservations or safe automatic SSH targets. Duplicate client MACs fail clearly; ambiguous device names are not guessed. Missing telemetry stays null. DHCP, firewall/port configuration, Wake-on-LAN, Windows and Ollama control remain outside this feature. See client lookup spec.

Only explicitly modeled fields are returned. WLAN passwords, VPN material and arbitrary nested settings are discarded. Network addresses and client names are still private network data visible to the MCP consumer. Treat device names and other controller text as untrusted data. Unknown numeric device state codes are preserved without guessing their meanings. Missing telemetry remains null.

Rename and locate default to dry_run=True: resolve the exact adopted device ID in the selected site and show the target and proposed change without mutating it. Execution requires explicit dry_run=False, API-key authentication, and user intent for that target/change. Session authentication supports reads/previews only. Names must be nonblank, bounded, and free of control characters. No arbitrary device settings, restart, or network changes are exposed.

Writes are never retried or replayed. Rename distinguishes controller acknowledgement from name read-back verification; a failed read-back is unverified, not a reason to repeat the mutation. Locate acknowledgement cannot verify the physical LED. Set enabled=False to stop locating; a start is not automatically stopped. Transport or invalid-response failures have an unknown outcome: inspect before acting again. These legacy writes are based on community source contracts. Live rename read-back and locate start/stop acknowledgement were tested on the development controller; support still depends on controller version, key permissions and device model. See the completed device operations spec.

Malformed responses, denied access, legacy API errors, and connection failures produce sanitized errors. Schema failures do not silently become empty inventory.

Development

"$WORKSPACE_ROOT/scratch/.venvs/py-unifi-mcp/bin/ruff" check .
"$WORKSPACE_ROOT/scratch/.venvs/py-unifi-mcp/bin/ruff" format --check .
"$WORKSPACE_ROOT/scratch/.venvs/py-unifi-mcp/bin/mypy" src/
"$WORKSPACE_ROOT/scratch/.venvs/py-unifi-mcp/bin/pytest"
uv build --wheel

Tests use synthetic HTTP transports, including an MCP stdio subprocess test; they do not contact a controller. Live verification uses an external local runner with in-memory secret injection; controller details and verification evidence remain outside this repository.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/duganth/py-unifi-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server