UniFi MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@UniFi MCP Serverlist all devices on the network"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
UniFi MCP Server
MCP server providing Claude Code with full UniFi network management capabilities -- devices, clients, ports, bandwidth auditing, firewall policies, and traffic rules -- all through natural language.
Overview
The UniFi MCP Server connects Claude Code to one or more UniFi network controllers via the UDM REST API. It exposes 19 tools that let you query, audit, and configure your network infrastructure conversationally.
Key capabilities:
Self-healing authentication -- automatic re-auth on expiry or 401, with proactive refresh and mutex protection against concurrent auth storms
Automated TOTP -- generates MFA codes from a stored seed, no manual authenticator app interaction
19 management tools covering devices, clients, ports, port profiles, networks, bandwidth auditing, traffic routes, and firewall policies
Encrypted session persistence using AES-256-GCM
Resilient API client with 429 throttle handling, 5xx exponential backoff, and 401 transparent re-auth
Dual-controller support -- cloud-hosted controllers (two-step MFA) and UDM Pro Max (single-step MFA)
Architecture
graph LR
CC[Claude Code] <-->|stdio| MCP[MCP Server<br/>StdioServerTransport]
MCP --> Tools[19 Tools<br/>Zod-validated inputs]
Tools --> UC[UniFi Client<br/>Axios + retry interceptors]
UC --> SM[Session Manager<br/>Self-healing auth]
SM --> SC[Session Cache<br/>AES-256-GCM]
SM --> TOTP[TOTP Generator<br/>RFC 6238]
UC -->|HTTPS| API[UniFi Controller<br/>REST API]
style CC fill:#4A90D9,color:#fff
style MCP fill:#6C5CE7,color:#fff
style Tools fill:#00B894,color:#fff
style UC fill:#FDCB6E,color:#000
style SM fill:#E17055,color:#fff
style API fill:#2D3436,color:#fffFeatures
Self-Healing Authentication
The server never requires manual re-authentication during operation. The session manager implements a multi-layered strategy:
Layer | Trigger | Behavior |
Proactive refresh | Session < 15 min remaining | Fire-and-forget re-auth in the background; current request uses existing session |
Expired session | Session TTL = 0 | Blocking re-auth before the request proceeds |
401 retry | Controller returns HTTP 401 | Interceptor triggers re-auth, then transparently retries the original request |
Mutex protection | Concurrent requests during auth | All callers await the same in-flight auth promise (no auth storms) |
Failure circuit breaker | 3 consecutive auth failures | Auth disabled until server restart (prevents credential lockout) |
Two-Step MFA with Automated TOTP
Cloud-hosted UniFi controllers require two-step MFA:
sequenceDiagram
participant S as MCP Server
participant C as UniFi Controller
participant T as TOTP Generator
S->>C: POST /api/auth/login {username, password}
C-->>S: 200 MFA_AUTH_REQUIRED + UBIC_2FA cookie
S->>T: Generate TOTP from base32 seed
T-->>S: 6-digit code
S->>C: POST /api/auth/login {username, password, token}<br/>Cookie: UBIC_2FA=...
C-->>S: 200 + Set-Cookie: TOKEN=jwt...
S->>S: Parse JWT for CSRF token + expiry
S->>S: Encrypt and persist session to diskFor UDM Pro Max controllers, authentication completes in a single step (username + password + TOTP in one request).
Resilient API Client
Every API call passes through axios interceptors that handle transient failures:
429 Too Many Requests -- waits for
Retry-Afterheader (or 5s default), then retries5xx Server Error -- exponential backoff (1s, 2s, 4s) up to 3 retries
401 Unauthorized -- triggers self-healing re-auth, refreshes headers, retries the request once
Tools Reference
Device Tools (3)
Tool | Description | Key Parameters |
| List all devices (switches, APs, gateways) with name, model, IP, state, firmware |
|
| Full detail for a single device by MAC address, including port count and uptime |
|
| All ports on a switch with status, speed, PoE, connected MACs, and assigned port profile |
|
Client Tools (3)
Tool | Description | Key Parameters |
| List active clients connected to the network |
|
| Detailed info for a specific client by MAC |
|
| All known clients including offline (historical database) |
|
Port Profile Tools (6 -- 2 read, 4 write)
Tool | Description | Key Parameters |
| List all port profiles (bandwidth/VLAN configs) | -- |
| Full raw detail for a port profile by ID |
|
| Create a new port profile with bandwidth limits |
|
| Update an existing profile (fetch-then-merge) |
|
| Delete a port profile (fails if ports still assigned) |
|
| Assign a port profile to a specific switch port |
|
All write tools require confirm: true to execute. Set confirm: false for a dry-run preview of what would change.
Network Tools (2)
Tool | Description | Key Parameters |
| List all configured networks (VLANs, corporate, guest) | -- |
| Full configuration for a specific network |
|
Audit Tools (1)
Tool | Description | Key Parameters |
| Per-room bandwidth audit across all switches. Shows port name, bandwidth limits, active status, connected device count. Designed for cross-referencing with billing systems. |
|
Route & Firewall Tools (2)
Tool | Description | Key Parameters |
| List traffic routes/rules with bandwidth shaping data (raw response) | -- |
| List firewall policies from v2 API including traffic rules (raw response) | -- |
Utility Tools (2)
Tool | Description | Key Parameters |
| Check authentication status, session expiry, and controller connectivity | -- |
| Generic API escape hatch -- make any UniFi API call not covered by other tools |
|
Setup
Prerequisites
Node.js >= 20.0.0
Access to a UniFi controller (cloud-hosted or UDM Pro / UDM Pro Max)
TOTP seed from your authenticator app setup (for automated MFA)
WireGuard VPN (if targeting a UDM Pro / UDM Pro Max on a private LAN)
VPN Setup (WireGuard)
If your UniFi controller is a UDM Pro / UDM Pro Max on a private network, you need a WireGuard VPN tunnel to reach it. Cloud-hosted controllers are publicly accessible and do not require VPN.
Required routed subnets:
Your WireGuard AllowedIPs must include every subnet the MCP server and your tools need to reach:
Subnet | Purpose |
UDM management subnet | Controller API access (required) |
Switch management subnet | SSH access for device operations (if switches are on a separate subnet) |
Tenant/client subnets | Client data and VLAN networks (if querying client tools) |
Example WireGuard peer config:
[Peer]
PublicKey = <udm-public-key>
AllowedIPs = <udm-subnet>/24, <switch-subnet>/24, <tenant-supernet>/8
Endpoint = <udm-wan-ip>:51820Verifying connectivity:
# Check the tunnel routes all required subnets
wg show <tunnel-name> allowed-ips
# Verify controller reachable
ping <your-udm-ip>
# Verify switches reachable (if on a separate management subnet)
ping <switch-ip>Troubleshooting: devices unreachable but controller responds
The WireGuard Windows app stores tunnel configs internally. Editing the .conf file on disk does not update the running tunnel. If your config file has the correct AllowedIPs but wg show is missing a subnet:
Open the WireGuard app
Remove the tunnel
Import tunnel from file (re-import the updated
.conf)Activate the tunnel
Verify with wg show <tunnel-name> allowed-ips.
Installation
# Clone the repository
git clone <repo-url>
cd mcp-servers/unifi-mcp-server
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Edit .env with your controller details (see Configuration section)
# Build
npm run build
# Authenticate (first time)
npm run auth # Interactive mode
npm run auth -- --auto # Non-interactive (requires UNIFI_USERNAME, UNIFI_PASSWORD, UNIFI_TOTP_SEED)
# Start the server
npm run startClaude Code Configuration
Add the server to your Claude Code MCP settings (~/.claude/settings.json or project-level .mcp.json):
{
"mcpServers": {
"unifi": {
"command": "node",
"args": ["/path/to/unifi-mcp-server/dist/index.js"],
"env": {
"UNIFI_HOST": "your-controller-ip",
"UNIFI_SITE": "default"
}
}
}
}The server communicates over stdin/stdout using the MCP StdioServerTransport.
Configuration
All configuration is loaded from .env at the project root via Zod-validated schemas.
Core Settings
Variable | Required | Default | Description |
| Yes | -- | Hostname or IP of the UniFi controller |
| No |
| HTTPS port |
| No |
| UniFi site ID (visible in the controller URL) |
| No |
| Set |
Authentication
Variable | Required | Default | Description |
| For self-healing | -- | UniFi SSO or local admin username |
| For self-healing | -- | UniFi SSO or local admin password |
| For self-healing | -- | Base32-encoded TOTP secret from authenticator setup |
Session Storage
Variable | Required | Default | Description |
| No |
| Path to the encrypted session cache file |
| No | -- | 64-character hex key for AES-256-GCM encryption. Generate with: |
Logging
Variable | Required | Default | Description |
| No |
| Log verbosity: |
Webhook Push (scripts/webhook-push.ts only)
Variable | Required | Default | Description |
| For webhook | -- | Make.com or other webhook URL for monthly bandwidth audit |
| No | -- | WireGuard tunnel name for automatic VPN connect/disconnect |
| No | -- | IP to ping for VPN health check (e.g., your gateway behind the tunnel) |
| No |
| Number of retry attempts for webhook push |
| No |
| Initial delay between retries in ms (linear backoff: 1x, 2x, 3x) |
Authentication
How It Works
UniFi controllers use cookie-based authentication with JWT tokens. The server supports two authentication flows depending on the controller type:
Cloud-hosted controller (two-step MFA):
POST username + password to
/api/auth/login-- returnsUBIC_2FAcookieGenerate TOTP code from the stored base32 seed (RFC 6238, HMAC-SHA1, 30-second period, 6 digits)
POST username + password + TOTP code with the
UBIC_2FAcookie -- returnsTOKENinSet-CookieParse the JWT payload for the
csrfTokenandexp(expiry) claimsEncrypt and persist the session to disk
UDM Pro / UDM Pro Max (single-step):
POST username + password + TOTP code in a single request -- returns
TOKENinSet-CookieParse and persist as above
Initial Authentication
Run the auth script to establish the first session:
# Interactive -- prompts for username and password
npm run auth
# Non-interactive -- reads credentials from environment
npm run auth -- --autoThe --auto flag is used for scheduled tasks and CI environments. It requires UNIFI_USERNAME, UNIFI_PASSWORD, and UNIFI_TOTP_SEED to be set in .env.
Self-Healing Auth
When UNIFI_USERNAME, UNIFI_PASSWORD, and UNIFI_TOTP_SEED are all present in the environment, the server re-authenticates automatically without any manual intervention:
Proactive refresh: When the session has fewer than 15 minutes remaining, a background refresh is triggered (fire-and-forget; the current request continues with the existing session)
Expired re-auth: When the session has fully expired, re-auth blocks until complete before the request proceeds
401 retry: If the controller returns 401, the axios interceptor triggers re-auth, refreshes the Cookie and X-CSRF-Token headers, and retries the original request
Mutex: A promise-based mutex ensures concurrent requests share a single auth attempt
Circuit breaker: After 3 consecutive authentication failures, auth is disabled until the server is restarted (prevents credential lockout loops)
Scripts
webhook-push.ts
Automated monthly bandwidth audit push to a Make.com webhook. Designed to run as a scheduled task on the 1st of each month.
npx tsx scripts/webhook-push.tsWhat it does:
Connects WireGuard VPN (if
VPN_TUNNEL_NAMEis set)Authenticates to the UniFi controller with automated TOTP
Pulls all switch device data and builds a per-room bandwidth payload
POSTs the payload to the configured
WEBHOOK_URLDisconnects VPN
Retries up to
MAX_RETRIEStimes with linear backoff on failure
initial-auth.ts
Interactive or automated initial login to establish a session cache.
npm run auth # Interactive
npm run auth -- --auto # Non-interactiveauto-reauth.ts
Non-interactive re-authentication for use in scheduled tasks and CI. Generates TOTP automatically, saves the session to the encrypted cache.
npx tsx scripts/auto-reauth.ts # re-auth + save session
npx tsx scripts/auto-reauth.ts --quiet # suppress progress logs
npx tsx scripts/auto-reauth.ts --json # output token JSON to stdout for script chainingDevelopment
Commands
Command | Description |
| Compile TypeScript to |
| Watch mode ( |
| Run the compiled server ( |
| Run the initial auth script ( |
Project Structure
unifi-mcp-server/
├── src/
│ ├── index.ts # Server bootstrap (McpServer + StdioServerTransport)
│ ├── config/
│ │ └── index.ts # Zod-validated env config from .env
│ ├── auth/
│ │ ├── sessionManager.ts # Self-healing auth with proactive refresh + mutex
│ │ ├── sessionCache.ts # AES-256-GCM encrypted session persistence
│ │ └── totp.ts # RFC 6238 TOTP generator (zero external dependencies)
│ ├── unifi/
│ │ ├── unifiClient.ts # Axios client with 401/429/5xx retry interceptors
│ │ └── types.ts # TypeScript interfaces for UniFi API entities
│ ├── tools/
│ │ ├── index.ts # registerTools() — wires all tool modules
│ │ ├── devices.ts # 3 tools: device-list, device-detail, device-ports
│ │ ├── clients.ts # 3 tools: client-list, client-detail, client-history
│ │ ├── ports.ts # 6 tools: profiles CRUD + port-set-profile
│ │ ├── networks.ts # 2 tools: network-list, network-detail
│ │ ├── audit.ts # 1 tool: bandwidth-audit
│ │ ├── routes.ts # 2 tools: traffic-routes, firewall-policies
│ │ └── utility.ts # 2 tools: auth-status, generic api escape hatch
│ └── utils/
│ ├── errors.ts # Error hierarchy: UniFiError → Auth / Api / Config
│ ├── logger.ts # stderr logger with sensitive key redaction
│ └── formatters.ts # Entity formatters for clean LLM output
├── scripts/
│ ├── initial-auth.ts # Interactive/auto auth script
│ ├── auto-reauth.ts # Non-interactive re-auth for scheduled tasks
│ └── webhook-push.ts # Monthly bandwidth audit → webhook
├── data/ # Session cache (gitignored)
├── dist/ # Compiled output (gitignored)
├── .env # Environment variables (gitignored)
├── .env.example # Template with all variables documented
├── .gitignore
├── package.json
└── tsconfig.jsonAdding a New Tool
Create or edit a file in
src/tools/(group by entity type).Export a
register*Tools(server: McpServer)function:import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { unifiClient } from '../unifi/unifiClient.js'; import { formatErrorForMcp } from '../utils/errors.js'; export function registerMyTools(server: McpServer): void { server.tool( 'unifi-my-tool', 'Description of what this tool does.', { param: z.string().describe('What this parameter controls'), }, async ({ param }) => { try { const data = await unifiClient.get('/rest/endpoint'); return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }], }; } catch (error) { return { content: [{ type: 'text', text: formatErrorForMcp(error) }], isError: true, }; } } ); }Wire it into
src/tools/index.ts:import { registerMyTools } from './mytools.js'; // ... export function registerTools(server: McpServer): void { // ... existing registrations registerMyTools(server); }Build and test:
npm run build && npm run start
TypeScript Conventions
Target: ES2022 with NodeNext module resolution
ESM:
"type": "module"in package.json -- all imports must use.jsextensionsStrict mode: enabled (
"strict": truein tsconfig)Output:
dist/with declaration files and source maps
Security
Token Encryption
Session tokens are persisted to disk for survival across server restarts. When TOKEN_ENCRYPTION_KEY is set (64-character hex string = 32 bytes), the session cache is encrypted using AES-256-GCM with:
Random 16-byte IV per write
GCM authentication tag for tamper detection
Format:
iv_hex:auth_tag_hex:ciphertext_hex
If the encryption key is not set, sessions fall back to base64 encoding (a warning is logged on every save).
Generate a key:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Credential Handling
Credentials (
UNIFI_USERNAME,UNIFI_PASSWORD,UNIFI_TOTP_SEED) are read from environment variables only -- never stored in code or committed to version control.The logger automatically redacts any log context key containing
token,secret,password,credential,authorization,bearer,apikey,api_key, orcookie.All logs are written to stderr (stdout is reserved for MCP protocol communication).
Self-Signed SSL
UniFi controllers typically use self-signed certificates. By default, UNIFI_VERIFY_SSL=false disables certificate verification via https.Agent({ rejectUnauthorized: false }). Set to true only if your controller has a valid certificate from a trusted CA.
Dependencies
Package | Version | Purpose |
| ^1.12.0 | MCP server framework (McpServer, StdioServerTransport) |
| ^1.7.9 | HTTP client with interceptor support for retry logic |
| ^16.4.7 | Load |
| ^3.24.1 | Runtime schema validation for config and tool inputs |
Dev dependencies: typescript ^5.7.2, tsx ^4.19.2, @types/node ^22.10.2
Built by Element Zero
This project is built and maintained by Element Zero -- an AI-driven network automation company specializing in multi-tenant building infrastructure.
We use this MCP server daily to manage commercial building networks with 60+ tenant VLANs, automated bandwidth auditing, and billing integration. It's battle-tested in production.
If you manage UniFi networks for multi-tenant buildings, hotels, or coworking spaces -- we'd love to hear from you. Reach out at phil@ezero.ai.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/philipvanlewis/unifi-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server