Skip to main content

UniFi MCP Server API Documentation

This document provides comprehensive documentation for the Model Context Protocol (MCP) server that exposes UniFi Network Controller functionality. The server enables AI agents and other applications to interact with UniFi network infrastructure in a standardized way.

Table of Contents

Overview

The UniFi MCP Server bridges the gap between AI applications and UniFi Network Controllers by exposing network management capabilities as MCP tools and resources. It handles authentication, request formatting, and error handling, providing a clean interface for network automation. This API reference is aligned to the current implementation baseline and the forward architecture described in SPEC.md.

Operator usage

Objective

Use this reference to verify the exposed MCP surface before changing configs, writing tests, or planning a rollout.

Prerequisites

  • You know the target API mode and transport mode.
  • You know whether you need read-only verification or a write-path review.
  • You have the current phase docs open alongside this reference.

Procedure

  1. Start with configuration and authentication to confirm the runtime assumptions.
  2. Read the feature surface that matches the current deployment mode.
  3. Cross-check any roadmap-dependent claims against SPEC.md, DEVELOPMENT_PLAN.md, and the phase runbooks.
  4. Use the examples and endpoint listings to validate the intended behavior before changing production state.

Verification

  • The documented endpoint or tool exists in the current implementation.
  • The reference does not overstate future features as live.
  • The operator can map the doc to an actual test or validation step.

Rollback

  • If the API reference drifts from the implementation, restate the supported surface and move aspirational material back to roadmap docs.

Common failure modes

  • Treating roadmap language as if it were an implemented guarantee.
  • Skipping the phase docs and relying on the API reference alone.
  • Confusing future transport or auth modes with currently shipped behavior.

Architecture

Key Features

  • Device Management: List, configure, and monitor UniFi devices
  • Network Configuration: Manage networks, VLANs, and subnets
  • Client Information: Query connected clients and their status
  • Firewall Rules: Create and manage firewall rules
  • Site Management: Work with multiple UniFi sites
  • Real-time Monitoring: Access device and network statistics
  • Roadmap alignment: native Protect, Access, multi-controller, dry-run, RBAC, audit, metrics, A2A, and webhooks are defined in SPEC.md

Configuration

Environment Variables

Configure the MCP server using environment variables:
Note: the table below includes both currently supported settings and planned controls that will become active as Phase 5 lands. | Variable | Description | Required | Default | |----------|-------------|----------|---------| | UNIFI_API_KEY | UniFi API Key (used for both Network and Site Manager APIs) | Yes | - | | UNIFI_API_TYPE | API type: cloud-ea, cloud-v1, or local | No | cloud-ea | | UNIFI_MIXED_MODE | Expose the cloud and local tool modules together, for a fleet whose registry holds both cloud controllers and local gateways. Without it the exposed modules follow the single UNIFI_API_TYPE, so local-only tools are missing on a cloud-typed server even when a local gateway is registered. Needs no UNIFI_LOCAL_HOST: the gateways come from the registry. See Connecting to the cloud API and local gateways together | No | false | | UNIFI_LOCAL_HOST | Local gateway hostname or IP (required when UNIFI_API_TYPE=local) | No* | - | | UNIFI_LOCAL_PORT | Local gateway port | No | 443 | | UNIFI_LOCAL_VERIFY_SSL | Verify the local gateway’s TLS certificate. Setting false logs a startup warning; prefer UNIFI_LOCAL_CA_FILE for self-signed certs | No | true | | UNIFI_LOCAL_CA_FILE | CA certificate (PEM) to verify the local gateway with, in place of the default roots. Checked at startup. Can be the console’s own self-signed certificate | No | - | | UNIFI_LOCAL_TLS_SERVER_NAME | Name to check the local gateway’s certificate against, and send as SNI, when it is not UNIFI_LOCAL_HOST. UniFi OS’s own certificate names unifi.local, not the console’s address | No | - | | UNIFI_CLOUD_API_URL | UniFi Cloud API base URL | No | https://api.ui.com | | UNIFI_DEFAULT_SITE | Default site ID | No | default | | UNIFI_RATE_LIMIT_REQUESTS | Max requests per rate limit period | No | 100 | | UNIFI_RATE_LIMIT_PERIOD | Rate limit period in seconds | No | 60 | | UNIFI_REQUEST_TIMEOUT | Request timeout in seconds | No | 30 | | UNIFI_MAX_RETRIES | Maximum retry attempts | No | 3 | | UNIFI_RETRY_BACKOFF_FACTOR | Exponential backoff factor between retries | No | 2.0 | | UNIFI_RETRY_TOTAL_TIMEOUT | Wall-clock budget in seconds for one request, including all retries and backoff waits | No | 60 | | UNIFI_PROFILE | Tool exposure profile: network, devices, security, system, minimal, protect. An unrecognised value logs a warning and falls back to all modules | No | unset | | UNIFI_READ_ONLY | Register only non-mutating tools. State-changing tools are not exposed to the client at all | No | false | | UNIFI_DRY_RUN | Change-safe mode: every tool with a dry_run parameter runs as a preview, and mutating tools without one are not registered | No | false | | UNIFI_AUDIT_LOG_ENABLED | Write an audit record for mutating operations | No | true | | UNIFI_AUDIT_LOG_PATH | Append-only audit log path. Relative paths resolve against the process working directory, which is not predictable under stdio transport — prefer an absolute path. The file is created mode 0600 | No | audit.log | | UNIFI_AUDIT_FAIL_CLOSED | Refuse a mutating tool call (and a read, with UNIFI_AUDIT_READS) when its audit record cannot be written. Set false only as break-glass | No | true | | UNIFI_AUDIT_READS | Also audit read tool calls: attempt and outcome records with the principal, tool, arguments and result, never the response payload | No | false | | UNIFI_AUDIT_CHAIN_KEY | HMAC key for the audit hash chain; without it the chain uses plain SHA-256, which detects edits but can be recomputed by anyone who can write the file | No | unset | | UNIFI_AUDIT_SIEM_TARGETS | Comma-separated SIEM destinations for stored audit records: syslog+tls://host:6514, https://host/path (signed webhook), otlp+https://collector:4318 (see SECURITY.md). Requires UNIFI_AUDIT_CHAIN_KEY | No | unset | | UNIFI_AUDIT_SIEM_WEBHOOK_SECRET | HMAC-SHA256 key signing webhook deliveries; required for https:// targets | With a webhook | unset | | UNIFI_AUDIT_SIEM_OTLP_HEADERS | Extra headers for OTLP targets, key=value,key=value | No | unset | | UNIFI_AUDIT_SIEM_CA_FILE | CA bundle used to verify SIEM targets | No | system roots | | UNIFI_AUDIT_SIEM_BUFFER_DIR | On-disk buffer for undelivered records | No | siem-buffer next to the audit log | | UNIFI_AUDIT_SIEM_BUFFER_MAX_MB | Most undelivered data kept per target; the oldest is dropped beyond it | No | 100 | | UNIFI_AUDIT_LOG_ROTATE_MB | Rotate the JSONL audit file at this size into <log>.<UTC time>Z files; the chain continues across them | No | unset (never rotate) | | UNIFI_AUDIT_RETENTION_DAYS | Archive and delete audit records older than this: Postgres with python -m src.fleet.cli audit-retention --confirm, rotated JSONL files with python -m src.utils.audit_retention LOG --confirm (see SECURITY.md) | No | unset (keep forever) | | UNIFI_AUDIT_ARCHIVE_DIR | Where retention writes its archives | No | audit-archive next to the audit log | | UNIFI_AUDIT_SIEM_ANCHOR_SECONDS | How often each chain’s head is sent to the SIEM as an anchor | No | 300 | | DATABASE_URL | Postgres URL of the fleet controller registry. When set, controllers come from Postgres instead of the UNIFI_* settings (needs the [fleet] extra and python -m src.fleet.cli migrate) | No | unset | | UNIFI_FLEET_CREDENTIAL_KEY | Encrypts the controller API keys stored in Postgres; required with DATABASE_URL | With DATABASE_URL | unset | | UNIFI_FLEET_REGISTRY_REFRESH_SECONDS | How often each server reloads the registry from Postgres | No | 30 | | UNIFI_FLEET_MAX_OPEN_CLIENTS | Most controller connections each server keeps open; least recently used idle ones are closed beyond this (controller state such as cached auth is kept) | No | 256 | | UNIFI_FLEET_EXPECTED_REPLICAS | How many processes (server replicas and workers) share each controller’s rate limit. While Redis is unavailable each falls back to UNIFI_RATE_LIMIT_REQUESTS divided by this (at least 1), so together they stay near the controller’s budget | No | 1 | | UNIFI_FLEET_FANOUT_CONCURRENCY | Most controllers one fleet_fanout call reaches at once | No | 20 | | UNIFI_FLEET_FANOUT_TIMEOUT_SECONDS | Default per-controller timeout of fleet_fanout | No | 15 | | UNIFI_FLEET_FANOUT_DEADLINE_SECONDS | Default overall deadline of fleet_fanout; controllers not done by then are reported as timed out | No | 60 | | UNIFI_FLEET_WORKER_HEALTH_SECONDS | Worker (python -m src.worker): how often each controller is health-checked | No | 60 | | UNIFI_FLEET_WORKER_SYNC_SECONDS | Worker: how often each controller’s sites and device inventory are refreshed | No | 900 | | UNIFI_FLEET_WORKER_CONCURRENCY | Worker: most controller jobs one worker runs at once | No | 20 | | UNIFI_FLEET_WORKER_LEASE_SECONDS | Worker: how long membership and controller leases last without renewal; a dead worker’s controllers move to the others after this | No | 30 | | UNIFI_FLEET_WORKER_DOWN_AFTER | Worker: consecutive failed health checks before a controller is down (earlier failures mark it degraded) | No | 3 | | MIGRATION_DATABASE_URL | Used by python -m src.fleet.cli migrate/status instead of DATABASE_URL, so the schema owner can differ from the server’s least-privilege role | No | unset | | UNIFI_STDIO_ROLE | Role of the local stdio caller: viewer, operator, admin or fleet-admin | No | fleet-admin | | UNIFI_METRICS_ENABLED | Enable Prometheus metrics server | No | false | | WEBHOOK_SECRET | HMAC-SHA256 key verifying X-UniFi-Signature on POST /webhooks/unifi; without it the receiver refuses every request | To receive webhooks | unset | | UNIFI_CACHE_ENABLED | Use the Redis read cache when Redis is configured; set false to bypass it | No | true | | REDIS_URL | Redis URL for the response cache; takes precedence over REDIS_HOST/PORT/DB/PASSWORD | No | unset | | REDIS_HOST / REDIS_PORT / REDIS_DB / REDIS_PASSWORD | Redis location for the response cache | No | localhost / 6379 / 0 / unset | | UNIFI_SITE_MANAGER_ENABLED | Enable Site Manager API multi-site tools | No | false | | MCP_SERVER_TRANSPORT | Transport: stdio, http, sse, streamable_http | No | stdio | | MCP_SERVER_HOST | Server bind address for http/sse/streamable_http | No | 127.0.0.1 | | MCP_SERVER_PORT | Server port (http/sse transports) | No | 3000 | | MCP_AUTH_MAX_FAILURES | Failed bearer-token checks (401) allowed per client address within MCP_AUTH_FAILURE_WINDOW_SECONDS on a network transport. Past that the address gets 429 with Retry-After on every route except /healthz until the window clears. 0 disables it. Behind a reverse proxy every client shares the proxy’s address unless uvicorn trusts its X-Forwarded-For (FORWARDED_ALLOW_IPS) | No | 10 | | MCP_AUTH_FAILURE_WINDOW_SECONDS | Sliding window for MCP_AUTH_MAX_FAILURES | No | 60 | | MCP_STATELESS_HTTP | Run the HTTP transports without MCP sessions so any replica can answer any request (ignored for stdio and SSE). select_controller is then refused: pass controller=<name> per call. The default (stateful) mode needs per-client sticky routing across replicas, keyed on something stable from initialize onward (see MULTI_CONTROLLER.md) | No | false | | MCP_AUTH_TOKEN | Bearer token required to call the server over a network transport; comma-separate for several. Write an entry as <role>:<token> (viewer, operator, admin, fleet-admin) to give it a role; a bare token is fleet-admin. A misspelled or wrong-case role (Viewer:, veiwer:) stops the server instead of becoming a fleet-admin token. http/sse/streamable_http refuse to start without it. Ignored for stdio | No | unset | | DEBUG | Register debug_api_request, an arbitrary GET or DELETE to any controller endpoint (destructive tier; never registered under UNIFI_READ_ONLY). Leave unset in production | No | unset | | LOG_LEVEL | Logging level: DEBUG, INFO, WARNING, ERROR | No | INFO | | LOG_API_REQUESTS | Log each controller API request | No | true | | UNIFI_CACHE_TTL | Accepted but not used: read-cache lifetimes are fixed per resource type (sites, networks and WLANs 5 min, devices 1 min, clients 30 s) | No | 300 | | UNIFI_BACKUP_DOWNLOAD_DIR | Directory download_backup writes into; the caller names only the file | No | . | | SUPERMEMORY_ENABLED | Enable the Supermemory integration for operator notes | No | false | | SUPERMEMORY_API_KEY | Supermemory API key | With SUPERMEMORY_ENABLED | unset |
  • Required when UNIFI_API_TYPE=local.

