Skip to main content
Glama
mgcrea
by mgcrea

@mgcrea/mcp-unifi-network

npm version build status

Model Context Protocol server for the UniFi Network API — sites, devices, clients, hotspot vouchers, networks, WiFi and firewall configuration, plus an optional tier for the parts the official API does not cover. Read-only by default: the mutating tools are not registered at all until you opt in, so an agent cannot call them.

Features

  • Wraps the official Network Integration API (X-API-KEY, no cookie, no CSRF) as the primary transport, with server-side filtering pushed down to the console.

  • Accepts a site as its UUID, its legacy 8-character name, or its display name and translates — so the obvious guess works on the first call instead of returning a 400.

  • Registers tools against the console's actual Network version. This API had 7 endpoints in 9.0 and 44 in 10.3, so an older console is offered only what it can serve.

  • Optional legacy controller tier for what the official API lacks: blocking, unblocking and reconnecting clients, events, alarms, health, port forwarding and adoption.

  • Native fetch, no HTTP client. Two runtime dependencies plus undici — see Security.

Related MCP server: UniFi MCP Server

Security

Supply chain. Three runtime dependencies: the MCP SDK, Zod, and undici. The last is a deviation from the rest of this fleet, taken deliberately: local UniFi consoles ship self-signed certificates, Node's native fetch ignores a node:https agent, and the only scoped way to relax verification is an undici dispatcher. The alternative, NODE_TLS_REJECT_UNAUTHORIZED=0, is process-global and would silently disable verification for every other request the process makes. undici is the same engine Node's own fetch already runs.

TLS. Verification is on by default, and UNIFI_INSECURE_TLS is refused in cloud mode where it would be a pure downgrade. When it is on, the banner says tls=INSECURE on every start. Verifying instead takes two things together, and either alone achieves nothing: the certificate is self-signed, so NODE_EXTRA_CA_CERTS must point at it; and it is issued to unifi.local with no IP SAN, so UNIFI_HOST must be a host name that resolves to the console rather than its IP. See .env.example for both commands.

Your credentials. The API key and, if the legacy tier is enabled, the console password come from the environment or from ~/.config/unifi/config.json, which is warned about if it is group-readable. The legacy session cookie is a full console-admin credential and is held in memory only — never written to disk. Nothing is sent anywhere but your console.

Configuration never kills the server. A contradictory setting is resolved to the safe option and reported through the startup banner and unifi_auth_status, rather than throwing — a server that exits at startup appears in the client as a bare Connection closed with stderr swallowed, taking its own explanation with it.

Blast radius. With the defaults, this server can only read. Turning on UNIFI_ALLOW_WRITES adds: restart a device, power-cycle a PoE port, authorize and unauthorize guest access, create and delete vouchers, and — with the legacy tier — block, unblock and reconnect clients. Every irreversible one requires an explicit confirm: true that the schema enforces before the handler runs. Firewall and network configuration are read-only in every configuration: a wrong policy locks you out of the console you are managing it through, with no undo.

Configure

Variable

Default

Meaning

UNIFI_HOST

The console. A pasted browser URL is accepted and split.

UNIFI_API_KEY

Settings → Control Plane → Integrations → Create API Key.

UNIFI_MODE

inferred

unifios · cloud · classic.

UNIFI_CONSOLE_ID

Cloud mode: the console id from unifi.ui.com.

UNIFI_SITE

Default site. UUID, legacy name or display name.

UNIFI_ALLOW_WRITES

false

Register the mutating tools.

UNIFI_INSECURE_TLS

false

Disable certificate verification, this server only.

UNIFI_ENABLE_LEGACY

false

Register the unifi_legacy_* tools.

UNIFI_USERNAME / UNIFI_PASSWORD

Legacy tier fallback only. A local admin, not SSO.

UNIFI_APP_VERSION

probed

Pin the version instead of probing at startup.

UNIFI_PAGE_LIMIT / UNIFI_MAX_PAGES / UNIFI_MAX_RETRIES

50 / 20 / 3

Tuning.

