mcp-server-unifi 0.1.0
Read-only MCP server for UniFi network analysis — gives LLMs safe access to controller data for diagnostics, monitoring, and capacity planning
Requires Python: >=3.11
Keywords: analysis, mcp, model-context-protocol, monitoring, network, unifi

# mcp-server-unifi

[![pipeline](https://gitlab.com/gitlab_joel/mcp-server-unifi/badges/main/pipeline.svg)](https://gitlab.com/gitlab_joel/mcp-server-unifi/-/pipelines)
[![coverage](https://gitlab.com/gitlab_joel/mcp-server-unifi/badges/main/coverage.svg)](https://gitlab.com/gitlab_joel/mcp-server-unifi/-/pipelines)
[![PyPI](https://img.shields.io/pypi/v/mcp-server-unifi)](https://pypi.org/project/mcp-server-unifi/)
[![Python](https://img.shields.io/pypi/pyversions/mcp-server-unifi)](https://pypi.org/project/mcp-server-unifi/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

Read-only MCP server for UniFi network analysis. Gives Claude (and other MCP
clients) safe access to controller data for diagnostics, monitoring, and
capacity planning.

Python 3.11+ | 19 tools | 4 resources | 6 prompts

## Architecture

```
                  +-----------------+
                  |  Claude / MCP   |
                  |     Client      |
                  +--------+--------+
                           | stdio (JSON-RPC)
                  +--------+--------+
                  |   mcp-server-unifi     |
                  |   MCP Server    |
                  |                 |
                  | - Tools (19)    |
                  | - Resources (4) |
                  | - Prompts (6)   |
                  +--------+--------+
                           |
                  +--------+--------+
                  | Session Pool    |
                  | Circuit Breaker |
                  +--------+--------+
                           | httpx (HTTPS)
                  +--------+--------+
                  | UniFi Controller|
                  | (Network API)   |
                  +-----------------+
```

Mermaid source: [docs/architecture.mmd](docs/architecture.mmd)

## Install

### uvx (recommended)

No install needed — runs directly from PyPI:

```json
{
  "mcpServers": {
    "unifi": {
      "command": "uvx",
      "args": ["mcp-server-unifi"],
      "env": {
        "UNIFI_HOST": "https://192.168.1.1",
        "UNIFI_USER": "mcp-user",
        "UNIFI_PASS": "your-password-here",
        "UNIFI_SITE": "default",
        "UNIFI_VERIFY_SSL": "false"
      }
    }
  }
}
```

Add this to `.mcp.json` (Claude Code) or your client's MCP config. Restart the client.

### pip

```bash
pip install mcp-server-unifi
mcp-server-unifi                              # start server on stdio
```

### From source

```bash
git clone https://gitlab.com/gitlab_joel/mcp-server-unifi.git
cd mcp-server-unifi
make venv
cp .env.example .env   # fill in your credentials
make run
```

### MCP Inspector

```bash
npx @modelcontextprotocol/inspector           # interactive testing
```

## Configuration

All config is via environment variables — set in `.env` or in the `env` block of `.mcp.json`.

| Variable | Default | Description |
|----------|---------|-------------|
| `UNIFI_HOST` | `https://localhost:8443` | Controller URL |
| `UNIFI_USER` | `admin` | API username |
| `UNIFI_PASS` | *(required)* | API password |
| `UNIFI_SITE` | `default` | UniFi site name |
| `UNIFI_VERIFY_SSL` | `false` | Verify TLS certificate |
| `UNIFI_SESSION_TTL` | `1800` | Session lifetime (seconds) before re-auth |
| `UNIFI_MIN_LOGIN_INTERVAL` | `10` | Minimum seconds between login attempts |
| `UNIFI_POLL_INTERVAL` | `60` | Resource polling interval (seconds, min 15) |

Recommend creating a dedicated read-only local user on the UniFi controller for the MCP server rather than using an admin account.

## Tools

### Devices & Topology

| Tool | Description | Required Args |
|------|-------------|---------------|
| `unifi_list_devices` | List all APs, switches, gateways with status | -- |
| `unifi_ap_details` | Detailed AP info (radios, VAPs, load) | `mac` |
| `unifi_switch_details` | Switch info + per-port status | `mac` |
| `unifi_switch_port_stats` | Per-port error/throughput stats | `mac` (opt: `port_idx`) |
| `unifi_topology` | Device uplink chain for backbone paths | -- |
| `unifi_device_stats_history` | Historical throughput, client count, error stats | `mac` (opt: `hours`, `granularity`) |

### Clients

| Tool | Description | Required Args |
|------|-------------|---------------|
| `unifi_list_clients` | Connected clients | (opt: `ap_mac`, `limit`, `offset`) |
| `unifi_client_details` | Client info by MAC | `mac` |
| `unifi_search_client` | Search by hostname pattern | `hostname` (opt: `limit`) |

### Wireless / RF

| Tool | Description | Required Args |
|------|-------------|---------------|
| `unifi_channel_utilization` | Channel stats across all APs | -- |
| `unifi_wlan_config` | SSID config (DTIM, roaming, band steering) | (opt: `ssid`) |
| `unifi_ap_radio_config` | Radio config (channel, power, RSSI) | `mac` |
| `unifi_rogue_aps` | Neighboring/rogue APs for interference analysis | (opt: `ap_mac`, `limit`) |
| `unifi_radio_ai_settings` | Radio AI auto-channel/power settings | -- |

### Events & Health

| Tool | Description | Required Args |
|------|-------------|---------------|
| `unifi_events` | Recent network events | (opt: `limit`, `client_mac`, `event_type`) |
| `unifi_alarms` | Active alarms | -- |
| `unifi_site_health` | Site health metrics for all subsystems | -- |
| `unifi_system_logs` | System log entries (richer than events) | (opt: `limit`, `category`, `severity`) |
| `unifi_recommend_roaming` | Best AP for roaming via AI sampling | `mac` |

All tools return JSON. Output exceeding ~25k tokens is automatically truncated with pagination metadata.

## Resources

| URI | Description | Update |
|-----|-------------|--------|
| `unifi://devices` | All network devices with status | Polled, SHA-256 change detect |
| `unifi://clients/active` | Connected wireless + wired clients | Polled, SHA-256 change detect |
| `unifi://health` | Site health summary | Polled, SHA-256 change detect |
| `unifi://events/infrastructure` | Non-roam events (device offline, switch connect, channel change) | Polled, SHA-256 change detect |

Clients receive `notifications/resources/updated` when data changes. Polling interval controlled by `UNIFI_POLL_INTERVAL`.

## Prompts

### Diagnostics

**`troubleshoot_wifi`** — Diagnose WiFi issues for a specific client.
- Args: `client_identifier` (required), `timeframe` (optional, default "last 24 hours")
- Searches for client, uses elicitation if multiple match, gathers diagnostics (details, events, channel utilization), returns timeline + root cause + recommendations

**`incident_investigation`** — Correlate events with device state for incident analysis.
- Args: `timeframe` (optional), `device_mac` (optional)
- Gathers system logs, events, topology, and switch port stats. Builds chronological timeline, traces blast radius through uplink chain, identifies root cause

### Assessments

**`deployment_assessment`** — Full network health assessment with structured findings.
- Args: `focus` (optional: "switches", "wireless", "events", or "all")
- Scans all devices, switch ports, wireless, topology, and infrastructure events. Produces a structured report with comparison anchors for delta tracking

**`wifi_audit`** — Full wireless environment audit.
- Args: none
- Scans all APs, channel utilization, rogues, client distribution, WLAN config. Returns structured findings with recommendations

### Capacity Planning

**`capacity_report`** — AP load and throughput trending analysis.
- Args: `hours` (optional, default 24)
- Per-AP client distribution by band, hourly throughput history, channel utilization, band steering effectiveness, capacity headroom estimation

**`switch_health`** — Deep dive on switch port errors and drop rates.
- Args: `switch_mac` (optional — analyzes all switches if omitted)
- Per-switch port error table, drop rate computation, STP state audit, uplink chain health

## Resilience

- **Circuit breaker** — Trips after 3 consecutive auth failures. Exponential backoff (30s → 60s → 120s → 300s max). Prevents hammering a down controller.
- **Session pool** — Single shared httpx session with configurable TTL (default 30 min). Auto-refreshes on expiry or 401. Avoids repeated logins.
- **Login throttle** — Minimum 10s between login attempts (configurable). Stays under the UniFi OS ~5 attempts/window rate limit.
- **Output truncation** — Binary-search truncation at ~80k chars (~20k tokens). Adds `_truncated` metadata with item count and guidance.

## Development

```bash
git clone <repo-url> && cd mcp-server-unifi
make venv          # create venv + install dev deps
make test          # run test suite (424 tests, 89% coverage)
make lint          # ruff linter
make format        # ruff auto-format
```

### Make Targets

| Target | Description |
|--------|-------------|
| `make test` | Run test suite with coverage |
| `make coverage` | Tests + HTML coverage report |
| `make lint` | Run ruff linter |
| `make format` | Auto-format with ruff |
| `make run` | Start MCP server |
| `make clean` | Remove build artifacts |
| `make docker-build` | Build Docker image |
| `make docker-run` | Run in Docker (uses `.env`) |

### Testing

424 tests across unit, protocol, and behavioral categories:

- **Unit** — tools, resources, prompts, circuit breaker, session pool, output truncation, progress, logging
- **Protocol** — MCP JSON-RPC validation through real client/server stack (initialization, capabilities, tool invocation, error handling)
- **Behavioral** — Tool naming conventions, JSON Schema correctness, keyword-overlap tool selection scoring, response format validation

Config: `asyncio_mode = "auto"` in pyproject.toml. Do **not** use `pytestmark = pytest.mark.anyio` — it conflicts with pytest-asyncio.

## Project Structure

```
src/mcp_server_unifi/
    server.py           MCP server, tool dispatch, create_server()
    client.py           UniFi API client (httpx, auth, retry)
    tools.py            Tool implementations (formatting, logic)
    resources.py        Resource handlers + ResourcePoller
    prompts.py          Prompt templates (troubleshoot, audit)
    session_pool.py     Shared session with TTL + login throttle
    circuit_breaker.py  CircuitBreaker (closed/open/half-open)
    output.py           Output truncation (binary search)
    progress.py         Progress notification helper
    logging_handler.py  MCP logging channel

tests/
    conftest.py            Fixtures, mock UniFi client, MCP session
    test_tools.py          Tool logic
    test_resources.py      Resource handlers
    test_prompts.py        Prompt workflows
    test_mcp_protocol.py   In-process protocol validation
    test_behavioral.py     Naming, schema, selection scoring
    test_circuit_breaker.py
    test_session_pool.py
    test_client.py
    test_output.py
    test_progress.py
    test_logging_handler.py
    test_advanced.py
```

## Known Gotchas

- **Claude Code does not render progress or logging notifications** — The server emits them correctly, but Claude Code's UI does not display them yet. Tracked: [claude-code#4157](https://github.com/anthropics/claude-code/issues/4157) (progress), [claude-code#3174](https://github.com/anthropics/claude-code/issues/3174) (logging). MCP Inspector displays both.
- **UniFi OS login rate limiting** — `/api/auth/login` allows ~5 attempts per window with a 5-10 min cooldown (HTTP 429). Cumulative across all clients. The session pool and login throttle mitigate this.
- **No switch historical stats** — The UniFi API has no `stat/report` endpoint for switches. Use `unifi_switch_port_stats` for real-time counters.

---

See [IMPROVEMENTS.md](IMPROVEMENTS.md) for development history.