The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Unifi MCP Server listing page.
A Model Context Protocol (MCP) server that exposes the UniFi Network Controller API today and is evolving into a production-grade multi-domain platform for Protect, Access, and enterprise-scale orchestration.
See SPEC.md for the architecture target and DEVELOPMENT_PLAN.md for the phase roadmap.
Operator quick start
Objective
Give operators a fast, safe reading order for understanding what the server does today, what it is becoming, and which docs govern rollout decisions.
Prerequisites
You know which UniFi API mode the deployment uses: local, cloud-ea, or cloud-v1.
You know whether the runtime is stdio, HTTP, SSE, or streamable HTTP.
You have read the phase target in SPEC.md and the current work item in DEVELOPMENT_PLAN.md.
Procedure
Confirm the current stable release and current phase focus.
Read SPEC.md for architecture intent and DEVELOPMENT_PLAN.md for sequencing.
Use API.md and docs/UNIFI_API.md for implementation surface details.
Use the phase runbooks in NETWORK_PLAYBOOK.md, HARBOR_SETUP.md, MULTI_CONTROLLER.md, METRICS.md, WEBHOOK_SETUP.md, and A2A.md when operating or extending phase 5 systems.
For release work, consult RELEASE_CHECKLIST.md and docs/RELEASE_PROCESS.md before tagging or publishing.
Verification
The chosen API mode matches the runtime configuration.
The current phase and the documented roadmap agree.
The operator can point to the correct runbook before making a change.
Rollback
If the selected runbook does not match the deployed capability, stop and reconcile docs before changing production state.
Common failure modes
README claims outrun the codebase.
Operators follow phase language without checking the specific runbook.
Release or rollout decisions are made from the README alone instead of the canonical docs.
📋 Version Notice
Current Stable Release: 0.2.5 (May 1, 2026) 🎉
Installation:
Terminal
pip install unifi-mcp-server
Roadmap focus:
Phase 3: native Protect API integration (camera/NVR/device/view/event read tools and resources now wired; PTZ and media streams still in progress)
Phase 4: testing, polish, minor gaps, runbooks, skills, and developer workflow hardening
Phase 5: multi-controller orchestration, dry-run, RBAC, audit logging, metrics, A2A, webhooks, Access API work, and tool exposure profiles
Limited to aggregate statistics - UniFi stable v1 cloud API.
✅ Site Information: List sites with aggregate statistics (device counts, client counts, bandwidth)
⚠️ No Individual Device/Client Access: Cannot query specific devices or clients
⚠️ No Configuration Changes: Cannot modify networks, firewall rules, or settings
⚙️ Configuration: UNIFI_API_TYPE=cloud-v1
📊 Rate Limit: 10,000 requests/minute
💡 Recommendation: Use Local Gateway API (UNIFI_API_TYPE=local) for full functionality. Cloud APIs are suitable only for high-level monitoring dashboards.
🔌 Transport Modes
The UniFi MCP Server supports multiple transport modes for different deployment scenarios:
STDIO (Default) ✅
Local subprocess communication — Best for Claude Desktop, Cursor, and local AI clients.
✅ Default mode: No configuration needed
✅ Zero network overhead: Direct stdin/stdout communication
✅ No port required: Runs as a subprocess of the MCP client
Network-accessible HTTP server — legacy transport, kept for backward compatibility.
⚠️ Known issue: client proxies such as mcp-remote can send the first tool call before the SSE initialize handshake finishes, which the MCP SDK rejects with Received request before initialization was complete (see #96). This is a timing issue in the SSE transport itself (upstream in the mcp SDK / client proxy, not this server's tool logic), so it cannot be fixed from this codebase.
✅ Network access: Connect from any MCP client over HTTP
✅ MCP gateway compatible: Works with MCP gateways that consolidate servers
Streamable HTTP 🌐 ✅ Recommended for network access
Modern HTTP transport — the current MCP transport standard, and the successor to SSE.
✅ Network access: Connect from any MCP client over HTTP
✅ MCP gateway compatible: Works with MCP gateways that consolidate servers
✅ No SSE handshake race: session initialization is part of the same request/response cycle, avoiding the class of timing issue SSE has with proxies like mcp-remote
⚠️ Authentication is required for network transports. The MCP endpoint exposes every
registered tool, including destructive ones. http, sse, and streamable_http will refuse
to start unless MCP_AUTH_TOKEN is set; clients then send Authorization: Bearer <token>.
The server binds to 127.0.0.1 by default — terminate TLS and authenticate at a reverse proxy
before widening MCP_SERVER_HOST to 0.0.0.0.
💡 Recommendation: Use STDIO for local AI clients (Claude Desktop, Cursor). Use Streamable HTTP when running behind an authenticating MCP gateway or reverse proxy — prefer it over SSE, which is kept only for backward compatibility.
🧭 Tool Exposure Profiles
To reduce context-window bloat, the server will support named exposure profiles that register only the tools relevant to a given UniFi application area.
protect — cameras, NVRs, devices, views, events, talkback, and Protect workflows (read surfaces wired; PTZ/media streams still in progress)
access — doors, readers, credentials, visitors, and access-control workflows
talk — UniFi Talk devices, calls, lines, and telephony workflows
drive — UniFi Drive storage, files, sharing, and drive workflows
read-only — get_*, list_*, stat_*, and search_* tools only
Intended behavior
Keep the full tool surface available when no profile is selected
Expose fewer tools per session so agents do not carry unrelated UniFi modules in context
Make the server easier to use in application-specific deployments and focused agent workflows
Pair with UNIFI_PROFILE so profile selection is explicit and repeatable
Running in Streamable HTTP Mode (recommended for network access)
server.ts
# Set transport to Streamable HTTP
export MCP_SERVER_TRANSPORT=streamable_http
export MCP_SERVER_PORT=3000# Required — the server refuses to start a network transport without it
export MCP_AUTH_TOKEN=$(openssl rand -hex 32)
# Start the server (binds to 127.0.0.1 by default)
unifi-mcp-server
# Server listening on 127.0.0.1:3000 via streamable_http
# Clients send: Authorization: Bearer $MCP_AUTH_TOKEN
Docker Compose for Streamable HTTP Mode
docker-compose.yml
services:
unifi-mcp:
image: ghcr.io/enuno/unifi-mcp-server:latest
environment:
UNIFI_API_KEY: your-api-key
UNIFI_API_TYPE: local
UNIFI_LOCAL_HOST: 192.168.2.1
MCP_SERVER_TRANSPORT: streamable_http
MCP_SERVER_PORT: 3000
MCP_SERVER_HOST: 0.0.0.0 # container-internal; keep the published port on loopback
MCP_AUTH_TOKEN: ${MCP_AUTH_TOKEN} # required — clients send Authorization: Bearer <token>
ports:
# Published on loopback; put an authenticating TLS proxy in front to expose it further.
- "127.0.0.1:3000:3000"
Connecting via MCP Gateway
Once running in Streamable HTTP mode, configure your MCP gateway to connect:
Read-Only Mode: Set UNIFI_READ_ONLY=true to register only non-mutating tools — state-changing tools are then absent from the MCP tool list entirely, rather than relying on a caller-supplied confirm flag
Confirmation Required: All mutating operations require explicit confirm=True flag
Dry-Run Mode: Planned change-safe preview path for all write and destructive operations
Audit Logging: Planned append-only audit trail for mutation paths
Tool Scoping: Planned API-key-based RBAC for least-privilege access
Input Validation: Comprehensive parameter validation with detailed error messages
Password Masking: Sensitive data automatically masked in logs
Type-Safe: Full type hints and Pydantic validation throughout
Security Scanners: CodeQL, Trivy, Bandit, Safety, and detect-secrets integration
Technical Excellence
Async Support: Built with async/await for high performance and concurrency
MCP Protocol: Standard Model Context Protocol for AI agent integration
Comprehensive Testing: 1,236 tests with high coverage, all passing across Python 3.10–3.13
UniFi API key (obtain from Settings → Control Plane → Integrations)
Access to UniFi Cloud API or local gateway
Installation
Using PyPI (Recommended)
The UniFi MCP Server is published on PyPI and can be installed with pip or uv:
bash
# Install from PyPI
pip install unifi-mcp-server
# Or using uv (faster)
uv pip install unifi-mcp-server
# Install specific version
pip install unifi-mcp-server==0.2.5
After installation, the unifi-mcp-server command will be available globally.
# Copy example configuration
cp .env.example .env
# Edit .env with your UniFi credentials
# Required: UNIFI_API_KEY
# Recommended: UNIFI_API_TYPE=local, UNIFI_LOCAL_HOST=<gateway-ip>
4. Run Tests
bash
# Run all unit tests
pytest tests/unit/ -v
# Run with coverage report
pytest tests/unit/ --cov=src --cov-report=html --cov-report=term-missing
# View coverage report
open htmlcov/index.html # macOS
# Or: xdg-open htmlcov/index.html # Linux
5. Run the Server
bash
# Development mode with MCP Inspector
uv run mcp dev src/main.py
# Production mode
uv run python -m src.main
# The MCP Inspector will be available at http://localhost:5173
# Build for current architecture
docker build -t unifi-mcp-server:0.2.0 .
# Build multi-architecture (requires buildx)
docker buildx create --use
docker buildx build \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
-t ghcr.io/enuno/unifi-mcp-server:0.2.0 \
--push .
# Test the image
docker run -i --rm \
-e UNIFI_API_KEY=your-key \
-e UNIFI_API_TYPE=cloud \
unifi-mcp-server:0.2.0
Publishing
Publish to PyPI
bash
# Install twine
uv pip install twine
# Check distribution
twine check dist/*
# Upload to PyPI (requires PyPI account and token)
twine upload dist/*
# Or upload to Test PyPI first
twine upload --repository testpypi dist/*
Publish to npm (Metadata Wrapper)
bash
# Ensure package.json is up to date
cat package.json
# Login to npm (if not already)
npm login
# Publish package
npm publish --access public# Verify publication
npm view unifi-mcp-server
See docs/RELEASE_PROCESS.md for the complete release workflow, including automated GitHub Actions, manual PyPI/npm publishing, and MCP registry submission.
Navigate to Settings → Control Plane → Integrations
Click Create API Key
Save the key immediately - it's only shown once!
Store it securely in your .env file
Configuration File
Create a .env file in the project root:
env
# Required: Your UniFi API Key
UNIFI_API_KEY=your-api-key-here
# API Mode Selection (choose one):
# - 'local': Full access via local gateway (RECOMMENDED)
# - 'cloud-ea': Early Access cloud API (limited to statistics)
# - 'cloud-v1': Stable v1 cloud API (limited to statistics)
UNIFI_API_TYPE=local
# Local Gateway Configuration (for UNIFI_API_TYPE=local)
UNIFI_LOCAL_HOST=192.168.2.1
UNIFI_LOCAL_PORT=443
UNIFI_LOCAL_VERIFY_SSL=false# Cloud API Configuration (for cloud-ea or cloud-v1)
# UNIFI_CLOUD_API_URL=https://api.ui.com
# Site Manager API (cloud-ea only, optional)
# UNIFI_SITE_MANAGER_ENABLED=true
# Optional settings
UNIFI_DEFAULT_SITE=default# Redis caching (optional - improves performance)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0# REDIS_PASSWORD=your-password # If Redis requires authentication
# Webhook support (optional - for real-time events)
WEBHOOK_SECRET=your-webhook-secret-here
# Performance tracking with agnost.ai (optional - for analytics)
# Get your Organization ID from https://app.agnost.ai
# AGNOST_ENABLED=true
# AGNOST_ORG_ID=your-organization-id-here
# AGNOST_ENDPOINT=https://api.agnost.ai
# AGNOST_DISABLE_INPUT=false # Set to true to disable input tracking
# AGNOST_DISABLE_OUTPUT=false # Set to true to disable output tracking
# Supermemory (optional - operator notes/context storage, scoped per site)
# Requires: pip install supermemory
# Get your API key from https://console.supermemory.ai
# SUPERMEMORY_ENABLED=true
# SUPERMEMORY_API_KEY=your-supermemory-api-key-here
See .env.example for all available options.
Running the Server
bash
# Development mode with MCP Inspector
uv run mcp dev src/main.py
# Production mode
uv run python src/main.py
The MCP Inspector will be available at http://localhost:5173 for interactive testing.
Usage
With Claude Desktop
Add to your Claude Desktop configuration (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
Option 1: Using PyPI Package (Recommended)
After installing via pip install unifi-mcp-server:
Important: Do NOT use -d (detached mode) in MCP client configurations. The MCP client needs to maintain a persistent stdin/stdout connection to the container.
With Cursor
Add to your Cursor MCP configuration (mcp.json via "View: Open MCP Settings → New MCP Server"):
Option 1: Using PyPI Package (Recommended)
After installing via pip install unifi-mcp-server:
The repo ships a SKILL.md and four categorized skill files in skills/ that let AI agents load UniFi context on-demand — without keeping all 215+ tool definitions in the LLM context for every conversation.
Install the skill
bash
# Personal skill (available in all Claude Code sessions)
cp SKILL.md ~/.claude/skills/unifi.md
# Or install all four domain skills individually
cp skills/unifi-network.md ~/.claude/skills/
cp skills/unifi-devices.md ~/.claude/skills/
cp skills/unifi-security.md ~/.claude/skills/
cp skills/unifi-system.md ~/.claude/skills/
Once installed, Claude Code will automatically reference the skill when you ask about UniFi topics, without loading the full MCP server into every conversation.
Scoped MCP profiles (reduce context footprint)
You can run the MCP server with only the tools you need by setting UNIFI_PROFILE:
UNIFI_DEFAULT_SITE: Default site ID (default: default)
UNIFI_SITE_MANAGER_ENABLED: Enable Site Manager multi-site tools for cloud-ea (default: false)
Tool Scope (reduces LLM context size):
UNIFI_PROFILE: Load only a subset of tools — network, devices, security, system, or minimal (default: all tools)
MCP Server Transport:
MCP_SERVER_TRANSPORT: Transport mode (stdio, sse, http, streamable_http; default: stdio)
MCP_SERVER_HOST: Bind address for network transports (default: 127.0.0.1)
MCP_SERVER_PORT: Server port (default: 3000)
MCP_AUTH_TOKEN: Bearer token required for network transports; comma-separate for several (default: unset — network transports refuse to start without it)
Programmatic Usage
server.ts
from mcp import MCP
import asyncio
async def main():
mcp = MCP("unifi-mcp-server")
# List all devices
devices = await mcp.call_tool("list_devices", {
"site_id": "default"
})
for device in devices:
print(f"{device['name']}: {device['status']}")
# Get network information via resource
networks = await mcp.read_resource("sites://default/networks")
print(f"Networks: {len(networks)}")
# Create a guest WiFi network with VLAN isolation
wifi = await mcp.call_tool("create_wlan", {
"site_id": "default",
"name": "Guest WiFi",
"security": "wpapsk",
"password": "GuestPass123!",
"is_guest": True,
"vlan_id": 100,
"confirm": True # Required for safety
})
print(f"Created WiFi: {wifi['name']}")
# Get DPI statistics for top bandwidth users
top_apps = await mcp.call_tool("list_top_applications", {
"site_id": "default",
"limit": 5,
"time_range": "24h"
})
for app in top_apps:
gb = app['total_bytes'] / 1024**3
print(f"{app['application']}: {gb:.2f} GB")
# Create Zone-Based Firewall zones (UniFi Network 9.0+)
lan_zone = await mcp.call_tool("create_firewall_zone", {
"site_id": "default",
"name": "LAN",
"description": "Trusted local network",
"confirm": True
})
iot_zone = await mcp.call_tool("create_firewall_zone", {
"site_id": "default",
"name": "IoT",
"description": "Internet of Things devices",
"confirm": True
})
# Set zone-to-zone policy (LAN can access IoT, but IoT cannot access LAN)
await mcp.call_tool("update_zbf_policy", {
"site_id": "default",
"source_zone_id": lan_zone["_id"],
"destination_zone_id": iot_zone["_id"],
"action": "accept",
"confirm": True
})
asyncio.run(main())
API Documentation
See API.md for complete API documentation, including:
# Run all tests
pytest tests/unit/
# Run with coverage report
pytest tests/unit/ --cov=src --cov-report=html --cov-report=term-missing
# Run specific test file
pytest tests/unit/test_zbf_tools.py -v
# Run tests for the current feature set
pytest tests/unit/test_new_models.py tests/unit/test_zbf_tools.py tests/unit/test_traffic_flow_tools.py
# Run only unit tests (fast)
pytest -m unit
# Run only integration tests (requires UniFi controller)
pytest -m integration
Current Test Coverage:
1,236 tests passing across Python 3.10-3.13
Coverage and module-level reporting are tracked in Codecov and CI
Module-specific targets are maintained in DEVELOPMENT_PLAN.md and the test suite
# Format code
black src/ tests/
isort src/ tests/
# Lint code
ruff check src/ tests/ --fix
# Type check
mypy src/
# Run all pre-commit checks
pre-commit run --all-files
Testing with MCP Inspector
bash
# Start development server with inspector
uv run mcp dev src/main.py
# Open http://localhost:5173 in your browser