Configuration File

Alternatively, use a config.yaml file:
Note: Environment variables take precedence over configuration file settings.

Connecting to the cloud API and local gateways together

One server can talk to the UniFi cloud API, to a local gateway, or to several of each at once. Three topologies are supported; which one you are in is decided by whether DATABASE_URL is set and whether UNIFI_MIXED_MODE is on.

1. One controller, from the environment

No DATABASE_URL. UNIFI_API_TYPE picks the backend and the UNIFI_* variables describe it; the registry holds a single controller named default.
The API key lives in the environment. Tool modules follow UNIFI_API_TYPE: local registers the cloud and local modules, a cloud type registers only the cloud ones.

2. Several controllers, from Postgres (fleet mode)

Set DATABASE_URL and UNIFI_FLEET_CREDENTIAL_KEY, run python -m src.fleet.cli migrate, then register each controller. Any mix of local gateways and cloud controllers is allowed, each with its own API key, stored encrypted in the database — not in the environment:
Each key is stored in the credentials table as a Fernet ciphertext, encrypted with UNIFI_FLEET_CREDENTIAL_KEY; list_controllers never returns a credential, and rotate_fleet_credentials re-encrypts every stored key under a new one. Calls are routed per call with controller=<name>, per session with select_controller, or to the registry default. See MULTI_CONTROLLER.md.

3. Mixed cloud and local, in one server

Fleet mode routes to controllers of any type, but the tool modules are chosen once at start-up from the base UNIFI_API_TYPE. On a cloud-typed server the local-only modules (firewall zones and policies, MAC tags, switching, QoS, RADIUS, Protect, topology and the rest) are therefore never registered, so a registered local gateway is routable while most of its tools do not exist. Set UNIFI_MIXED_MODE=true to register the union instead:
With it:
  • the cloud and local tool modules are both registered, so a call routed to a cloud controller reaches the cloud tools and a call routed to a local gateway reaches the local ones;
  • no base UNIFI_LOCAL_HOST is needed — the gateways come from the registry;
  • UNIFI_PROFILE still narrows the union, so UNIFI_PROFILE=protect in mixed mode exposes the Protect modules only;
  • the start-up log names the mode and the module count, so the exposed surface is visible rather than inferred.
Mixed mode widens the tool surface only. MCP resources are not routed — they take no controller argument and are built from the process-wide settings — so they stay gated on the base UNIFI_API_TYPE: a cloud base exposes no local resources even with mixed mode on. Read a specific gateway with a tool and controller=<name> instead. Mixed mode changes which tools exist, never which controller a call reaches or what a role may do: routing, the risk tiers, UNIFI_READ_ONLY, UNIFI_DRY_RUN and the audit trail are unchanged.

Limitations

  • A tool must be registered to be reachable. A module outside the registered set cannot be called on any controller, whatever its type. This is what UNIFI_MIXED_MODE exists to fix.
  • MCP resources always read the default controller. The sites:// family and friends take no controller argument; use the tools for a specific controller. Because they are built from the process-wide settings and cannot be routed, they stay gated on the base UNIFI_API_TYPE — mixed mode does not expose local resources on a cloud base.
  • Tool modules are process-wide. A cloud-only call routed to a cloud controller still fails on a local-only endpoint, and the reverse, because the endpoint does not exist on that backend — mixed mode registers both sets, it does not make an endpoint appear on a controller that lacks it.
  • One API type per registered controller. A controller entry is local, cloud-ea or cloud-v1; to reach the same hardware both ways, register it twice under different names.

Performance Tracking

Agnost.ai Integration (Optional)

The UniFi MCP Server includes optional performance tracking powered by agnost.ai to monitor server performance, usage patterns, and identify optimization opportunities.

Features

  • Real-time Performance Metrics: Track tool execution times, success rates, and error patterns
  • User Analytics: Monitor which tools are most frequently used and by whom
  • Resource Access Patterns: Understand how MCP resources are accessed
  • Error Tracking: Automatic error capture with stack traces and context
  • Custom Event Tracking: Track specific business events (device adoptions, configuration changes)
  • Dashboard: Visual analytics dashboard for performance insights

Configuration

Performance tracking is disabled by default and can be enabled via environment variables:

Tracking Controls

The agnost integration provides granular control over what data is tracked: Input Tracking (disable_input):
  • When false (default): Tool input parameters are tracked
  • When true: Input parameters are excluded from tracking
  • Use this to prevent sensitive configuration data from being sent
Output Tracking (disable_output):
  • When false (default): Tool results and outputs are tracked
  • When true: Output data is excluded from tracking
  • Use this to prevent sensitive response data from being sent
Example - Privacy-Focused Configuration:

Tracked Metrics

The following metrics are automatically tracked by agnost.ai: Tool Invocation Metrics:
  • Tool name
  • Input parameters (if disable_input=false)
  • Output results (if disable_output=false)
  • Execution time (milliseconds)
  • Success/failure status
  • Error messages (if any)
Resource Access Metrics:
  • Resource URI accessed
  • Response data (if disable_output=false)
  • Response size
  • Access patterns
Error Metrics:
  • Error type and message
  • Stack trace and context
  • Request metadata
  • Frequency and patterns
Performance Metrics:
  • Average execution time per tool
  • Tool usage frequency
  • Peak usage times
  • Success/failure rates

Privacy & Security

Data Protection:
  • Granular control via disable_input and disable_output flags
  • Sensitive data (API keys, passwords) automatically masked in logs
  • All data encrypted in transit (HTTPS)
  • Opt-in only (disabled by default)
Privacy Controls:
  • Disable input tracking to prevent parameter exposure
  • Disable output tracking to prevent response data exposure
  • Complete opt-out by setting AGNOST_ENABLED=false
  • No personally identifiable information (PII) collected by default
Compliance:
  • GDPR compliant data handling
  • Configurable data retention policies via agnost.ai dashboard
  • Transparent data collection (documented in this API guide)
  • Data encrypted in transit and at rest

Example Configurations

Full Tracking (Docker):
Privacy-Focused Tracking (Claude Desktop):
Metadata-Only Tracking:

Viewing Analytics

Once configured, visit your agnost.ai dashboard at app.agnost.ai to view:
  • Real-time Performance Metrics: Tool execution times and latency
  • Usage Trends: Most frequently used tools and resources
  • Error Rates: Failure patterns and error types
  • Performance Bottlenecks: Slowest operations and optimization opportunities
  • Historical Data: Trends over time for capacity planning
For more information, see the agnost.ai FastMCP documentation.

MCP Toolbox Dashboard

For a web-based analytics dashboard with visual metrics and debugging tools, use MCP Toolbox:
MCP Toolbox provides:
  • Real-time performance monitoring
  • Visual analytics and graphs
  • Error tracking and debugging
  • Historical trend analysis
  • Request/response inspection
See MCP_TOOLBOX.md for complete documentation.

Authentication

Obtaining Your API Key

The UniFi MCP Server uses the official UniFi Cloud API with API key authentication:
  1. Login to UniFi Site Manager
  2. Navigate to Settings → Control Plane → Integrations
  3. Create a new API Key by clicking “Create API Key”
  4. Save the key immediately - it’s only displayed once!
  5. Store the key securely in environment variables or secret management

Authentication Method

The server uses stateless API key authentication via the X-API-Key HTTP header:
No session management or cookie handling is required. Each request is independently authenticated.

API Access Modes

Cloud API (Recommended)
  • Base URL: https://api.ui.com/v1/
  • Access cloud-hosted UniFi instances
  • Requires internet connectivity
  • SSL verification recommended
Local Gateway Proxy
  • Base URL: https://{gateway-ip}:{port}/proxy/network/integration
  • Access local UniFi gateway directly
  • Works without internet
  • May require SSL verification disabled for self-signed certificates

Current Limitations

  • Read-Only Access: The Early Access API is currently read-only. Write operations will be available in future API versions and will require manual key updates.
  • Rate Limiting: See Rate Limiting section below

Security Considerations

  • Never hardcode API keys in your code
  • Store API keys in environment variables or secure secret management systems (AWS Secrets Manager, HashiCorp Vault, etc.)
  • Use HTTPS when connecting to the UniFi API (especially for cloud)
  • Implement proper access controls for MCP server access
  • Rotate keys regularly - API keys can be regenerated from unifi.ui.com
  • Monitor API key usage for suspicious activity
  • Treat API keys like passwords - they provide full access to your UniFi environment

MCP Tools

Tools are executable functions that perform actions on the UniFi Controller.

Tool Categories

  • Read-only Tools: Read-only operations for querying network information
  • Mutating Tools: Mutating operations with safety mechanisms (confirm + dry-run)

Health Check

