MCP Galaxy / Directory / UniFi Read-Only
Software Defined Network Read-Only Docker / Podman

UniFi Read-Only

A production-grade, read-only Model Context Protocol server for a single Ubiquiti UniFi OS console. Wraps both the modern Network Integration API and the broader Classic Controller API on the same console , pinned to one site, scoped to GET only.

Runtime
Docker / Podman
Transport
stdio
API
Integration + Classic
Overview

What This Server Does

UniFi Read-Only exposes a curated set of GET-only tools that wrap two API surfaces on the same console:

  • Network Integration API (official, X-API-KEY), modern UUID-keyed endpoints. Tools prefixed unifi_int_*.
  • Classic / Internal Controller API (cookie session), broader legacy coverage for alarms, events, rogue APs, DPI stats, daily reports, and more. Tools prefixed unifi_classic_*.

The Site Manager (cloud) API is intentionally not wrapped, this server talks only to a console you can reach on the LAN.

The instance is pinned to a single site at startup: UNIFI_SITE_ID (Integration UUID) and UNIFI_SITE_NAME (Classic short name). Site-scoped tools take no site parameter at all, the model cannot enumerate or query an unrelated site the admin can also see.

Ships as both a Docker and a Podman container from the same server.py, pick whichever runtime you already run.

Try Asking

What to Ask, in Plain English

A handful of real questions you can put to your AI client once this server is wired in. The model figures out which tools to call, you describe the outcome.

  • "List rogue APs detected in the last 24 hours and where they were seen."
  • "Which clients are connected to the IoT VLAN right now?"
  • "Show me the firewall policies between the LAN and Guest zones, in evaluation order."
  • "Which clients failed to connect to the WiFi today, and why?"
  • "What SSIDs are configured, and which networks do they sit on?"
  • "Give me the per-client DPI breakdown for the busiest user over the last 24 hours."
Capabilities

78 Read-Only Tools Across Both API Surfaces

A
System & Sites
Application version, controller system info, per-subsystem health, site list (diagnostic), admin accounts.
B
Devices
Adopted & pending devices, latest device statistics, per-device lookup by UUID or MAC, across both API surfaces.
C
Clients & DPI
Currently connected clients (filterable), historic clients (past N hours), per-client lookup, DPI breakdowns.
D
WiFi (SSIDs & WLANs)
Integration WiFi broadcasts, Classic WLAN and WLAN-group configurations.
E
Networks / VLANs
VLAN configuration across both APIs, plus Integration network reference lookup (what objects reference a given network).
F
Firewall, ACL, DNS & VPN
Integration zones / policies / policy ordering, ACL rules & ordering, DNS policies, traffic matching lists, site-to-site VPN tunnels, VPN servers; Classic firewall rules & groups, IDS/IPS settings, port forwards.
G
Switching & Routing
Switch stacks, LAGs (LOCAL / SWITCH_STACK / MULTI_CHASSIS), MC-LAG domains, switch port profiles, WAN interfaces, static routes, traffic routes, RADIUS profiles, device tags, dynamic DNS.
H
Events, Alarms & Reports
Events log, alarms, rogue AP detections, hotspot vouchers, daily site reports, DPI category / application catalog, ISO country list.
Installation

Build & Configure

Two supported runtimes, pick the one you already run. The docker/ and podman/ backends ship an identical server.py; only the container file and the way the client is wired differ. Both keep your UniFi credentials out of the client's config file.

New to the Docker MCP Gateway? The Docker pane below uses a catalog → registry → gateway flow with three bind-mounts. If any of that terminology is unfamiliar, skim Docker MCP Gateway under Good to Know first, it walks through the architecture these steps build on. The Podman pane runs the container directly and doesn't depend on the gateway.

Step 0, Create a local read-only admin and generate an API key. On the console, go to Settings → Control Plane → Admins & Users → Add Admin. Restrict to Local Access Only, role Limited Admin → Read-Only. Do not use your UI.com cloud account, UI.com enforces MFA, which breaks non-interactive logins with HTTP 499. Sign in as that local admin, go to Settings → Control Plane → Integrations → Create API Key, and copy the key (shown only once). If you also want the Classic-API tools, keep the same admin's username and password handy.

Docker Docker Desktop · MCP Gateway
Step 1, Get the project files

Clone the repository. The Docker backend lives in docker/ and ships a custom-catalog.yaml alongside the Dockerfile.

Clone
$ git clone https://github.com/rosarion97/unifi-readonly-mcp-server-public.git
$ cd unifi-readonly-mcp-server-public/docker
Step 2, Build the image

The image tag must match the image: field in custom-catalog.yaml (unifi-readonly-mcp:latest).

Build
$ docker build -t unifi-readonly-mcp:latest .
Step 3, Provide credentials

