Walk the user through configuring their UniFi Protect NVR connection. Ask one question at a time and wait for the answer before continuing.
Interaction Rules
Use the client target that matches the current agent runtime:
- Claude Code:
claude - Codex:
codex - OpenClaw:
openclaw
If the runtime is unclear, ask which client to configure. For questions, use the platform's blocking question tool when available (AskUserQuestion in Claude Code, request_user_input in Codex). If no blocking question tool is available, ask in chat with numbered options and wait for the user's reply.
This skill may come from a complete UniFi plugin or a standalone npx skills
install. Before using a helper script, verify that the plugin's scripts/
directory exists. If it does not, do not guess a relative path: follow the
canonical agent installation guide
for the client's native MCP registration path. That guide covers OpenCode,
Antigravity, Devin Desktop, Cursor, and other manual clients. A standalone
skill install does not install or register the MCP server by itself.
On macOS and Linux, resolve setup scripts relative to this skill file:
../../scripts/check-prereqs.sh../../scripts/set-env.sh
When the host exposes a plugin-root variable such as CLAUDE_PLUGIN_ROOT, using $CLAUDE_PLUGIN_ROOT/scripts/... is also valid. Do not assume the current shell directory is the plugin root.
On Windows, use ../../scripts/set-env.ps1 -Target <claude|codex|openclaw>
for every target. Use the matching PowerShell prerequisite checker. Both helpers
require uv/uvx and the selected client's CLI (claude, codex or openclaw). A working
Python 3.11+ on PATH is used first; otherwise uv supplies managed Python
(with a possible first-run download). No separate Python installation is needed.
Do not substitute direct client registration commands, which can expose env
values in process arguments.
Step 0: Check Prerequisites
Before asking for credentials, run the prereq checker for the current OS.
On macOS/Linux:
bash <path-to-plugin>/scripts/check-prereqs.sh --target <claude|codex|openclaw> "unifi-protect"
On Windows PowerShell:
powershell -ExecutionPolicy Bypass -File <path-to-plugin>/scripts/check-prereqs.ps1 -Target <claude|codex|openclaw> -PluginName "unifi-protect"
If the script exits non-zero, stop and report the error. Do not proceed to credentials.
Step 1: Controller Host
Ask: "What is your UniFi controller's IP address or hostname?" Example: 192.168.1.1.
If another UniFi MCP server is already configured, ask whether Protect is on the same controller. Inspect a sanitized summary of the existing host and variable names only. Do not print full settings or raw mcp list/get/show output: these can contain stored credentials.
Step 2: Credentials
Shared credentials can be reused deliberately; prefer explicit product-scoped providers when configuring this plugin. Apply the precedence rules below before assuming an existing shared credential will be selected.
Ask for the username, using a local admin account rather than a Ubiquiti SSO
account. Never ask the user to send the password in chat, and never place it in a
tool call or command argument. Ask for either
UNIFI_PROTECT_PASSWORD_FILE=<absolute-path> or
UNIFI_PROTECT_PASSWORD_COMMAND=<absolute argv>. A command provider can call a
Keychain, pass, or 1Password helper; it is not run through a shell and must not
prompt. If no indirect provider already exists, explain how to create one outside
the chat transcript or use a client-native masked secret UI, then wait. In Claude
Code that UI is the plugin's options dialog (/plugin, then unifi-protect, then
Configure options): its password and API key fields are masked and stored in
the system keychain.
All three servers apply the same precedence: non-empty UNIFI_<PRODUCT>_*
values override shared UNIFI_* values; empty values count as unset. This applies
to host, username and credentials even where bundled YAML defaults name different
variables. Shared providers are fallback only. Multiple non-empty spellings
(plain, _FILE, _COMMAND) at the selected level refuse startup.
The setup helper switches providers atomically: selecting one spelling removes
its saved siblings at the same level while preserving unrelated settings. To
remove an authentication path, pipe a JSON patch setting all three saved
spellings to null (for example UNIFI_PROTECT_PASSWORD,
UNIFI_PROTECT_PASSWORD_FILE, UNIFI_PROTECT_PASSWORD_COMMAND). Remove or
override shared fallback settings deliberately too; deleting product settings
alone can reactivate shared credentials. Inherited environment providers must
also be corrected in the launcher. Never delete a working provider in a separate
preparatory write.
Claude Code is the exception for a raw password or API key it holds in the
system keychain: it cannot clear one through claude plugin configure or the
options dialog. Setup therefore refuses, with nothing changed, any change that
would clear it, such as selecting a _FILE or _COMMAND provider for that
credential or a null deletion. The message names the variable and the choices:
keep the saved secret, or run claude plugin uninstall and claude plugin install with the plugin id it names (usually unifi-protect@unifi-plugins),
then rerun setup with the provider and the options the message lists.
Uninstalling clears every option of the plugin, so offer it to the user and
never run it without their consent.
Before changing or skipping API-key setup, remove obsolete
UNIFI_PROTECT_API_KEY, UNIFI_PROTECT_API_KEY_FILE, and
UNIFI_PROTECT_API_KEY_COMMAND spellings with null deletions in the same patch;
keep only the selected replacement, or delete all three when deselecting it. In
Claude Code, a keychain-held UNIFI_PROTECT_API_KEY cannot be removed this way;
see above.
AI-powered alarms need SuperAdmin. The alarm-rule tools (
protect_alarm_list_rules/protect_alarm_get_rule) surface AI-powered alarms from the UniFi-OS Alarm Manager only when the account is SuperAdmin; otherwise they return the classic automations view (with a_metanotice that AI alarms need SuperAdmin). A standard local admin runs every Protect tool fine. Mention SuperAdmin only if the user asks about AI alarms; don't require it for normal setup. On a combined UDM console, SuperAdmin also grants Network/UniFi-OS control — call that out so the user can decide.
Optional API Key
After collecting the username and password provider, explain that a UniFi Protect API key enables selected capabilities implemented through the Protect Integration API, including sensor settings, per-camera chime ring settings, and viewer liveview assignment. Ask whether to configure an API-key provider too.
If yes, configure UNIFI_PROTECT_API_KEY_FILE or
UNIFI_PROTECT_API_KEY_COMMAND; never ask for or pass the raw key. If no, skip it.
Step 3: Permission Configuration
Ask whether to enable write permissions. The guided setup explicitly disables all Protect mutations unless the user opts in.
For Codex and OpenClaw, offer:
- Read-only for now
- Enable camera management
- Enable all device management
- Custom categories
For Claude Code, the plugin's options hold only the server-level gates. Offer:
read-only (the default), allow updates, allow creates and updates, or allow
creates, updates and deletes. Set UNIFI_POLICY_PROTECT_CREATE,
UNIFI_POLICY_PROTECT_UPDATE and UNIFI_POLICY_PROTECT_DELETE to true or false,
and keep UNIFI_PROTECT_TOOL_PERMISSION_MODE=confirm so every mutation is previewed
first. Per-category overrides (UNIFI_POLICY_PROTECT_<CATEGORY>_<ACTION>) are not
available for Claude Code and setup refuses them. A user who wants only some
categories writable can allow the action server-wide and decline the previews
they do not want, or keep the action off.
For Codex and OpenClaw, before writing policy values, inspect a sanitized list of the selected client's
existing unifi-protect MCP environment variable names (for Codex and OpenClaw, the key names in
~/.config/unifi-mcp/protect.env).
Remove every existing category-specific
UNIFI_POLICY_PROTECT_<CATEGORY>_<ACTION> entry by including null deletions
in the same JSON patch, because those entries take precedence over server-level
defaults. Preserve unrelated variables. Set
UNIFI_PROTECT_TOOL_PERMISSION_MODE=confirm, then add back only the
category/action overrides the user selected.
For read-only setup, explicitly configure
UNIFI_POLICY_PROTECT_CREATE=false, UNIFI_POLICY_PROTECT_UPDATE=false, and
UNIFI_POLICY_PROTECT_DELETE=false. For requested writes on Codex or OpenClaw,
keep those server-level defaults and add only selected category/action overrides
using the existing
UNIFI_POLICY_PROTECT_<CATEGORY>_<ACTION>=true format.
Step 4: Write Configuration
On macOS/Linux, run the target-aware setup script with only values the user provided or selected:
bash <path-to-plugin>/scripts/set-env.sh --target <claude|codex|openclaw> \
UNIFI_PROTECT_HOST=<host> \
UNIFI_PROTECT_USERNAME=<username> \
'UNIFI_PROTECT_PASSWORD_FILE=<absolute-path>' \
UNIFI_PROTECT_TOOL_PERMISSION_MODE=confirm \
UNIFI_POLICY_PROTECT_CREATE=false \
UNIFI_POLICY_PROTECT_UPDATE=false \
UNIFI_POLICY_PROTECT_DELETE=false
Optional values keep their defaults unless the user asks: port
(UNIFI_PROTECT_PORT, default 443), SSL verification
(UNIFI_PROTECT_VERIFY_SSL, default false), and tool registration mode
(UNIFI_TOOL_REGISTRATION_MODE: lazy by default, eager or meta_only; the
Claude Code option is registration_mode).
Add optional values and policy variables to the same command. Per-category overrides apply to Codex and OpenClaw only, for example:
bash <path-to-plugin>/scripts/set-env.sh --target <codex|openclaw> \
UNIFI_PROTECT_HOST=<host> \
UNIFI_PROTECT_USERNAME=<username> \
'UNIFI_PROTECT_PASSWORD_COMMAND=<absolute-argv>' \
'UNIFI_PROTECT_API_KEY_FILE=<absolute-path>' \
UNIFI_PROTECT_TOOL_PERMISSION_MODE=confirm \
UNIFI_POLICY_PROTECT_CREATE=false \
UNIFI_POLICY_PROTECT_UPDATE=false \
UNIFI_POLICY_PROTECT_DELETE=false \
UNIFI_POLICY_PROTECT_CAMERAS_UPDATE=true
The script handles the client-specific write:
- Claude: validates every value, then saves the plugin's Claude Code options
with
claude plugin configure --values-stdin(values on stdin): non-secret options in usersettings.json(pluginConfigs), a raw password or API key in the system keychain. The options apply in every project - Codex: validates every value, then atomically writes the settings file
~/.config/unifi-mcp/protect.env(%USERPROFILE%\.config\unifi-mcp\protect.envon Windows), readable by the user only. The plugin's own server reads it throughUNIFI_SETTINGS_FILE; setup writes noconfig.tomlentry. Env never enters argv - OpenClaw: writes the same settings file as Codex,
~/.config/unifi-mcp/protect.env, readable by the user only. The plugin's own server reads it throughUNIFI_SETTINGS_FILE; setup writes noopenclaw.jsonentry
For raw values or null deletions, use JSON stdin: set-env.sh --target <target> --input-json or set-env.ps1 -Target <target> -InputJson. Have the user supply
this JSON locally from a private file or a masked input UI; never put raw secrets
in chat, a tool call, command arguments, or a shell history entry. Provider
references in the examples are non-secret paths or helper argv, never embedded
passwords or API keys. Setup checks that provider files are readable and non-empty
and provider executables exist; it does not read secrets, execute providers, or
contact the controller. Provider output and controller authentication are checked
only when the server starts.
Codex upgrades
The plugin's own MCP server is the one Codex runs, and it reads the settings file setup wrote, so a plugin upgrade upgrades the server with nothing to rerun. Codex installs an upgrade in the background as a session starts; the next session runs the new server.
Setup before plugin 1.0.0 saved a [mcp_servers.unifi-protect] entry in Codex's
config.toml instead. That entry takes precedence over the plugin's server and
keeps an old server version. Step 0 then prints a notice naming the entry and
--refresh. Relay it and, once the user agrees, run refresh from this skill's
plugin root, then restart Codex:
bash <plugin-root>/scripts/set-env.sh --target codex --refresh
On Windows: & <plugin-root>/scripts/set-env.ps1 -Target codex -Refresh.
Refresh needs no credential input. It moves the entry's UNIFI_ settings,
provider references included, into the settings file, removes the entry from
config.toml after saving a private backup beside it, and names any other entry
settings it did not move. Ordinary setup does the same when it finds the entry.
It is needed once. If a settings file already exists and holds different
settings, refresh changes nothing and says which to remove.
Setup edits only that entry's tables in config.toml, so every other setting and
comment stays as written. If the file lays out its MCP servers in a form setup
cannot edit in place (dotted or inline mcp_servers keys), setup lets Codex
remove the entry, which drops the comments inside every [mcp_servers.*]
table, and says so in its output.
OpenClaw upgrades
OpenClaw runs the plugin's own MCP server, which reads the settings file setup
wrote, so openclaw plugins update upgrades the server with nothing to rerun.
A running Gateway applies the update at the next agent turn.
Setup before plugin 1.0.0 saved an mcp.servers.unifi-protect entry in
openclaw.json instead. That entry replaces the plugin's server and keeps an
old server version. Step 0 then prints a notice naming the entry and
--refresh. Relay it and, once the user agrees, run refresh from this skill's
plugin root:
bash <plugin-root>/scripts/set-env.sh --target openclaw --refresh
On Windows: & <plugin-root>/scripts/set-env.ps1 -Target openclaw -Refresh.
Refresh needs no credential input. It moves the entry's UNIFI_ settings,
provider references included, into the settings file, saves a private backup of
openclaw.json beside it, then removes the entry with openclaw mcp unset and
names any other entry settings it did not move. Ordinary setup does the same
when it finds the entry. It is needed once. If a settings file already exists
and holds different settings, refresh changes nothing and says which to remove.
Claude Code projects set up by an earlier plugin version
Earlier versions saved Claude Code settings in the project's
.claude/settings.local.json. This version no longer reads them; setup, the
prerequisite check and a session-start notice name the leftover variables. From
that project, run:
bash <plugin-root>/scripts/set-env.sh --target claude --migrate
On Windows: & <plugin-root>/scripts/set-env.ps1 -Target claude -Migrate. It
copies this plugin's settings into its options, then removes only this plugin's
keys from the project file atomically. Shared UNIFI_* keys are kept unless
every possible consumer is proven migrated; setup currently verifies only this
plugin's options. It names the retained shared keys and explains that other
consumers have not been verified as migrated. Output includes only variable
names, including what it moved and what has no Claude Code option (per-category
policy overrides). A missing provider file stops it with nothing changed, as
does a provider that would replace a password or API key held in the keychain.
Plugin options apply in every project. If options saved from another project
already hold a different host, username, port or site, --migrate refuses
and names those variables without values. Ask the user which settings every
project should use: rerun with --replace (-Replace) to make this project's
settings the options for every project, or remove this plugin's leftover
UNIFI_PROTECT_* keys from the project file to keep the saved options. A
session-start notice also fires in a project that still holds its own
UNIFI_PROTECT_* keys while options from another project are in use; offer the
same choice there. When the project's host, username and port equal the saved
options, the notice says nothing changes instead; offer to remove the leftover
keys, or to tidy up with --migrate, which also copies the project's other
settings, such as its credential source, into the options.
After the migration, in a session that was already open, run /reload-plugins
or start a new session. /mcp reconnect alone keeps the options the session
started with: it reports success, but the server stays in setup mode. Use
/mcp reconnect plugin:unifi-protect:unifi-protect only if /mcp shows the server as
failed. Once no UniFi plugin keys remain in the project file, the prerequisite
check names any shared UNIFI_* keys still there: Claude Code exports them into
every process in that project, so suggest removing them once nothing else needs
them.
Recovery
On validation, dependency, registration, or write failure the previous file and
registration remain in place. Repair malformed configuration without discarding
unrelated settings, install the reported dependency, or correct the provider,
then rerun. --dry-run / -DryRun validates the patch without changing files and
omits values. OpenClaw setup reads openclaw.json as strict JSON; a JSON5
configuration is refused unchanged. After a forced termination,
remove adjacent .setup-lock and .unifi-setup-* staging directories only after
confirming no setup process is running. Staging files are private and may contain
credentials; delete them without displaying their contents. An interruption at
publication leaves either the old configuration or the complete new one.
Step 5: Final Message
For Claude Code, tell the user:
"Saved as the unifi-protect plugin's Claude Code options (a raw password or API key goes to the system keychain). In this session, run /reload-plugins (a new session also loads the options); /mcp reconnect alone keeps the options the session started with, so the server would stay in setup mode. Then run /mcp and check that plugin:unifi-protect:unifi-protect offers more than one tool: a server offering only its setup-status tool is still unconfigured. Use /mcp reconnect plugin:unifi-protect:unifi-protect only if /mcp shows the server as failed. Review or change the options with /plugin, then unifi-protect, then Configure options."
For Codex, tell the user:
"Saved the settings for unifi-protect to ~/.config/unifi-mcp/protect.env, readable by you only. The plugin's own server reads them and follows plugin upgrades on its own. Restart Codex so the server loads them."
For OpenClaw, tell the user:
"Saved the settings for unifi-protect to ~/.config/unifi-mcp/protect.env, readable by you only. The plugin's own server reads them and follows plugin updates on its own. OpenClaw starts it with these settings in a new session or after openclaw gateway restart; a session that is already open keeps the unconfigured server until then."