UNIFI_CONFIG

~/.config/unifi/config.json

JSON alternative to all of the above.

.env.example is the annotated version. Environment variables beat the config file per field, so a one-off UNIFI_ALLOW_WRITES=0 overrides a file that says true without discarding the rest.

Quick start

npx -y @mgcrea/mcp-unifi-network

or from source:

pnpm install && pnpm build
UNIFI_HOST=192.168.1.1 UNIFI_API_KEY=… node dist/cli.js

The banner on stderr reports what it resolved:

unifi-mcp connected (mode=unifios, host=192.168.1.1, version=10.6.101, tier=full (probe),
  sites=1, integration=on, legacy=off, tls=verified, writes=disabled)

Wire into Claude Code

Copy .mcp.json.example to .mcp.json (gitignored) and fill it in.

Inspect the tools

npx @modelcontextprotocol/inspector node dist/cli.js

Tools

W = registered only with UNIFI_ALLOW_WRITES=1. ⚠️ = requires confirm: true. Needs = the minimum UniFi Network version.

Tool

What it does

Needs

unifi_auth_status

Configuration, console version, and what to set

unifi_get_console_info

Version, tier, and which capabilities are gated off here

9.0

unifi_list_sites

Every site with all three of its identifiers

9.0

unifi_list_clients

Currently connected clients

9.0

unifi_get_client

One client in full

9.3

unifi_list_devices

Adopted devices, state, model, firmware

9.0

unifi_get_device

One device in full

9.0

unifi_get_device_stats

CPU, memory, uptime, uplink throughput

9.0

unifi_list_vouchers

Hotspot guest vouchers

9.3

unifi_list_networks

Networks / VLANs (read-only)

10.0

unifi_list_wlans

WiFi broadcasts / SSIDs (read-only)

10.0

unifi_list_firewall_zones

Firewall zones (read-only)

10.0

unifi_list_firewall_policies

Zone policies, and their ordering per zone pair

10.0

unifi_request

Escape hatch for any unwrapped endpoint

W for non-GET

9.0

unifi_restart_device

Reboot a device

W ⚠️

9.0

unifi_power_cycle_port

Reboot whatever is on a PoE port

W ⚠️

9.3

unifi_authorize_guest

Let a client onto the guest network

W

9.3

unifi_unauthorize_guest

Cut a guest's access immediately

W ⚠️

9.3

unifi_create_vouchers

Generate guest vouchers

W

9.3

unifi_delete_vouchers

Delete one voucher, or every match of a filter

W ⚠️

9.3

With UNIFI_ENABLE_LEGACY=1. On a UniFi OS console this needs no extra credential — the console accepts UNIFI_API_KEY on the legacy paths too, so the flag alone is enough:

Tool

What it does

unifi_diagnose_client

"Why will this device not connect?" — one call, with a verdict

unifi_health_check

"Is my network OK?" — ranked findings across every subsystem

unifi_legacy_list_known_clients

Every client ever seen, which are blocked, and what is new

unifi_legacy_get_health

Raw per-subsystem health

unifi_legacy_list_events

Controller event log

unifi_legacy_list_alarms

Open alarms

unifi_legacy_request

Escape hatch: port forwarding, adoption, upgrades, DPI

W ⚠️ non-GET

unifi_legacy_unblock_client

Let a blocked client back on

W

unifi_legacy_block_client

Block a client by MAC

W ⚠️

unifi_legacy_reconnect_client

Kick a client so it reassociates

W ⚠️

Prompts

Clients surface these as slash commands. They carry the order to call things in and the wrong conclusions to avoid on the way — the part no single tool description can hold, because it spans several tools.

Prompt

Argument

For

/diagnose-client

device

"Why won't my lawnmower connect?"

/new-devices

days (default 7)

"Did anything new join the network?"

/network-health

"Is everything OK?"

All three are registered unconditionally, so they work before anything is configured.

Resources

unifi://troubleshooting — field notes on the API behaviours that return a successful, plausible, wrong answer. Registered unconditionally, including with no credentials, because several of them describe failures that occur before anything is configured. Read it before concluding that a device is absent, that nothing is blocked, or that a client has been offline for months.