Three values are required: UNIFI_HOST, UNIFI_API_KEY, and UNIFI_SITE_ID. Optional: UNIFI_PORT (default 443), UNIFI_SITE_NAME (default default), UNIFI_VERIFY_TLS (default false), and the Classic-API credentials.

Find your UNIFI_SITE_ID by calling unifi_int_list_sites once with a placeholder, then copy the UUID.

Option A, Docker secret store (recommended)
$ docker mcp secret set UNIFI_API_KEY
$ docker mcp secret set UNIFI_SITE_ID
# Optional, only if you want the unifi_classic_* tools:
$ docker mcp secret set UNIFI_CLASSIC_USERNAME
$ docker mcp secret set UNIFI_CLASSIC_PASSWORD
$ docker mcp secret ls   # values are masked
Non-secret values go in the catalog entry, not the secret store. UNIFI_HOST, UNIFI_PORT, UNIFI_SITE_NAME, and UNIFI_VERIFY_TLS are runtime config, set them on the unifi-readonly entry's env block in the custom-catalog.yaml you copy in Step 4. The shipped catalog already declares all secret bindings; you just fill in the runtime fields.

With Option A, continue through Steps 4–7 below.

Option B writes your API key to disk in clear text. Use it only for local testing, chmod 600 .env, never commit it. This path skips the gateway: skip Steps 4–6, point your client at docker run --env-file (below), then jump to Step 7.
Option B, plaintext .env (testing only)
$ cp .env.example .env
$ chmod 600 .env   # UNIFI_HOST, UNIFI_API_KEY, UNIFI_SITE_ID required; Classic creds optional
claude_desktop_config.json (Option B)
{
  "mcpServers": {
    "unifi": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "--env-file", "/absolute/path/to/docker/.env",
        "unifi-readonly-mcp:latest"
      ]
    }
  }
}
Step 4, Install the custom catalog

Steps 4–6 are the Option A (secret store) path. Used Option B? Skip to Step 7. After copying the catalog, open it and fill in the UNIFI_HOST, UNIFI_PORT, UNIFI_SITE_NAME, and UNIFI_VERIFY_TLS values on the unifi-readonly entry.

Catalog
$ mkdir -p ~/.docker/mcp/catalogs
$ cp custom-catalog.yaml ~/.docker/mcp/catalogs/custom.yaml
Step 5, Enable the server in the registry

Add the entry under the single top-level registry: key in ~/.docker/mcp/registry.yaml. Don't overwrite the file if it already exists.

~/.docker/mcp/registry.yaml
registry:
  unifi-readonly:
    catalog: custom
    enabled: true
Step 6, Point Claude Desktop at the gateway

Add the gateway block to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json). The gateway spawns the UniFi image on demand, reads your catalog and registry, and resolves your UNIFI_* secrets at request time. Replace <your-username> with your macOS username (run whoami to check):

claude_desktop_config.json, gateway block
{
  "mcpServers": {
    "mcp-toolkit-gateway": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-v", "/var/run/docker.sock:/var/run/docker.sock",
        "-v", "/Users/<your-username>/.docker/mcp:/mcp",
        "-v", "/Users/<your-username>/Library/Caches/docker-secrets-engine/engine.sock:/root/.cache/docker-secrets-engine/engine.sock",
        "docker/mcp-gateway:latest",
        "--catalog=/mcp/catalogs/custom.yaml",
        "--registry=/mcp/registry.yaml",
        "--transport=stdio"
      ]
    }
  }
}

All three bind-mounts are required: the Docker socket lets the gateway spawn the server container; ~/.docker/mcp is where it reads your catalog and registry; the docker-secrets-engine socket is the resolver Docker Desktop exposes for the secret store, without it your UNIFI_* URLs resolve to empty strings and the server never starts. On Linux Docker Desktop the path is ~/.docker/desktop/secrets-engine/engine.sock; find it with find ~ -name engine.sock.

Works the same for other clients. Claude Code takes the same args via claude mcp add -s user mcp-toolkit-gateway -- docker run …; Codex reads the same shape from ~/.codex/config.toml as [mcp_servers.mcp-toolkit-gateway]. Any MCP-capable client, Google's Gemini included, reads an mcpServers block from its own settings file; only the location changes, the entry is identical.

Then quit and reopen the client. claude_desktop_config.json never contains UNIFI_API_KEY, the gateway resolves it from Docker's secret store at request time. As a shortcut, docker mcp client connect claude-desktop (or MCP Toolkit → Clients in Docker Desktop) writes a similar block automatically; the explicit JSON above survives Docker Desktop updates that may rewrite the auto-managed entry.

Step 7, Verify
Verify
$ docker mcp server list   # unifi-readonly → enabled
$ docker mcp tools list

Or run the image directly, it starts and waits silently on stdin (correct for an MCP stdio server). Press Ctrl+C to exit; a missing required value fails fast on stderr.

