Skip to content

Configuration

All configuration is read from environment variables (and a .env file when present). Pydantic validates values at startup; invalid config fails fast with a clear message.

Variable Type Default Required Notes
STUB_MODE bool true no When false, real-mode controller config is required (either legacy env vars or MCP_UNIFI_CONTROLLERS_FILE).

These env vars cover the single-controller case. When set without MCP_UNIFI_CONTROLLERS_FILE, they auto-promote to a one-entry controller list named default. v0.4.x users keep working unchanged.

Variable Type Default Required Notes
UNIFI_HOST string "" real mode Gateway IP or hostname (no scheme).
UNIFI_API_KEY string "" real mode Local API key from Settings → Control Plane → Integrations.
UNIFI_PORT int (1-65535) 443 no HTTPS port for the gateway.
UNIFI_SITE string default no Controller site identifier. Most setups have one site.
UNIFI_VERIFY_SSL bool false no Set true once the gateway has a real TLS certificate.
Variable Type Default Required Notes
MCP_UNIFI_CONTROLLERS_FILE path (unset) no YAML file listing named controllers. When set, the legacy UNIFI_* vars are ignored. See the Multi-Site Setup guide for the schema.
Variable Type Default Required Notes
MCP_UNIFI_MODULES_ENABLED CSV string network no Modules to load. Known values: network, protect, access. Unknown values fail startup with UnknownModuleError. Access requires UNIFI_ACCESS_HOST and UNIFI_ACCESS_API_KEY in real mode.

Only required when MCP_UNIFI_MODULES_ENABLED includes access and STUB_MODE=false. See the Access setup guide.

Variable Type Default Required Notes
UNIFI_ACCESS_HOST string "" access + real mode Access hub IP or hostname. Often the same as UNIFI_HOST.
UNIFI_ACCESS_API_KEY string "" access + real mode Access API key. Separate from the Network API key; generated on the Access controller’s developer settings.
UNIFI_ACCESS_PORT int (1-65535) 12445 no HTTPS port for the Access hub (the direct Access app port).
Variable Type Default Required Notes
MCP_TRANSPORT enum (stdio, streamable-http) streamable-http no stdio for Claude Desktop / uvx / .dxt installs; streamable-http for the long-running container.
MCP_HOST string 0.0.0.0 no Bind address (Streamable HTTP only).
MCP_PORT int (1-65535) 3714 no Listen port (Streamable HTTP only).

The Streamable HTTP transport is secure by default. See the Authentication guide for the full model.

Variable Type Default Required Notes
MCP_UNIFI_AUTH_REQUIRED bool true no When true, the HTTP transport refuses to start without tokens. Set to false only on a loopback-bound single-host deployment. Stdio transport ignores this.
MCP_UNIFI_AUTH_TOKENS CSV string "" HTTP + auth on Comma-separated bearer tokens. Each entry is one of: a bare token (auto-assigned client-N), a name:token pair (recommended — name shows up in the audit log), or a name:token:module1|module2 triple to scope a client to specific modules. Pipe-separated because comma is the entry delimiter. Known modules: network, protect, access; * means all.
Variable Type Default Required Notes
MCP_UNIFI_READONLY bool false no When true, every tool that changes state is hidden from tools/list and refused on tools/call. Applies to both transports. Refusals come back as the standard {"error": ..., "stub_mode": ...} envelope, so callers need no new error handling. See the Security guide.

Read-only mode is defense in depth on top of a read-only UniFi API key, not a replacement for one. The key decides what the controller will accept; this setting decides what the server is willing to attempt.

Classification is per tool and explicit: each tool declares mutates=True or mutates=False at registration, and the server refuses to start if any registered tool has not declared one. It is not inferred from tool names — twelve mutating tools (confirm_destructive_action, restore_config, block_client, restart_device, locate_device, trigger_speedtest, and others) carry no create_/update_/delete_/set_ prefix, so a name-based gate would leave them callable.

Variable Type Default Required Notes
LOG_LEVEL enum INFO no One of DEBUG, INFO, WARNING, ERROR, CRITICAL.
LOG_FORMAT enum (json, text) json no json for production, text for local dev. Logs go to stderr (stdout is reserved for stdio JSON-RPC framing).
Variable Type Default Required Notes
MCP_UNIFI_AUDIT_SINK enum file no One of file, stdout, syslog.
MCP_UNIFI_AUDIT_PATH path audit.jsonl no File path when SINK=file. Relative paths resolve from the process CWD.
MCP_UNIFI_AUDIT_SYSLOG_ADDRESS string /dev/log no Socket path or host:port when SINK=syslog.

See the Dry-Run & Audit Log guide for the JSONL record schema and mcp-unifi-replay usage.

These shape the subnet and DHCP range that create_iot_network synthesizes when callers don’t specify them.

Variable Type Default Required Notes
IOT_SUBNET_TEMPLATE string 10.0.{vlan_id}.0/24 no Subnet template. Must contain the literal {vlan_id} placeholder.
IOT_DHCP_START_OFFSET int (2-254) 100 no First DHCP lease offset within the IoT /24.
IOT_DHCP_STOP_OFFSET int (2-254) 200 no Last DHCP lease offset. Must be greater than IOT_DHCP_START_OFFSET.
  • Pydantic validates types and ranges at startup.
  • Invalid values fail with a clear message that names the field.
  • Real mode with no controller config (no MCP_UNIFI_CONTROLLERS_FILE, no legacy UNIFI_HOST + UNIFI_API_KEY) is a hard error.
  • Duplicate controller names in the YAML are rejected.
  • IOT_DHCP_STOP_OFFSET <= IOT_DHCP_START_OFFSET is rejected.
  • IOT_SUBNET_TEMPLATE without the {vlan_id} placeholder is rejected.

The server reads .env from the process CWD if present. Values in the actual environment override .env. The repo’s .env.example is a good starting template.

On startup, the server logs a safe_repr() of its resolved config. Per-controller api_key values are never included; each controller gets an api_key_set: true/false boolean instead. Grep startup logs for safe_repr to confirm what the server actually loaded.