health_check

Verify the MCP server is running and accessible. Parameters: None Returns: Server health status information. Example:
Response:

Debug Passthrough

debug_api_request

Send a raw GET or DELETE to any controller endpoint. Registered only when DEBUG is true, 1 or yes, and never under UNIFI_READ_ONLY. Every call needs the destructive tier (admin or fleet-admin); UNIFI_DRY_RUN forces a preview. Parameters:
  • endpoint (string, required): controller-relative API path starting with a single /, e.g. /proxy/network/api/s/default/stat/device. Absolute (https://...) and scheme-relative (//host/...) URLs are refused, so the controller’s API key is never sent to another host
  • method (string, optional): GET (default) or DELETE; anything else is refused
  • confirm (boolean, optional): must be true for a DELETE unless dry_run is set
  • dry_run (boolean, optional): return the request instead of sending it
Returns: The raw JSON response, or {"dry_run": true, "operation": "debug_api_request", "method": ..., "endpoint": ...}. Example:

Controller Selection

Every controller-bound tool accepts an optional controller argument naming the controller to run against. Without it, a call goes to the controller chosen with select_controller in the current MCP session, then to the server’s default. A mutating tool may not fall back to the server default when more than one controller is registered: it needs controller= or a select_controller in the same session. Until the controller registry ships (see docs/FLEET_SCALING_PLAN.md), the server has one controller, default, built from the UNIFI_* settings. MCP resources (sites:// and friends) always use the default controller.

list_controllers

List the controllers this server can reach: name, API type, host, default site and labels (never credentials), plus the default controller and this session’s selection. Parameters: None

select_controller

Choose the controller this session’s later tool calls target. A call’s own controller argument still takes precedence. Parameters: name (string, required) — a name from list_controllers

get_active_controller

Show which controller a call without controller would target, and whether it comes from the session (select_controller) or the default. Parameters: None

Fleet Registry Administration

Available only when DATABASE_URL is set, and never in read-only mode. These tools manage the controller registry rather than a controller, so they take no controller argument. They are fleet-admin tier, need confirm=true, accept dry_run, and every call is audited as an admin event with API keys redacted. A change is routable on the server that made it immediately, on other servers at once when Redis is configured (registry:changed pub/sub), and otherwise within UNIFI_FLEET_REGISTRY_REFRESH_SECONDS.

register_controller

Register a controller. Give it its own api_key (stored encrypted), or name a cloud_account to share that account’s key. Parameters: name (required), api_type (local, cloud-ea, cloud-v1; required), local_host (required for local), local_port, local_verify_ssl, cloud_api_url, default_site, api_key, cloud_account, labels, make_default, confirm, dry_run

update_controller

Change a controller; only the fields passed change. enabled=false takes it out of routing without deleting it; make_default=true moves the default to it. Parameters: name (required), local_host, local_port, local_verify_ssl, cloud_api_url, default_site, labels, enabled, api_key, make_default, confirm, dry_run

register_cloud_account

Register a cloud (Site Manager) API key that cloud controllers can share. Parameters: name (required), api_key (required), api_type (default cloud-ea), cloud_api_url, labels, confirm, dry_run

rotate_fleet_credentials

Re-encrypt every stored API key under the newest UNIFI_FLEET_CREDENTIAL_KEY key. Put the new key first (new,old), run this, then remove the old key. Parameters: confirm, dry_run

Access control

Every tool has a risk tier: read, write, destructive or fleet-admin. A caller’s role grants tiers: viewer (read), operator (+ write), admin (+ destructive), fleet-admin (+ registry, token and audit tools). Destructive tools include deletes, restores, restarts, client blocking and firewall policy changes (create_firewall_policy, update_firewall_policy, update_zbf_policy, create_firewall_rule, update_firewall_rule, assign_network_to_zone, unassign_network_from_zone), so an operator cannot change which traffic the firewall allows. A token can be narrowed further by allow_modules/deny_modules (patterns over tool module names, e.g. protect_*) and controller_labels (a controller must carry all of them). A refused call fails with Permission denied: <tool> needs the '<tier>' tier and is recorded as a denied audit event. MCP_AUTH_TOKEN tokens and stdio are break-glass fleet-admin callers (stdio’s role is set by UNIFI_STDIO_ROLE). An MCP_AUTH_TOKEN entry written <role>:<token>, for example viewer:<token>, gets that role instead, with or without the fleet registry, so a single host can issue a read-only token. Static tokens take no module or controller filters; use issued tokens for those.

create_api_token

Issue a bearer token; its value is returned only in this result (only a hash is stored). Parameters: name, role (required), expires_in_days (default 90; 0 never expires), allow_modules, deny_modules, controller_labels, confirm, dry_run

list_api_tokens

Token names, roles, filters and dates, never values. Parameters: None

revoke_api_token / update_api_token_role

Revoke a token, or change its role. Every server applies the change within 30 seconds. Parameters: name (and role), confirm, dry_run

search_audit_log

Search the Postgres audit trail, newest first, with payloads decrypted when the audit key is set. Each search is itself audited. Parameters: since, until (ISO 8601), event_type, operation, principal, controller, result, limit (max 1000), cursor

verify_audit_chain

Check every server’s hash chain for edited, missing or reordered records, that each chain ends exactly at its audit_chain_heads entry (so records deleted from the end of a chain, a whole chain, or the whole table are reported, and so is a chain whose head was deleted), and that each row’s searchable columns match its sealed record. Returns the chain heads to keep off the server. Parameters: None

export_audit_log

Export stored records (payloads still encrypted) as JSON lines, verifiable with python -m src.utils.audit_verify. Parameters: since, until, limit (max 1000) Operator CLI (needs only DATABASE_URL, except import):

Fleet-Wide Reads

Available when DATABASE_URL is set. All six are read tier, from the module fleet (so deny_modules: ["fleet"] removes them), take no controller argument, and cover only the controllers inside the caller’s controller_labels scope. Naming a controller that is unknown, disabled or out of scope is refused without saying which. fleet_health, fleet_sites and fleet_inventory answer from the worker’s snapshots (python -m src.worker, see MULTI_CONTROLLER.md) with one database query and no controller calls; fleet_compare_versions and fleet_compare_sites also read those snapshots but return comparison summaries without per-entry age fields. Without a running worker, these snapshot tools return unknown health or no sites/devices as applicable.

fleet_health

Each enabled controller’s last health check: ok, degraded (failing), down (UNIFI_FLEET_WORKER_DOWN_AFTER failures in a row) or unknown (never checked), the failure detail, capabilities (network_version on local controllers), and ages. An entry is stale when its last check is more than three health intervals old. Also returns a count per state. Network 11 / UniFi OS 6 platform compatibility. A controller with a USG gateway anywhere in its synced device inventory carries a platform_compat field: {"network_11_capable": false, "reason": "..."}. USG support ends at Network 10.6, so that site has no Network 11 target at all (DEVELOPMENT_PLAN.md §4 Phase 8, Fix 6). platform_compat is absent — not null, not true — on every other controller, including cloud controllers (the worker never inventories them) and any local controller the worker has not yet synced; absence means “no USG-gating signal found”, not an affirmative “compatible” result. There is no corresponding field for the other platform break (a classic, self-hosted Network Application Server): the Integration API’s /v1/info has no host-type field, so it cannot be auto-detected — label that controller instead (e.g. deployment=classic-server) and filter fleet_health/fleet_sites on the labels parameter. See docs/NETWORK_11_COMPATIBILITY.md for the full compatibility matrix.
Parameters: health (ok, degraded, down or unknown), labels (controllers carrying all of these)

fleet_sites

Every controller’s sites: controller, site_id (UUID), short name, display name, age. Parameters: controllers (names), labels

fleet_inventory

Devices across the fleet (local controllers; the worker does not inventory cloud controllers): controller, site, id, MAC, name, model, firmware, state, age. Also the total match count and a count per model. Parameters: model (substring, case-insensitive, e.g. UDM), firmware_below (numeric comparison, e.g. 4.1.0; devices without a version are left out), state (e.g. OFFLINE), controllers, labels, limit (default 200, max 1000), offset

fleet_compare_versions

Compare firmware versions for one model across controllers. Returns the set of versions seen, the newest version, per-controller counts and versions, and a behind flag for each controller that does not have the newest version. Devices with no recorded version are not considered behind. Each model and controller entry also reports the age of the oldest inventory snapshot it used and a stale flag when that age exceeds three health-check intervals. Parameters: model (substring, case-insensitive), controllers, labels

fleet_compare_sites

Compare site short names across controllers. Returns the sites common to every controller compared, the sites unique to each controller, and the full per-controller site lists. Controllers with no synced sites contribute nothing to the common set. Every site entry includes the age of its snapshot; the response also reports the oldest snapshot age per controller and overall, plus a top-level stale flag when the overall age exceeds three health-check intervals. Parameters: controllers, labels

fleet_fanout

Run one read-tier controller tool live on many controllers: at most UNIFI_FLEET_FANOUT_CONCURRENCY at once, each with its own timeout, all under one deadline. Each controller gets its own result, so one unreachable controller never fails the call. Every per-controller call goes through the normal pipeline: the caller’s role, module filters and controller scope apply to it, and cached reads are served from the read cache. Write, destructive and fleet_* tools are refused. Prefer the snapshot tools above when they cover the question. Parameters: tool (required, e.g. list_devices), arguments (the tool’s arguments except controller), controllers, labels, timeout_seconds (default UNIFI_FLEET_FANOUT_TIMEOUT_SECONDS), deadline_seconds (default UNIFI_FLEET_FANOUT_DEADLINE_SECONDS)

Protect Phase 3 Tools

Protect tools are exposed in local API mode and can be isolated with UNIFI_PROFILE=protect. The currently implemented Protect MCP resources are:
  • protect://nvrs
  • protect://nvrs/{nvr_id}
  • protect://cameras
  • protect://cameras/{camera_id}
The currently implemented Protect MCP tools are grouped below. update_protect_device, update_protect_light, update_protect_sensor, update_protect_chime, update_protect_viewer, create_protect_live_view, update_protect_live_view, and send_protect_alarm_webhook require confirm=True and support dry_run.

Cameras

  • list_protect_cameras(limit=None, offset=None)
  • get_protect_camera(camera_id)

NVRs

  • list_protect_nvrs()
  • get_protect_nvr(nvr_id)

Devices, lights, sensors, and chimes

  • list_protect_devices(limit=None, offset=None)
  • get_protect_device(device_id)
  • update_protect_device(device_id, ...)
  • list_protect_lights(limit=None, offset=None)
  • get_protect_light(light_id)
  • update_protect_light(light_id, ...)
  • list_protect_sensors(limit=None, offset=None)
  • get_protect_sensor(sensor_id)
  • update_protect_sensor(sensor_id, ...)
  • list_protect_chimes(limit=None, offset=None)
  • get_protect_chime(chime_id)
  • update_protect_chime(chime_id, ...)
  • list_protect_device_updates(limit=None, offset=None) for device subscription messages

Views and viewers

  • get_protect_meta_info()
  • list_protect_viewers(limit=None, offset=None)
  • get_protect_viewer(viewer_id)
  • update_protect_viewer(viewer_id, ...)
  • list_protect_live_views(limit=None, offset=None)
  • get_protect_live_view(live_view_id)
  • create_protect_live_view(live_view)
  • update_protect_live_view(live_view_id, ...)

Events and alarm webhooks

  • list_protect_events(limit=None, offset=None)
  • get_protect_subscribe_events(limit=None, offset=None) compatibility alias
  • get_protect_event_messages(limit=None, offset=None) compatibility alias
  • get_protect_subscribe_devices(limit=None, offset=None) compatibility alias
  • get_protect_device_updates(limit=None, offset=None) compatibility alias
  • send_protect_alarm_webhook(webhook_id, payload=None): webhook_id is the alarm’s webhook trigger ID, one path segment of letters, digits, ., _, ~, : and -

Still planned / not yet exposed

Snapshot capture, media stream helpers, talkback, and PTZ operations remain roadmap work.

Device Management Tools

get_device_details

Get detailed information for a specific device. Parameters:
  • site_id (string, required): Site identifier
  • device_id (string, required): Device integration-API UUID (the IDs returned by get_network_topology). A legacy _id ObjectId is also accepted.
Returns: Object containing detailed device information, in the Integration v1 shape. This tool reads the Integration v1 device route, which reports a different shape from the legacy /ea/ endpoints behind search_devices and list_devices_by_type: mac_address rather than mac, ip_address rather than ip, a string state ("ONLINE") rather than the legacy integer, and no type field at all. Keys the controller does not report are omitted rather than returned as null, so look keys up defensively — an absent key means “not reported”, never “empty” or “false”. Example:
Response:

get_device_statistics

Retrieve real-time statistics for a device. Parameters:
  • site_id (string, required): Site identifier
  • device_id (string, required): Device ID
Returns: Object containing device statistics. Example:
Response:

list_devices_by_type

Filter devices by type (AP, switch, gateway). Parameters:
  • site_id (string, required): Site identifier
  • device_type (string, required): Device type filter (uap, usw, ugw, udm, uxg, etc.)
Returns: Array of device objects matching the type. Example:

search_devices

Search devices by name, MAC, or IP address. Parameters:
  • site_id (string, required): Site identifier
  • query (string, required): Search query string
Returns: Array of matching device objects. Example:

list_pending_devices

List devices on a site that are waiting to be adopted. Parameters:
  • site_id (string, required): Site identifier
  • limit (integer, optional): Maximum number of devices to return
  • offset (integer, optional): Number of devices to skip
Returns: Array of device objects in the legacy shape (mac, ip, integer state), one per device the controller reports with adopted: false. The Integration v1 API has no pending-devices route, so this reads the legacy stat/device endpoint and filters it. The controller assigns a device its _id when it adopts it, so a pending device has id: null. Key a pending device by its mac, which is the identifier adopt_device takes. Do not pass id to any device tool for a pending record. Example:
Response:
Fields the controller does not report for an unadopted device come back as null; the response above omits the remaining optional fields for brevity.

Client Management Tools

get_client_details

Get detailed information for a specific client. Parameters:
  • site_id (string, required): Site identifier
  • client_mac (string, required): Client MAC address
Returns: Object containing detailed client information. Example:
Response:

get_client_statistics

Retrieve bandwidth and connection statistics for a client. Parameters:
  • site_id (string, required): Site identifier
  • client_mac (string, required): Client MAC address
Returns: Object containing client statistics. Example:
Response:

list_active_clients

List currently connected clients. Parameters:
  • site_id (string, required): Site identifier
Returns: Array of active client objects. Example:

search_clients

Search clients by MAC, IP, or hostname. Parameters:
  • site_id (string, required): Site identifier
  • query (string, required): Search query string
Returns: Array of matching client objects. Example:

Network Information Tools

get_network_details

Get detailed network configuration. Parameters:
  • site_id (string, required): Site identifier
  • network_id (string, required): Network ID
Returns: Object containing network configuration. Example:
Response:

list_vlans

List all VLANs in a site. Parameters:
  • site_id (string, required): Site identifier
Returns: Array of VLAN/network objects. Example:

get_subnet_info

Get subnet and DHCP information for a network. Parameters:
  • site_id (string, required): Site identifier
  • network_id (string, required): Network ID
Returns: Object containing subnet and DHCP configuration. Example:
Response:

get_network_statistics

Retrieve network usage statistics for a site. Parameters:
  • site_id (string, required): Site identifier
Returns: Object containing network statistics across all networks. Example:
Response:

WAN and Dynamic DNS Tools

list_wan_connections

List WAN connections for a site. Parameters:
  • site_id (string, required): Site identifier
Returns: Array of WAN connection objects. Only id and name are guaranteed. The Integration v1 /sites/{site_id}/wans endpoint returns just those two fields and offers no per-WAN detail route, so every other key — including site_id, wan_type, interface, status, dns_servers and is_backup — may be missing. Fields the controller does not report are omitted from the object rather than returned as null, so consumers must look keys up defensively instead of assuming they are always present. An absent key means “not reported”, never “empty” or “false”. Example:

list_dynamic_dns

List Dynamic DNS records for a site. This local API tool redacts stored password material and returns password_configured instead. Parameters:
  • site_id (string, required): Site identifier
Returns: Array of Dynamic DNS records. Example:

get_dynamic_dns

Get one Dynamic DNS record by ID. Parameters:
  • dynamic_dns_id (string, required): Dynamic DNS record ID
  • site_id (string, required): Site identifier
Returns: Dynamic DNS record details.

create_dynamic_dns

Create a Dynamic DNS record. Mutating operation; requires confirm=true unless dry_run=true. Parameters:
  • site_id (string, required): Site identifier
  • host_name (string, required): Hostname to update
  • service (string, optional): Provider service name, default custom
  • interface (string, optional): WAN interface identifier, default wan
  • login (string, optional): Provider login or username
  • password (string, optional): Provider password or token, sent as x_password
  • server (string, optional): Provider server for custom configurations
  • custom_service (string, optional): Custom provider protocol/service label
  • options (array, optional): Provider-specific options
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying
Example:

update_dynamic_dns

Update a Dynamic DNS record. Mutating operation; requires confirm=true unless dry_run=true. Parameters:
  • dynamic_dns_id (string, required): Dynamic DNS record ID
  • site_id (string, required): Site identifier
  • host_name, service, interface, login, password, server, custom_service, options (optional): Fields to update
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying

delete_dynamic_dns

Delete a Dynamic DNS record. Mutating operation; requires confirm=true unless dry_run=true. Parameters:
  • dynamic_dns_id (string, required): Dynamic DNS record ID
  • site_id (string, required): Site identifier
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying

Tagged MAC Tools

A tag is a named set of device MAC addresses (member_table). The controller uses tags to target WiFi broadcasts at a subset of access points. These tools read and write the local gateway’s legacy rest/tag endpoint and require UNIFI_API_TYPE=local; list_device_tags is the read-only Integration API view. MACs are accepted with any common separator and normalized to lowercase colon form. Write tools compare what the controller stored against the request and attach warnings on any difference.

list_mac_tags

List MAC tags on a site. Parameters:
  • site_id (string, required): Site identifier
Returns: Array of tags with id, name, member_table (device MACs) and member_count.

get_mac_tag

Get one MAC tag by ID. Parameters:
  • tag_id (string, required): Tag ID (24-hex ObjectId)
  • site_id (string, required): Site identifier

create_mac_tag

Create a MAC tag. Mutating operation; requires confirm=true unless dry_run=true. A name that matches an existing tag (case-insensitive) is rejected instead of creating a duplicate. Parameters:
  • name (string, required): Tag display name
  • macs (array, required): Device MAC addresses to assign (may be empty)
  • site_id (string, required): Site identifier
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying
Example:

update_mac_tag

Rename a MAC tag or change its members. Mutating operation; requires confirm=true unless dry_run=true. add_macs/remove_macs are applied to the tag’s current members, so members added by someone else are kept. macs replaces the member list outright ([] clears it) and cannot be combined with add_macs/remove_macs. A dry run returns changes (added_macs, removed_macs, name) and the merged_payload that would be sent. Parameters:
  • tag_id (string, required): Tag ID
  • site_id (string, required): Site identifier
  • name (string, optional): New display name
  • macs (array, optional): Full replacement member list
  • add_macs (array, optional): MACs to add
  • remove_macs (array, optional): MACs to remove
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying
Example:

delete_mac_tag

Delete a MAC tag. Mutating operation; requires confirm=true unless dry_run=true. A WLAN that broadcasts only on this tag’s access points may stop broadcasting. Parameters:
  • tag_id (string, required): Tag ID
  • site_id (string, required): Site identifier
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying

Device Migration Tools

Move a device to another site on this controller, or hand it to another controller. Local API only (UNIFI_API_TYPE=local). Each tool finds the device in site_id first (by MAC in any common format, or by 24-hex device ID), so a typo fails before any command is sent and a dry run names the device that would move. All three are mutating: they require confirm=true unless dry_run=true, and are hidden in read-only mode.

move_device_to_site

Move an adopted device to another site on the same controller (cmd/sitemgr move-device). The device reprovisions with the target site’s configuration, so expect a brief outage on it and anything behind it. Parameters:
  • site_id (string, required): Site the device is in now
  • device_id (string, required): Device MAC or 24-hex device ID
  • target_site (string, required): Destination site by short name (e.g. default), UI description, or 24-hex _id. A description shared by several sites is rejected as ambiguous.
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying
Example:

migrate_device

Migrate a device to another controller by setting its inform URL (cmd/devmgr migrate). The device then appears on the new controller as pending adoption; adopt it there to finish. An offline device only receives the command when it next checks in, and the result carries a warning. Parameters:
  • site_id (string, required): Site the device is in now
  • device_id (string, required): Device MAC or 24-hex device ID
  • inform_url (string, required): http(s)://<host>[:port]/inform, e.g. http://controller.example.net:8080/inform. Other paths, credentials, query strings, and non-HTTP schemes are rejected.
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying

cancel_device_migration

Abort a pending migration (cmd/devmgr cancel-migrate). Only useful before the device is adopted on the new controller. Parameters:
  • site_id (string, required): Site the device was migrated from
  • device_id (string, required): Device MAC or 24-hex device ID
  • confirm (boolean/string, required for execution): Set true to apply
  • dry_run (boolean/string, optional): Preview without applying

Site Management Tools

get_site_details

Get detailed site information. Parameters:
  • site_id (string, required): Site identifier
Returns: Object containing site details. Example:
Response:

list_all_sites

List all accessible sites. Parameters: None Returns: Array of site objects. Example:

get_site_statistics

Retrieve site-wide statistics. Parameters:
  • site_id (string, required): Site identifier
Returns: Object containing comprehensive site statistics. Example:
Response:

Site Manager API Tools (v0.3.0+)

Note: These tools require the Site Manager API to be enabled:
  • Set UNIFI_SITE_MANAGER_ENABLED=true
  • The same UNIFI_API_KEY is used for both Site Manager and Network APIs
  • See Site Manager API Docs for more details
The Site Manager API (api.ui.com/v1/) provides cloud-based multi-site management, ISP metrics monitoring, SD-WAN configuration, and host management capabilities.

get_isp_metrics

Get ISP performance metrics for a specific site. Parameters:
  • site_id (string, required): Site identifier
Returns: ISP metrics including bandwidth, latency, jitter, and packet loss. Example:
Response:

query_isp_metrics

Query ISP metrics with optional filters for time range and site. Parameters:
  • site_id (string, optional): Site identifier (omit for all sites)
  • start_time (string, optional): Start time in ISO format (e.g., “2026-02-01T00:00:00Z”)
  • end_time (string, optional): End time in ISO format
Returns: List of ISP metrics matching the query. Example:

list_sdwan_configs

List all SD-WAN configurations across your organization. Parameters: None Returns: List of {id, name, type} summaries from GET https://api.ui.com/v1/sd-wan-configs. The v1.0.0 SD-WAN list has no pagination parameters. An empty list is different from a 404/401/403/error, which is not converted to empty success. Example:
Response:

get_sdwan_config

Get detailed SD-WAN configuration by ID. Parameters:
  • config_id (string, required): Configuration identifier
Returns: Detailed upstream data object from GET /v1/sd-wan-configs/{id}: id, name, type, variant, settings, hubs, spokes and unrecognized fields are preserved. This is not a typed Site Manager destination/performance-policy resource. A null/malformed detail response is an error, not a fabricated config. Example:

get_sdwan_config_status

Get SD-WAN configuration deployment status. Parameters:
  • config_id (string, required): Configuration identifier
Returns: The upstream data object from GET /v1/sd-wan-configs/{id}/status, including generation/apply states, per-hub/spoke warnings/errors, WAN state and connection/tunnel records. The documented generateStatus values are OK, GENERATING, GENERATE_FAILED; site applyStatus values are ok, creating, updating, removing, createFailed, updateFailed, removeFailed. Deployment/apply state is not a tunnel-health inference. Null/malformed known status fields are errors; unknown extension fields are preserved. This read-only Site Manager surface requires UNIFI_SITE_MANAGER_ENABLED=true and a valid cloud API key; it is registered with the site_manager module in full and system profiles across local/cloud modes, subject to RBAC and fleet routing. It is not a standalone controller endpoint. Example:
Response:

list_hosts

List all managed UniFi hosts/consoles in your organization. Parameters:
  • limit (integer, optional): Maximum number of hosts to return
  • offset (integer, optional): Number of hosts to skip (for pagination)
Returns: List of managed hosts with connection status and site counts. Example:
Response:

get_host

Get detailed information about a specific managed host. Parameters:
  • host_id (string, required): Host identifier
Returns: Detailed host information including model, version, and status. Example:

get_version_control

Get Site Manager API version control information. Parameters: None Returns: Version information including current version, latest version, and deprecated versions. Example:
Response:

Cloud Connector passthrough tools

connector_network_get, connector_network_post, connector_network_put, connector_network_patch, connector_network_delete, and the matching connector_protect_* tools forward a request to https://api.ui.com/v1/connector/consoles/{console_id}/proxy/{network,protect}/{path}. The get tools are read tier; the others need confirm=True. Parameters: console_id, path, plus params (optional query parameters) on the get tools and body, confirm and dry_run on the mutating tools Input rules (checked before any request is made; a bad value raises ValidationError):
  • console_id is the host ID from list_hosts, for example 70A7...8EC3:1093004219. Surrounding whitespace is stripped and the stripped value is what goes into the URL. What remains must be one path segment of letters, digits, ., _, ~, : and -, and cannot be only dots.
  • path is the sub-path under the proxy prefix, for example api/s/default/stat/device. Leading slashes are stripped. Every segment uses the same character set as console_id and cannot be empty, . or ..; one trailing slash is allowed. The whole path is capped at 2048 characters. ?, #, % and \ are refused: on the get tools, pass query strings through params.

Phase 4: Mutating Tools

All Phase 4 tools modify UniFi configuration and require safety mechanisms.

Safety Mechanisms

All mutating tools implement these safety features:
  1. Confirmation Required: Must pass confirm=True to execute
  2. Dry Run Mode: Pass dry_run=True to preview without changes
  3. Audit Logging: All operations logged to audit.log
  4. Input Validation: Parameters validated before execution

Firewall Management

list_firewall_rules

List all firewall rules (read-only). Parameters: site_id

create_firewall_rule

Create a new firewall rule. Parameters: site_id, name, action (accept/drop/reject), source, destination, protocol, port, enabled, confirm, dry_run Example:

update_firewall_rule & delete_firewall_rule

Modify or remove firewall rules. Requires confirm=True.

Zone-Based Firewall (ZBF)

Requires: UniFi Network Application 9.0+, Local API Only Zone-Based Firewall provides modern, scalable network security by grouping networks into security zones and defining policies between zones. ⚠️ CRITICAL LIMITATIONS (Verified 2025-11-18): API Availability:
  • ZBF is NOT available via Cloud API - requires local gateway API access (api_type='local')
  • Tested on UniFi Express 7 and UDM Pro (API v10.0.156)
  • Only 2 out of 15 implemented endpoints actually exist in the UniFi API
What Works (✅):
  • list_firewall_zones - List all zones
  • create_firewall_zone - Create new zone (untested but likely works)
  • update_firewall_zone - Modify zone (untested but likely works)
  • delete_firewall_zone - Delete zone (untested but likely works)
  • assign_network_to_zone - Assign network to zone (untested but likely works)
  • unassign_network_from_zone - Remove network from zone (untested but likely works)
  • get_zone_networks - List networks in a zone
What Doesn’t Work (❌ Endpoints Do Not Exist):
  • ❌ Zone policy matrix operations (get/update/delete policies between zones)
  • ❌ Application blocking per zone
  • ❌ Zone traffic statistics
  • These tools have been REMOVED - configure these features in UniFi Console UI
Key Concepts (Available Features Only):
  • Zones: Logical groups of networks (e.g., Internal, External, Gateway, VPN, Hotspot, DMZ)
  • Network Assignment: Assign networks to zones for organization
  • Zone-to-Zone Policies: Must be configured manually in UniFi Console UI
  • Application Blocking: Use DPI categories at network level instead

Zone Management

list_firewall_zones
List all configured firewall zones. Parameters:
  • site_id (string, required): Site identifier
Returns: Array of zone objects with networks and metadata. Example:
Response:
create_firewall_zone
Create a new firewall zone. Parameters:
  • site_id (string, required): Site identifier
  • name (string, required): Zone name
  • description (string, optional): Zone description
  • network_ids (array, optional): Network IDs to assign
  • confirm (boolean, required): Must be true
  • dry_run (boolean, optional): Preview changes
Returns: Created zone object. Example:
update_firewall_zone
Update an existing zone’s name, description, or networks. Parameters:
  • site_id (string, required): Site identifier
  • firewall_zone_id (string, required): Zone ID
  • name (string, optional): New zone name
  • description (string, optional): New description
  • network_ids (array, optional): Updated network list
  • confirm (boolean, required): Must be true
  • dry_run (boolean, optional): Preview changes
Returns: Updated zone object.
delete_firewall_zone
Delete a firewall zone. Parameters:
  • site_id (string, required): Site identifier
  • zone_id (string, required): Zone ID to delete
  • confirm (boolean, required): Must be true
  • dry_run (boolean, optional): Preview changes
Returns: Deletion confirmation. Example:
assign_network_to_zone
Dynamically assign a network to a zone. Parameters:
  • site_id (string, required): Site identifier
  • zone_id (string, required): Zone ID
  • network_id (string, required): Network ID to assign
  • confirm (boolean, required): Must be true
  • dry_run (boolean, optional): Preview changes
Returns: Network assignment confirmation. Example:
unassign_network_from_zone
Remove a network from a zone. Parameters:
  • site_id (string, required): Site identifier
  • zone_id (string, required): Zone ID
  • network_id (string, required): Network ID to remove
  • confirm (boolean, required): Must be true
  • dry_run (boolean, optional): Preview changes
Returns: Network unassignment confirmation.
get_zone_networks
List all networks assigned to a zone. Parameters:
  • site_id (string, required): Site identifier
  • zone_id (string, required): Zone ID
Returns: Array of network assignments with details.

ZBF Usage Example

⚠️ Important Notes:
  • Zone-to-zone policies must be configured in UniFi Console UI
  • Application blocking per zone is not available via API - use DPI categories at network level
  • Zone traffic statistics are not available - use client statistics instead
  • Always use dry_run=True to preview changes before applying
  • See tests/verification/PHASE2_FINDINGS.md for complete verification details

Network Configuration

create_network

Create a new network/VLAN. Parameters: site_id, name, vlan_id, subnet, purpose, dhcp_enabled, DHCP settings, confirm, dry_run

update_network & delete_network

Modify or remove networks. Requires confirm=True.

update_site_to_site_vpn

Update a site-to-site VPN’s networkconf entry. Requires confirm=True. Parameters: site_id, vpn_id, name, enabled, ipsec_peer_ip, remote_vpn_subnets, x_ipsec_pre_shared_key, confirm, dry_run vpn_id must be a 24-character hex ObjectId (or a UUID), as returned by list_site_to_site_vpns. It is lowercased before use, and anything else raises ValidationError before the controller is contacted. get_site_to_site_vpn filters the list client-side and does not apply this check.

Device Control

restart_device

Restart a UniFi device. Parameters: site_id, device_mac, confirm, dry_run

locate_device

Enable/disable LED locate mode. Parameters: site_id, device_mac, enabled, confirm, dry_run

upgrade_device

Trigger firmware upgrade. Parameters: site_id, device_mac, firmware_url, confirm, dry_run

Client Management

block_client & unblock_client

Block or unblock a client from the network. Parameters: site_id, client_mac, confirm, dry_run

reconnect_client

Force a client to reconnect. Parameters: site_id, client_mac, confirm, dry_run

MCP Prompts

The server exposes eight parameterised UniFi operational workflows through the standard MCP prompts interface. A client discovers them with prompts/list and retrieves a rendered workflow brief with prompts/get. Availability. A workflow is advertised only when the tools it needs are registered on this server, so the prompt list follows the API mode, the profile and the read-only and dry-run settings. The full catalog, with each prompt’s arguments and the tools it depends on, is in docs/PROMPTS.md. Retrieving a prompt is not running it. prompts/get returns text: a scoped brief telling an agent which read tools to call and what to report. The agent then calls the tools itself, and the server’s role check, the confirm gate and the audit trail all still apply. Listing and retrieving a prompt contacts no controller, runs no tool, reads no secret and starts no work.

A2A Endpoints

On the network transports the server also serves an A2A-style discovery and delegation surface, behind the same bearer token as /mcp. The card follows the AgentCard object shape of the A2A specification version 0.3.0. The server does not implement the A2A task protocol, so the card declares streaming: false and pushNotifications: false. See A2A.md for the runbook and docs/AUDIT_A2A_SKILLS.md for what is and is not implemented. A delegated call runs the registered tool through its own wrapper, so the role check, the confirm gate, the dry-run behaviour and the audit record apply exactly as they do for an MCP client. The caller’s permissions come from the bearer token’s role, never from the request body.

A2A Error Handling

All mutating tools raise:
  • ConfirmationRequiredError: If confirm parameter not True
  • ValidationError: If parameters invalid
  • ResourceNotFoundError: If resource not found
  • APIError: If UniFi API returns error

Phase 5: Enterprise scale and operational excellence

Phase 5 expands the platform with WiFi management, port forwarding, DPI analytics, caching, webhook support, and enterprise-scale controls.

WiFi Network (SSID) Management

list_wlans

List all wireless networks (SSIDs) in a site (read-only). Parameters:
  • site_id (string, required): Site identifier
  • limit (integer, optional): Maximum number of WLANs to return
  • offset (integer, optional): Number of WLANs to skip
Example:
Response:

create_wlan

Create a new wireless network/SSID. Parameters:
  • site_id (string, required): Site identifier
  • name (string, required): SSID name
  • security (string, required): Security type (open, wpapsk, wpaeap)
  • password (string, optional): WiFi password (required for wpapsk)
  • enabled (boolean, optional): Enable the WLAN immediately (default: true)
  • is_guest (boolean, optional): Mark as guest network (default: false)
  • wpa_mode (string, optional): WPA mode (wpa, wpa2, wpa3) (default: wpa2)
  • wpa_enc (string, optional): WPA encryption (tkip, ccmp, ccmp-tkip) (default: ccmp)
  • vlan_id (integer, optional): VLAN ID for network isolation (1-4094)
  • hide_ssid (boolean, optional): Hide SSID from broadcast (default: false)
  • confirm (boolean, required): Confirmation flag (must be True)
  • dry_run (boolean, optional): Preview without creating
Example:
Security Note: Passwords are automatically masked in audit logs.

update_wlan

Update an existing wireless network. Parameters:
  • site_id (string, required): Site identifier
  • wlan_id (string, required): WLAN ID
  • name (string, optional): New SSID name
  • security (string, optional): New security type
  • password (string, optional): New WiFi password
  • enabled (boolean, optional): Enable/disable the WLAN
  • is_guest (boolean, optional): Mark as guest network
  • wpa_mode (string, optional): New WPA mode
  • wpa_enc (string, optional): New WPA encryption
  • vlan_id (integer, optional): New VLAN ID
  • hide_ssid (boolean, optional): Hide/show SSID
  • confirm (boolean, required): Confirmation flag
  • dry_run (boolean, optional): Preview without updating

delete_wlan

Delete a wireless network. Parameters:
  • site_id (string, required): Site identifier
  • wlan_id (string, required): WLAN ID
  • confirm (boolean, required): Confirmation flag
  • dry_run (boolean, optional): Preview without deleting

get_wlan_statistics

Get WiFi usage statistics for a site or specific WLAN. Parameters:
  • site_id (string, required): Site identifier
  • wlan_id (string, optional): Optional WLAN ID to filter statistics
Example:
Response:

Port Forwarding Management

list_port_forwards

List all port forwarding rules in a site (read-only). Parameters:
  • site_id (string, required): Site identifier
  • limit (integer, optional): Maximum number of rules to return
  • offset (integer, optional): Number of rules to skip
Example:
Response:

create_port_forward

Create a port forwarding rule. Parameters:
  • site_id (string, required): Site identifier
  • name (string, required): Rule name/description
  • dst_port (integer, required): Destination port (external/WAN port), 1-65535
  • fwd_ip (string, required): Forward to IPv4 address (internal/LAN), dotted quad without leading zeros
  • fwd_port (integer, required): Forward to port (internal)
  • protocol (string, optional): Protocol (tcp, udp, tcp_udp) (default: tcp_udp)
  • src (string, optional): Source restriction: any, an IPv4 address, or a CIDR network such as 203.0.113.0/24 (host bits are cleared, so 203.0.113.7/24 is sent as 203.0.113.0/24) (default: any)
  • enabled (boolean, optional): Enable the rule immediately (default: true)
  • log (boolean, optional): Enable logging for this rule (default: false)
  • confirm (boolean, required): Confirmation flag
  • dry_run (boolean, optional): Preview without creating
Example:

delete_port_forward

Delete a port forwarding rule. Parameters:
  • site_id (string, required): Site identifier
  • rule_id (string, required): Port forwarding rule ID
  • confirm (boolean, required): Confirmation flag
  • dry_run (boolean, optional): Preview without deleting

Diagnostics and Historical Statistics

get_speed_test_history

Get historical speed test results for a site, oldest first. Reads the stat/report/archive.speedtest report. Parameters:
  • site_id (string, required): Site identifier
  • hours (integer, optional): How far back to look (default: 168 = 7 days)
Returns: {"results": [...]}. An empty history adds statistics_note (see the note on the statistics reset below).

get_historical_stats

Read archived time-series statistics from stat/report/{interval}.{subject} — the only place historical airtime, per-client signal, and gateway counters live. Parameters:
  • site_id (string, required): Site identifier
  • subject (string, optional): site, ap, user, or gw (default: ap)
  • interval (string, optional): 5minutes, hourly, daily, or monthly (default: hourly)
  • hours (integer, optional): Window ending now, 1-2160 (default: 24)
  • macs (string or array, optional): Restrict to specific devices/clients
  • attrs (array, optional): Report attributes; defaults per subject, time always included
Returns: {"samples": [...]}, oldest first, each sample carrying epoch-ms time. An empty series adds statistics_note (below).
The statistics reset note. UniFi Network 11 replaces the statistics engine and resets historical data, so an empty history immediately after the upgrade is expected — not evidence of an outage or a broken tool (DEVELOPMENT_PLAN.md §4 Phase 8, N3). Every empty response from get_historical_stats, get_speed_test_history, get_dpi_statistics, list_top_applications and get_client_dpi carries a statistics_note field saying so. The field is absent on non-empty responses.

DPI (Deep Packet Inspection) Statistics

get_dpi_statistics

Get Deep Packet Inspection statistics for a site. Parameters:
  • site_id (string, required): Site identifier
The counters are lifetime totals read from the stat/sitedpi report; the endpoint accepts no time window. app and cat are the controller’s numeric catalog ids — translate them with list_dpi_applications and list_dpi_categories. A controller that runs traffic identification through the flow engine reports empty lists plus a note pointing at get_top_flows / get_flow_analytics, and the statistics_note described above. Example:
Response:

list_top_applications

List top applications by bandwidth usage. Parameters:
  • site_id (string, required): Site identifier
  • limit (integer, optional): Number of top applications to return (default: 10)
Example:
Response:
An empty list adds statistics_note (see the statistics reset note above).

list_dpi_categories

List the DPI categories the controller recognises. Parameters: none. Returns: Array of category objects. Only id and name are guaranteed, and id is a number on the Integration v1 route (categories are numbered from zero) while the legacy endpoint sends a string. Fields the controller does not report are omitted rather than returned as null. Example:

list_dpi_applications

List the DPI applications the controller recognises. Parameters:
  • limit (integer, optional): Maximum number of results
  • offset (integer, optional): Starting position
  • filter_expr (string, optional): Filter expression
Returns: Array of application objects. Only id and name are guaranteed — the Integration v1 route returns just those two, with a numeric id — so category_id, category_name, enabled, protocols and ports may all be missing. Absent keys are omitted rather than returned as null; an absent key means “not reported”, never “empty” or “disabled”. Example:

get_client_dpi

Get DPI statistics for a specific client. Parameters:
  • site_id (string, required): Site identifier
  • client_mac (string, required): Client MAC address
  • time_range (string, optional): Time range for statistics (default: 24h)
  • limit (integer, optional): Maximum number of applications to return
  • offset (integer, optional): Number of applications to skip
Example:
Response:
A client with no DPI rows adds statistics_note (see the statistics reset note above).

Traffic Flow Monitoring

Real-time traffic flow monitoring and analysis for UniFi Network 9.0+. Provides comprehensive network visibility, security quick-response capabilities, and advanced analytics.

get_traffic_flows

Retrieve traffic flows with optional filtering. By default the tool returns the 50 most-recent completed flows. Pass hours and/or max_flows to page through a longer window instead (pageNumber / pageSize with timestampFrom / timestampTo on the v2 endpoint); source_mac and direction are then also sent as server-side pre-filters. The client-side filters apply in both modes. Parameters:
  • site_id (string, required): Site identifier
  • source_mac (string, optional): Filter by source MAC address (case-insensitive)
  • source_ip (string, optional): Filter by source IP address
  • source_zone_name (string, optional): Filter by source firewall zone name
  • source_network_name (string, optional): Filter by source VLAN display name
  • destination_ip (string, optional): Filter by destination IP address
  • destination_port (integer, optional): Filter by destination port
  • destination_zone_name (string, optional): Filter by destination zone name (useful for External)
  • destination_network_name (string, optional): Filter by destination VLAN display name
  • protocol (string, optional): Filter by protocol (TCP, UDP, ICMP, …)
  • action (string, optional): Filter by firewall action (allowed, blocked)
  • direction (string, optional): Filter by direction (outgoing, incoming, local)
  • risk (string, optional): Filter by risk classification
  • min_bytes (integer, optional): Only flows with at least this many total bytes
  • client_name_contains (string, optional): Substring match against the source client name
  • limit (integer, optional): Maximum number of flows to return (after filtering)
  • hours (integer, optional): Page through the flows completed in the last hours (at least 1)
  • max_flows (integer, optional): Upper bound on flows fetched in paged mode, before client-side filtering (1-50000, default 5000)
Example:
Response:

stream_traffic_flows

⚠️ NOT SUPPORTED — This tool raises NotImplementedError. The UniFi v2 traffic-flows endpoint returns a rolling snapshot of the 50 most-recent completed flows per call. It has no persistent connection, no pagination, and no server-push mechanism, so true streaming is not possible. Fast networks roll through the 50-flow window in under a second, making polling-based streaming impractical. Use instead: Poll get_traffic_flows on your own interval.

get_connection_states

⚠️ NOT SUPPORTED — This tool raises NotImplementedError. The UniFi v2 traffic-flows endpoint reports completed flows with start/end timestamps. It does not expose a live connection table or track in-progress connection states (active, closed, timed_out). Use instead: Use get_traffic_flows and inspect flow_start_time / flow_end_time fields to infer connection duration.

get_client_flow_aggregation

Get comprehensive traffic aggregation for a specific client. Parameters:
  • site_id (string, required): Site identifier
  • client_mac (string, required): Client MAC address
  • hours (integer, optional): Aggregate the flows of the last hours instead of the latest-50 snapshot (see get_traffic_flows)
  • max_flows (integer, optional): Upper bound on flows fetched in paged mode (1-50000, default 5000)
Example:
Response:

block_flow_source_ip

Block source IP address from a suspicious traffic flow. Parameters:
  • site_id (string, required): Site identifier
  • flow_id (string, required): Flow identifier to block
  • duration (string, optional): Block duration - “permanent” or “temporary” (default: permanent)
  • expires_in_hours (integer, optional): Hours until expiration (for temporary blocks)
  • confirm (boolean, required): Confirmation flag (must be true)
  • dry_run (boolean, optional): Validate without executing (default: false)
Example:
Response:

block_flow_destination_ip

Block destination IP address from a traffic flow. Parameters:
  • site_id (string, required): Site identifier
  • flow_id (string, required): Flow identifier to block
  • duration (string, optional): Block duration (default: permanent)
  • expires_in_hours (integer, optional): Hours until expiration
  • confirm (boolean, required): Confirmation flag
  • dry_run (boolean, optional): Validate without executing
Example:

block_flow_application

Block application identified in a traffic flow using traditional firewall rules or DPI categories. Parameters:
  • site_id (string, required): Site identifier
  • flow_id (string, required): Flow identifier to block
  • confirm (boolean, required): Confirmation flag
  • dry_run (boolean, optional): Validate without executing
Example:
Response:
Note: Uses traditional firewall rules or DPI categories. Zone-based application blocking is not available via API.

export_traffic_flows

Export traffic flows to JSON or CSV format. Parameters:
  • site_id (string, required): Site identifier
  • export_format (string, optional): Export format - “json” or “csv” (default: json)
  • time_range (string, optional): Time range (default: 24h)
  • include_fields (array, optional): Specific fields to include (null = all fields)
  • filter_expression (string, optional): Filter expression
  • max_records (integer, optional): Maximum number of records
Example:
Response:

get_flow_analytics

Get comprehensive flow analytics dashboard. Parameters:
  • site_id (string, required): Site identifier
  • time_range (string, optional): Time range (default: 24h)
Example:
Response:

get_flow_statistics

Get aggregated flow statistics. Parameters:
  • site_id (string, required): Site identifier
  • time_range (string, optional): Time range (default: 24h)
Example:

get_traffic_flow_details

Get detailed information for a specific traffic flow. Parameters:
  • site_id (string, required): Site identifier
  • flow_id (string, required): Flow identifier
Example:

get_top_flows

Get top bandwidth-consuming flows. Parameters:
  • site_id (string, required): Site identifier
  • limit (integer, optional): Number of top flows (default: 10)
  • time_range (string, optional): Time range (default: 24h)
  • sort_by (string, optional): Sort by field - “bytes”, “packets”, or “duration” (default: bytes)
Example:

get_flow_risks

Get risk assessment for traffic flows. Parameters:
  • site_id (string, required): Site identifier
  • time_range (string, optional): Time range (default: 24h)
  • min_risk_level (string, optional): Minimum risk level - “low”, “medium”, “high”, “critical”
Example:
⚠️ NOT SUPPORTED — This tool raises NotImplementedError. The UniFi v2 traffic-flows endpoint provides a rolling snapshot of the 50 most-recent completed flows — it has no historical query capability and does not support time ranges or aggregation intervals. Use instead: For bandwidth time-series data, use the stat/report/* endpoints via other tools (e.g., client bandwidth history).

filter_traffic_flows

Filter flows with a comma-separated list of key=value pairs. Parameters:
  • site_id (string, required): Site identifier
  • filter_expression (string, required): Comma-separated key=value pairs using the filter parameters of get_traffic_flows (e.g. "protocol=TCP,destination_zone_name=External,min_bytes=1000")
  • limit (integer, optional): Maximum number of flows
  • hours (integer, optional): Page through the flows of the last hours (see get_traffic_flows)
  • max_flows (integer, optional): Upper bound on flows fetched in paged mode (1-50000, default 5000)
Example:
Use Cases:
  • Security Operations: Real-time threat detection and automated blocking
  • Network Monitoring: Live bandwidth and connection tracking
  • Compliance: Flow data export for audit requirements
  • Troubleshooting: Connection state analysis and client profiling
  • Analytics: Application usage patterns and network insights

Network Topology

Retrieve and visualize your complete network topology including devices, clients, and their interconnections. Available Tools:
  • get_network_topology: Retrieve complete topology graph
  • get_device_connections: Device interconnection details
  • get_port_mappings: Port-level connection mapping
  • export_topology: Export topology in multiple formats
  • get_topology_statistics: Topology summary statistics

Get Network Topology

Retrieve the complete network topology including all devices, clients, and their interconnections. Example:
Response:

Get Device Connections

Retrieve connection details for a specific device or all devices. Both get_device_connections and get_port_mappings accept a topology node id, a device MAC, or a device name as device_id. An identifier that matches none of them raises ResourceNotFoundError — distinct from a known device with no connections, which returns an empty result. get_port_mappings reports the resolved node id as device_id and echoes the caller’s identifier as requested_id. Example:

Get Port Mappings

Get detailed port-level connection mapping for a device. Each port maps to a list of peers: one physical port routinely carries several hosts — a virtualization host bridging its guests, or a downstream unmanaged switch. Example:
Response:

Export Topology

Export network topology in multiple formats for visualization or documentation. Supported Formats:
  • json: JSON format for programmatic use
  • graphml: GraphML (XML) format for network visualization tools (Gephi, yEd, etc.)
  • dot: DOT format for Graphviz rendering
Example:
Example DOT Output:

Get Topology Statistics

Get summary statistics about your network topology. Example:
Response:
Use Cases:
  • Network Documentation: Auto-generate network diagrams
  • Visualization: Export to Graphviz, yEd, or Gephi for interactive visualization
  • Capacity Planning: Understand network depth and connection patterns
  • Troubleshooting: Identify connection issues and network bottlenecks
  • Monitoring: Track topology changes over time
  • Compliance: Document network architecture for audits

Caching (Optional)

The MCP server includes optional Redis-based caching to reduce API calls and improve performance. Configuration: Install the redis client with pip install 'unifi-mcp-server[fleet]', then set these environment variables to enable caching:
  • REDIS_URL (optional; e.g. redis://:password@host:6379/0, takes precedence over the fields below)
  • REDIS_HOST (default: localhost)
  • REDIS_PORT (default: 6379)
  • REDIS_DB (default: 0)
  • REDIS_PASSWORD (optional)
The cache is used only when REDIS_URL or REDIS_HOST is set explicitly (a local Redis that merely exists is never used) and UNIFI_CACHE_ENABLED is not false. It covers the inventory and configuration reads of the device, client, site, network and WLAN tools (statistics and RF-health tools are live metrics and never cached). Those tools gain an optional fresh argument that skips the cache and refreshes it. Any write to a controller, successful or not (dry runs excepted), clears that controller’s cached reads; changes made outside this server show up when entries expire. Redis errors bypass the cache instead of failing a call. Hits and misses are counted in unifi_mcp_cache_requests_total. Independently of Redis, every server keeps one long-lived connection per controller: tool calls share it, along with the controller’s rate limiter (so UNIFI_RATE_LIMIT_REQUESTS applies across calls), its authentication probe and site map (reused for 5 minutes), and a circuit breaker that fails fast for 30 seconds after 5 consecutive calls the controller did not answer. Cache TTL by Resource Type:
  • Sites: 5 minutes
  • Devices: 1 minute
  • Clients: 30 seconds
  • Networks: 5 minutes
  • WLANs: 5 minutes
  • Firewall Rules: 5 minutes
  • Port Forwards: 5 minutes
  • DPI Stats: 2 minutes
Cache Invalidation: Caches are automatically invalidated when mutating operations are performed on related resources.

Liveness (GET /healthz)

On a network transport the server answers GET /healthz with {"status": "ok"} (Cache-Control: no-store) without a bearer token, for load balancers and container health checks. It says only that the process is serving HTTP: no version, controller or configuration. Use the health_check tool or /metrics (both behind the token) for more.

Webhooks (Optional)

On a network transport (http, sse, streamable_http) the server serves POST /webhooks/unifi when WEBHOOK_SECRET is set, with the default handlers below registered. Without the secret the route is not mounted at all; over stdio there is no HTTP surface. Authentication: the route does not use the MCP bearer token (UniFi cannot send one). Each request must carry X-UniFi-Signature: the hex HMAC-SHA256 of the raw request body, keyed with WEBHOOK_SECRET. The receiver (src/webhooks/) is a Starlette endpoint and can also be mounted on another app:
An unconfigured receiver mounted this way refuses every request (503). Responses: 200 processed (or {"status": "duplicate"} for a repeated event_id within 5 minutes), 400 invalid JSON or event, 401 missing or invalid signature, 429 more than 100 events per site per minute, 503 no secret configured (receiver mounted by hand only). Supported Events:
  • device.online - Device came online
  • device.offline - Device went offline
  • client.connected - Client connected
  • client.disconnected - Client disconnected
  • alert.raised - Alert raised
  • event.occurred - Generic event
Event Handlers: device.* and client.* events clear the server’s read cache (when Redis is configured). A payload does not say which controller sent it, so the cached reads of every controller are cleared. Custom handlers can be registered with receiver.register_handler(event_type, handler).

Redis webhook event bus

When Redis is configured (REDIS_URL or REDIS_HOST), every verified webhook event is also published to the pub/sub channel unifi:webhooks:v1:events after local handlers run. Other replicas and workers can subscribe to the channel to react to events without each process receiving the HTTP callback directly. Payload shape:
A listener ignores messages whose origin matches its own instance id, so a process does not handle an event twice. The publish does not sit on the HTTP acknowledgement path. It is dispatched as a background task once the local handlers have run, so a Redis that accepts connections but stops answering cannot add the connection pool’s socket timeout to the webhook response, and a sender with a short delivery timeout does not retry an event whose handlers already ran. At most 1000 publishes may be in flight at once; past that the fan-out drops events with a warning rather than queueing without bound, and local handling is unaffected. On shutdown the server waits up to 5 seconds for outstanding publishes, then cancels them and logs how many never reached Redis.

MCP Resources

Resources provide read-only access to UniFi data through standardized URIs.

Resource URI Scheme

Resources use a hierarchical URI scheme:

Available Resources

sites://{site_id}/devices

List all devices in a site. Example:

sites://{site_id}/devices/{mac_address}

Get a specific device by MAC address. Example:

sites://{site_id}/networks

List all networks in a site. Example:

sites://{site_id}/clients

List all active clients in a site. Example:

Resource Error Handling

Error Response Format

Errors are returned in a standardized format:

Common Error Codes

Rate Limiting

Official API Rate Limits

The UniFi Cloud API enforces the following rate limits:

Rate Limit Response

When rate limits are exceeded, the API returns: HTTP Status: 429 Too Many Requests Headers:
The Retry-After header indicates how many seconds to wait before retrying.

MCP Server Rate Limit Handling

The MCP server implements intelligent rate limit handling:
  • Automatic retry with exponential backoff
  • Request queuing to prevent overwhelming the API
  • Configurable rate limit via UNIFI_RATE_LIMIT_REQUESTS environment variable
  • Graceful degradation when limits are reached

Best Practices

For Application Developers:
  • Batch operations when possible to reduce API calls
  • Cache frequently accessed data (devices, networks, etc.)
  • Avoid polling; use event-driven approaches when available
  • Implement client-side rate limiting to stay under limits
  • Monitor the X-RateLimit-* headers (if provided by API)
For High-Volume Applications:
  • Consider the v1 Stable API when available (10,000 req/min)
  • Distribute load across multiple time windows
  • Use pagination efficiently to minimize requests
  • Cache static data locally
Rate Limit Configuration Example:

Examples

Backup and Restore Operations

trigger_backup

Create a backup of the UniFi controller configuration. Parameters:
  • site_id (string, required): Site identifier
  • backup_type (string, required): Type of backup - “network” or “system”
    • "network": Network settings and device configurations only (~5-10 MB)
    • "system": Complete OS, application, and device configurations (~50-100 MB)
  • retention_days (integer, optional): Days to retain backup (default: 30, -1 for indefinite)
  • confirm (boolean, required): Must be true to execute
  • dry_run (boolean, optional): Validate without creating backup
Returns:
Note: a UniFi OS console serves backup files from its /dl/ tree — automatic backups from dl/autobackup/, manual ones from dl/backup/. See Download Backup. Example:

list_backups

List all available backups for a site. Parameters:
  • site_id (string, required): Site identifier
Returns:

get_backup_details

Get detailed information about a specific backup. Parameters:
  • site_id (string, required): Site identifier
  • backup_filename (string, required): Backup filename
Returns: Detailed backup metadata dictionary

download_backup

Download a backup file to local storage. Parameters:
  • site_id (string, required): Site identifier
  • backup_filename (string, required): Backup filename (a single path component; separators and .. are rejected)
  • output_path (string, required): Filename to save under. Only the filename is used — any directory component is ignored and the file is written inside UNIFI_BACKUP_DOWNLOAD_DIR (default: the current working directory)
  • verify_checksum (boolean, optional): Calculate SHA-256 checksum (default: true)
Returns:
Example:

delete_backup

Delete a backup file from the controller (requires confirmation). Parameters:
  • site_id (string, required): Site identifier
  • backup_filename (string, required): Backup filename
  • confirm (boolean, required): Must be true to execute
  • dry_run (boolean, optional): Validate without deleting
Warning: This permanently deletes the backup file.

restore_backup

Restore the UniFi controller from a backup file (DESTRUCTIVE operation). Parameters:
  • site_id (string, required): Site identifier
  • backup_filename (string, required): Backup filename to restore from
  • create_pre_restore_backup (boolean, optional): Create safety backup first (default: true, recommended)
  • confirm (boolean, required): Must be true to execute
  • dry_run (boolean, optional): Validate without restoring
Returns:
Example:
Critical Warnings:
  • Controller will restart during restore process
  • All current configuration will be overwritten
  • Devices may temporarily disconnect
  • ALWAYS create pre-restore backup for rollback capability

validate_backup

Validate a backup file before restore. Parameters:
  • site_id (string, required): Site identifier
  • backup_filename (string, required): Backup filename
Returns:
Example:

Complete Example: Network Setup

Example: Monitor Devices

Example AI Assistant Prompts

This section provides example natural language prompts you can use with AI assistants (like Claude) that have access to the UniFi MCP Server. These demonstrate real-world use cases for the current feature set.

Network Topology & Visualization

Prompt: “Show me a complete map of my network topology including all devices and how they’re connected.” The assistant will use get_network_topology to retrieve the full network graph, showing devices, clients, and interconnections. Prompt: “Export my network topology as a Graphviz DOT file so I can visualize it in a network diagram tool.” Uses export_topology with format="dot" to generate a DOT format file compatible with Graphviz and similar visualization tools. Prompt: “Which devices are connected to my main switch on ports 5-10?” The assistant will use get_port_mappings to show exactly what’s plugged into specific switch ports.

Quality of Service (QoS) Management

Prompt: “My video calls stutter when someone is uploading. Turn on Smart Queues on my WAN for my 500/50 Mbps connection.” The assistant will use configure_smart_queue with download_mbps=500 and upload_mbps=50 to enable fq_codel shaping on the primary WAN. Prompt: “Is Smart Queue enabled on my WAN, and what rates is it shaping to?” Uses get_smart_queue_status to show whether the shaper is on and its download/upload rates in Mbps. Prompt: “Which of my devices are routed through the VPN, and is the kill switch on?” The assistant uses list_traffic_routes to read the site’s Traffic Routes, including each route’s matching target, target clients or networks, egress interface, and kill-switch setting.

Backup & Disaster Recovery

Prompt: “Create a full system backup of my UniFi controller right now and keep it for 90 days.” The assistant will use trigger_backup with backup_type="system" and retention_days=90. Prompt: “Show me all backups from the last 7 days and their sizes.” Uses list_backups and filters results to show recent backups with size information. Prompt: “Download my latest network backup to /tmp/emergency-backup.unf and verify the checksum.” The assistant uses download_backup with checksum verification enabled for data integrity. Prompt: “Set up automatic daily backups at 2 AM with 30-day retention.” Uses configure_backup_schedule to establish automated backup policies with cron expressions. Prompt: “What’s the status of my last backup? Is it complete and cloud-synced?” The assistant uses get_backup_status to check backup completion and synchronization state.

RADIUS & Authentication

Prompt: “Set up a RADIUS server for 802.1X authentication using my FreeRADIUS server at 192.168.1.50.” The assistant will use create_radius_profile to configure RADIUS authentication for network access control. Prompt: “Create a RADIUS user account ‘guest-wifi’ with password for our guest portal.” Uses create_radius_account to provision user credentials for RADIUS authentication. Prompt: “Configure the guest portal with a 4-hour session timeout and redirect users to our company website after login.” The assistant uses configure_guest_portal to customize the captive portal experience. Prompt: “Show me all active RADIUS profiles and which ones have accounting enabled.” Uses list_radius_profiles to display authentication server configurations. Prompt: “Create a hotspot package: ‘$5 for 24 hours’ for our café WiFi.” The assistant uses create_hotspot_package to set up paid WiFi access sold by duration — the controller prices packages by the hour; bandwidth and data quotas are not part of this API surface.

Multi-Site Management

Prompt: “Compare the device count and health status across all my sites.” The assistant uses aggregate_device_stats to provide cross-site device analytics. Prompt: “Create a new site called ‘Branch Office’ with description ‘Remote office in Austin, TX’.” Uses create_site to provision a new UniFi site for multi-location management. Prompt: “Move the device ‘AP-Office-2’ from the default site to the ‘Branch Office’ site.” The assistant uses move_device_to_site for cross-site device transfers. Prompt: “Show me aggregate client statistics across all sites - total count, by connection type, and by band.” Uses aggregate_client_stats to provide organization-wide client analytics. Prompt: “Set up a site-to-site VPN between my main office and branch office.” The assistant uses configure_site_vpn to establish secure inter-site connectivity.

Firewall & Traffic Control

Prompt: “Block all BitTorrent traffic on my guest network during business hours.” The assistant will create a traffic matching list for P2P protocols and a firewall rule with schedule. Prompt: “Create an ACL that allows only IT department devices (192.168.10.0/24) to access the server VLAN (192.168.20.0/24).” Uses create_acl to implement network segmentation and access control. Prompt: “Show me all traffic flows where clients are using more than 10 GB per day.” The assistant uses list_traffic_flows with filtering to identify high-bandwidth users. Prompt: “Block the client at MAC address aa:bb:cc:dd:ee:ff for suspicious activity.” Uses block_traffic_flow to immediately block a specific device.

Advanced Network Analysis

Prompt: “Analyze my network topology and tell me the maximum depth from the gateway and identify any bottlenecks.” The assistant uses get_topology_statistics to analyze network architecture and identify potential issues. Prompt: “Show me all wired connections and their link speeds, highlighting any that aren’t running at 1 Gbps.” Combines get_device_connections with filtering to identify underperforming network links. Prompt: “Export all my site configurations for documentation purposes.” The assistant uses export_site_config to generate comprehensive site documentation.

Troubleshooting & Monitoring

Prompt: “Why is my AP-Hallway showing as offline in the topology? Show me its last known connection details.” The assistant uses get_network_topology and filters for the specific device to diagnose connectivity issues. Prompt: “Latency spikes whenever the link is busy. Is Smart Queue turned on, and are its rates set below my line speed?” Uses get_smart_queue_status to check the WAN’s shaper state and rates. Prompt: “Which backup is most recent and has it been tested for restore capability?” The assistant uses list_backups and get_backup_details to validate backup integrity. Prompt: “Show me all RADIUS authentication failures in the last 24 hours.” Uses list_radius_accounts or authentication logs to troubleshoot access issues.

Combining Multiple Operations

Prompt: “I’m setting up a new branch office. Create a site, configure basic networks, enable Smart Queues on the WAN, enable RADIUS authentication, and schedule daily backups.” The assistant will orchestrate multiple MCP tools:
  1. create_site - Provision the site
  2. create_network - Set up VLANs
  3. configure_smart_queue - Enable WAN Smart Queues
  4. create_radius_profile - Enable 802.1X authentication
  5. configure_backup_schedule - Automate backups
Prompt: “Audit my network: show me topology depth, check Smart Queue status, count clients per site, check backup status, and verify all RADIUS servers are reachable.” Demonstrates complex multi-tool queries combining:
  • get_topology_statistics
  • get_smart_queue_status
  • aggregate_client_stats
  • list_backups + get_backup_status
  • list_radius_profiles

Best Practices for Prompts

  1. Be Specific: Instead of “configure my network,” say “create a guest WiFi network with 4-hour timeout”
  2. Include Context: Mention site names, device IDs, IP ranges, or specific requirements
  3. Specify Timing: Use “right now,” “schedule for 2 AM,” or “during business hours”
  4. Request Validation: Ask for dry-run mode first: “Show me what would happen if…”
  5. Combine Related Tasks: Group related operations in a single prompt for better workflow
  6. Ask for Summaries: End with “and summarize the changes” to get a clear action report

API Changelog

Version 0.1.0 (Initial Release)

  • Basic device management (list, get, restart)
  • Network management (list, create)
  • Client management (list, block)
  • Firewall rule management (list, create)
  • Site-based resource URIs

Future Enhancements

Planned features for future releases:
  • Port forwarding management (✅ Phase 5)
  • WiFi network (SSID) management (✅ Phase 5)
  • DPI (Deep Packet Inspection) statistics (✅ Phase 5)
  • Webhook support for events (✅ Phase 5)
  • Redis caching for performance (✅ Phase 5)
  • Backup and restore operations (✅ v0.2.0 - Phase 4)
  • Bulk operations for devices
  • Advanced firewall rule management
  • VPN configuration
  • Alert and notification management

Support

For issues, questions, or contributions:
Last Updated: 2026-06-20 | API Version: v10.3.55
Last modified on November 8, 2026