Smoke test (Option B)
$ docker run --rm -i --env-file .env unifi-readonly-mcp:latest
Podman Rootless · stdio
Step 1, Get the project files

Clone the repository. The Podman backend lives in podman/.

Clone
$ git clone https://github.com/rosarion97/unifi-readonly-mcp-server-public.git
$ cd unifi-readonly-mcp-server-public
Step 2, Build the image

On macOS / Windows, start a Podman machine first (podman machine init && podman machine start); Linux runs natively.

Build
$ podman build -t unifi-readonly-mcp:latest podman/
Step 3, Provide credentials

Set them in podman/.env; the file is gitignored and chmod 600'd. Three required values, plus optional Classic-API credentials and tuning knobs.

podman/.env
$ cp podman/.env.example podman/.env
$ chmod 600 podman/.env   # UNIFI_HOST / UNIFI_API_KEY / UNIFI_SITE_ID required

If the UniFi console uses the stock self-signed certificate, leave UNIFI_VERIFY_TLS=false. For production, install a real cert and flip it to true.

Step 4, Configure your AI client

Point Claude Desktop at the container with the absolute path to podman/.env:

claude_desktop_config.json
{
  "mcpServers": {
    "unifi": {
      "command": "podman",
      "args": [
        "run", "--rm", "-i",
        "--env-file", "/absolute/path/to/podman/.env",
        "unifi-readonly-mcp:latest"
      ]
    }
  }
}
Works the same for other assistants. Any MCP-capable client reads an mcpServers block from its own settings.json, Google's Gemini included. Drop this same entry into that client's settings.json and the UniFi tools appear there too. Only the file's name and location change; the server entry is identical.

Claude Code takes the same invocation:

Claude Code
$ claude mcp add -s user unifi -- \
    podman run --rm -i \
    --env-file /absolute/path/to/podman/.env \
    unifi-readonly-mcp:latest

-i is required, MCP stdio needs stdin attached, and every --env-file path must be absolute.

Step 5, Verify

Run the image manually, it starts and waits silently on stdin. Press Ctrl+C to exit; a missing required value fails fast on stderr.

Smoke test
$ podman run --rm -i --env-file podman/.env unifi-readonly-mcp:latest

One container serves one site, run a second container with its own credentials to serve another site. --rm cleans up the container after each session.

Configuration

Three Required, Five Optional

  • UNIFI_HOST, required. Console host or IP (no scheme). LAN IP of your UniFi OS device.
  • UNIFI_API_KEY, required. Integration-API bearer key. Generate at Settings → Control Plane → Integrations → Create API Key while signed in as the local read-only admin.
  • UNIFI_SITE_ID, required. Integration-API site UUID. Pins this instance to one site.
  • UNIFI_PORT, optional. TLS port. Default 443 (UDM/UCG/UDR/UDW); use 11443 for UniFi OS Server.
  • UNIFI_SITE_NAME, optional. Classic-API short site name. Default default.
  • UNIFI_CLASSIC_USERNAME / UNIFI_CLASSIC_PASSWORD, optional. Required only for unifi_classic_* tools. Without them the server still starts; Classic tools raise a clear error on first call.
  • UNIFI_VERIFY_TLS, optional. Default false (stock self-signed cert). Set true once you install a real certificate.
  • UNIFI_MAX_RESPONSE_BYTES, optional. JSON byte cap per tool response. Default 120000 (~30k tokens).
Requirements

Before You Start

  • Docker Engine 24+ / Docker Desktop 4.27+ with the MCP Toolkit feature, or Podman 4.x+ on $PATH. Pick one; the step-by-step for each is above.
  • A UniFi OS console (UDM / UCG / UDR / UDW or UniFi OS Server) running Network application ≥ 9.0 (9.3+ recommended), reachable on the LAN from the host running the container.
  • A dedicated local Limited Admin (Read-Only) on the console: Settings → Control Plane → Admins & Users → Add Admin; Restrict to Local Access Only; role Limited Admin → Read-Only. Do not use a UI.com cloud account, UI.com enforces MFA, which breaks non-interactive logins with HTTP 499.
  • Network Integration API key generated as that local admin under Settings → Control Plane → Integrations → Create API Key. Shown only once at creation; copy immediately.
  • An MCP-capable client, Claude Desktop, Claude Code, Codex, Gemini CLI, or any client speaking MCP over stdio.
License & Policy

Read-Only by Construction

Provided as-is for internal use. Every data query routes through a shared _request() helper that refuses any verb other than GET; the only POST anywhere is the one-off Classic-API login. The server contains no code path that issues a write verb to the console, so it is incapable of modifying any UniFi configuration regardless of what an LLM or user asks for. The Site Manager (cloud) API is intentionally not wrapped, this server talks only to a console you can reach on the LAN. Pull requests that add additional read-only endpoints are welcome; any change that introduces a write-capable verb against the console will be rejected. Not affiliated with or endorsed by Ubiquiti.