A worked example: a device that will not connect

unifi_diagnose_client { "device": "husqvarna" }
  → { found: false, verdict: "absent", explanation: "NOT KNOWN TO THIS CONSOLE AT ALL …" }

absent is a diagnosis, not a dead end. A client record is written on association, which happens before the password is checked — so a device refused at the 802.11 authentication frame appears nowhere in the API: no record, no event, nothing. The usual cause is an orphaned block: blocking a client writes its MAC to /etc/persistent/cfg/blocked_sta on every AP, and deleting the client from the controller afterwards leaves that file behind with no way to undo it in the UI. It survives reboots and re-provisioning.

unifi_legacy_unblock_client is safe on a MAC the controller has never heard of and clears exactly this. To confirm before or after, read the AP logs the gateway already collects:

ssh <gateway> 'grep -a "<mac>" /srv/unifi/logs/remote/*.log | tail -20'

auth: disallowed by ACL is a block. Silence means the device never reached the AP at all.

A worked example: find and reboot a stuck access point

unifi_list_devices { "state": "OFFLINE" }
  → [{ id: "…", name: "Garage AP", model: "U6LR", state: "OFFLINE", … }]

unifi_get_device_stats { "deviceId": "…" }
  → { uptimeSec: 32, cpuUtilizationPct: 94, … }

unifi_restart_device { "deviceId": "…", "confirm": true }

The first call filters on the consolestate.eq('OFFLINE') goes down as a query parameter, so nothing is fetched and discarded here.

Traps worth knowing

All of these are baked into the tool descriptions, but they explain the shape of this server.

  1. unifi_list_clients returns only what is connected right now. It is not a device inventory. A blocked client, or one that has not been on the network for a week, is simply absent — so "is anything blocked?" and "why will this thing not connect?" cannot be answered from it, and an empty result reads like an all-clear when it is nothing of the kind. The Integration API has no historical view at all: there is no known-clients, blocked-clients or event endpoint anywhere in it. unifi_legacy_list_known_clients is the answer, and it is the main reason to turn the legacy tier on.

    Worse, the obvious workaround does not work. A server-side filter for the blocked state returns an empty set rather than an error for a value that does not exist:

    filter=access.type.eq('BLOCKED')          → 0 results
    filter=access.type.eq('NOT_A_REAL_VALUE') → 0 results

    So a zero from that query is not evidence of absence, and it is very easy to report a false all-clear from it. Whenever a filtered count is load-bearing, check it against a value you know is fake before you trust the zero.

  2. There are two kinds of API key. A cloud key from unifi.ui.com is not a local console key, and using one against a local console gives a 401 that looks like a typo. See Configure. A local key is accepted on the legacy paths too, which is why the legacy tier needs no console password on UniFi OS.

  3. Cloud mode cannot reach a console the Site Manager API does not list. UNIFI_CONSOLE_ID has to come from GET https://api.ui.com/v1/hosts, and that listing is not the same as what unifi.ui.com shows you. A console grouped into a Fabric — several consoles (Network, Protect, NAS) presented under one name — appears in the web UI but not in /v1/hosts, even with cloudConnected: true on the console itself. Observed on a UDM-Pro that the portal showed and the API did not, on both /v1/hosts and /ea/hosts, with no pagination involved. For such a console there is no host id, so cloud mode is unavailable and you need a local Integration key with UNIFI_MODE=unifios.

  4. siteId is a UUID, not default. A site has three identifiers: the UUID this API's paths take, the legacy 8-character internalReference that appears in every controller URL and forum post, and a display name. Every tool accepts all three. The legacy tools need the internalReference, and that translation happens for you too.

  5. The endpoint set depends on the console's version. 7 paths in 9.0, 12 in 9.3, 32 in 10.0, 44 in 10.3. The server probes GET /v1/info at startup and registers accordingly, so the tool list can differ between two runs against different consoles. unifi_get_console_info says why. If the console cannot be reached at startup the server still comes up, assumes the newest version, and lets any gap surface as an error naming the version it needs — a visible failure beats a silently missing tool.

  6. Local consoles use self-signed certificates, and pinning one is not enough. The certificate is issued to unifi.local with no IP SAN, so a console addressed by IP fails verification however the certificate is trusted — you need a host name too. See Security.

  7. The classic self-hosted controller has no Integration API. API keys are UniFi OS only, so port 8443 means the legacy tier or nothing. The config refuses the contradictory combination rather than failing later at request time.

  8. The legacy API reports errors with HTTP 200. {"meta":{"rc":"error"}} is a failure however healthy the status line looks. That is unwrapped for you in one place.

  9. Legacy payloads are enormous — a stat/device object declares ~423 fields and one UDM-Pro is 50–150 KB. Legacy responses are projected down, and unifi_legacy_request refuses anything over 5 MB rather than parsing it. Pass attrs and _limit.

  10. Login is rate-limited. The legacy session is established once per server start and reused; a 429 on login is never retried, because retrying deepens the lockout.

