Multi-Site Setup
mcp-unifi can manage more than one UniFi controller from a single server instance. Every tool accepts an optional controller: str = "default" parameter; that parameter names one of the controllers in your config and routes the call to it.
This is the architectural commitment that’s painful to retrofit. It’s the only feature that genuinely differentiates mcp-unifi from the competing UniFi MCP servers, and it’s also the one most homelab operators end up wanting once they have more than one site (home + parents’, home + office, etc).
Config sources, in priority order
Section titled “Config sources, in priority order”The server picks controller config from the first source that resolves:
MCP_UNIFI_CONTROLLERS_FILE— YAML file with a list of named controllers.- Legacy single-controller env vars (
UNIFI_HOST,UNIFI_API_KEY,UNIFI_PORT,UNIFI_SITE,UNIFI_VERIFY_SSL) auto-promoted tocontrollers=[ControllerConfig(name="default", ...)]. STUB_MODE=truewith no controller config: synthesizes a singledefaultstub controller.
Real mode with no controller config from any source is a hard validation error.
YAML config
Section titled “YAML config”Create a file (anywhere readable to the server process) and point MCP_UNIFI_CONTROLLERS_FILE at it:
- name: home host: 192.168.1.1 api_key: <home-api-key> port: 443 site: default verify_ssl: false
- name: office host: 10.0.0.1 api_key: <office-api-key> port: 443 site: default verify_ssl: falseA dict-with-key form is also accepted:
controllers: - name: home host: 192.168.1.1 api_key: <home-api-key> - name: office host: 10.0.0.1 api_key: <office-api-key>Per-controller fields
Section titled “Per-controller fields”| Field | Required | Default | Notes |
|---|---|---|---|
name |
yes | — | Stable identifier used by tools. No duplicates allowed. |
host |
yes | — | UniFi gateway IP or hostname (no scheme). |
api_key |
yes, or api_key_file |
— | Local API key from the gateway. Wrapped in SecretStr internally; never logged. |
api_key_file |
no | — | Path to a file containing the API key (a secret mount). Wins over api_key when both are set, and turns verify_ssl on by default. access_api_key_file and os_password_file work the same way for those secrets. |
port |
no | 443 |
HTTPS port for the gateway. |
site |
no | default |
UniFi controller site name. Most setups have one site called default. |
verify_ssl |
no | false (true with api_key_file or pinned_cert) |
Set true if the gateway has a real TLS certificate. An explicit value always wins over the default. |
pinned_cert |
no | — | Path to this console’s own certificate, recorded with mcp-unifi-pin-cert. Makes it the only certificate the server accepts from this controller; a mismatch fails closed. See the configuration reference. |
Use it from a tool call
Section titled “Use it from a tool call”Every tool accepts controller="<name>":
list_devices(controller="home")list_devices(controller="office")create_vlan(name="iot", vlan_id=50, subnet="10.50.0.0/24", controller="home", dry_run=True)If controller is omitted, the call routes to the controller named default. When no controller has that name, the alias resolves to MCP_UNIFI_DEFAULT_CONTROLLER if it is set, or to the only controller when exactly one is configured. With several controllers and no default named, every call must pass controller= explicitly: the server never silently picks the first entry, because a forgotten argument on a multi-site deployment must not write to whichever site happens to be listed first.
Docker example
Section titled “Docker example”The HTTP transport requires a bearer token — see the Authentication guide. The examples below mint one with openssl rand -hex 32 and pass it as MCP_UNIFI_AUTH_TOKENS.
export MCP_UNIFI_TOKEN=$(openssl rand -hex 32)
# Mount the YAML and point MCP_UNIFI_CONTROLLERS_FILE at itdocker run --rm -p 3714:3714 \ -e STUB_MODE=false \ -e MCP_UNIFI_CONTROLLERS_FILE=/etc/mcp-unifi/controllers.yaml \ -e MCP_UNIFI_AUTH_TOKENS="$MCP_UNIFI_TOKEN" \ -v ./controllers.yaml:/etc/mcp-unifi/controllers.yaml:ro \ ghcr.io/pete-builds/mcp-unifi:latestStub mode with two controllers
Section titled “Stub mode with two controllers”Stub mode honors the YAML config too. This is useful for testing multi-site flows before pointing at real hardware:
- name: home host: stub-home api_key: stub- name: office host: stub-office api_key: stubexport MCP_UNIFI_TOKEN=$(openssl rand -hex 32)
docker run --rm -p 3714:3714 \ -e STUB_MODE=true \ -e MCP_UNIFI_CONTROLLERS_FILE=/etc/mcp-unifi/controllers.yaml \ -e MCP_UNIFI_AUTH_TOKENS="$MCP_UNIFI_TOKEN" \ -v ./controllers.yaml:/etc/mcp-unifi/controllers.yaml:ro \ ghcr.io/pete-builds/mcp-unifi:latestEach controller gets its own in-memory state. Creating a VLAN on home does not affect office.
In values.yaml, mount an existingSecret containing the API keys and a ConfigMap containing the YAML. The chart’s values.yaml.unifi.* block is the simple single-controller path; for multi-site, prefer the existingSecret + ConfigMap pattern and mount the YAML into the pod. See the chart’s templates/ directory for the volume hooks.