Troubleshooting

The server does not appear / Connection closed. Run node dist/cli.js by hand with the same environment and read stderr — this server is built never to exit on missing configuration, so a real crash is visible there.

A tool I expected is missing. Call unifi_auth_status, then unifi_get_console_info. It is almost always the version gate or the write flag, both of which unregister rather than refuse.

401 on every call. The key is per-console and shown only once. Re-create it under one of two different kinds of key, which are not interchangeable and which produce a confusing 401 when mixed up:

  • Local Integration key — created on the console itself at https://<console>/network/default/settings/control-plane/integrations. This is what UNIFI_MODE=unifios needs. Use the URL rather than hunting the sidebar: on Network 10.6 Control Plane lives under a heading named after your console at the bottom of the settings sidebar, below System, which is why it gets reported as missing.

  • Cloud Site Manager key — created at https://unifi.ui.com/settings/api-keys. A console's local API rejects this with a 401. It is used with UNIFI_MODE=cloud and UNIFI_CONSOLE_ID, which also works behind CGNAT and needs no TLS workaround at all.

Either kind is shown once and can afterwards only be renamed or deleted, any admin can create one, and creation sometimes errors on the first attempt, so retry before assuming it is broken.

fetch failed / certificate errors. Self-signed certificate; see Security.

Develop

pnpm dev            # tsdown --watch
pnpm test           # vitest, offline, no credentials needed
pnpm typecheck
pnpm lint && pnpm format

Release:

pnpm dlx release-it        # bump, commit, tag
git push --follow-tags     # CI publishes to npm and cuts the GitHub release

The offline suite covers the registration matrix, the confirm gates, site resolution and both error envelopes. The real-console check is the curl probe in .env.example plus the inspector.

License

MIT

A
license - permissive license
A
quality
B
maintenance

Maintenance

Maintainers
Response time
0dRelease cycle
5Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    Enables AI assistants to manage and monitor UniFi Network Controllers through natural language. Provides 25 read-only tools for discovering devices and clients, viewing security configurations, analyzing network statistics, and exporting configuration data.
    41
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables comprehensive management of UniFi Network infrastructure through 24 tools for monitoring and controlling devices, clients, wireless networks, security, and guest access. Supports network administration tasks like device restarts, client blocking, WLAN configuration, and backup creation.
    20
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Exposes the UniFi Network Integration API as tools for managing sites, devices, clients, networks, WiFi, firewalls, ACLs, switching, DNS policies, hotspot vouchers, VPNs, and more.
    41
    260
    MIT

View all related MCP servers

Related MCP Connectors

  • Read-only CVE intelligence, remediation playbooks, and agent setup guides. Not a scanner.

  • Read-only access to your VortexIQ store data: audits, KPIs, alerts, Brand DNA, reports, Ask VIQ.

  • XFA's remote MCP server — query device posture, compliance, policies & CVEs. Read-only.

View all MCP Connectors

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/mgcrea/mcp-unifi-network'

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