@mgcrea/mcp-unifi-protect
This server is an MCP gateway to a self-hosted UniFi Protect console, letting an agent inspect cameras, search recorded events and detections, grab snapshots/footage, manage devices, and audit or reconfigure the system (writes optional).
Check connectivity & auth:
unifi_protect_auth_status, login (incl. 2FA), logout, and clear cached sessions.System overview: console model, Protect version, firmware, timezone, storage, device counts.
Cameras: list all cameras (summarized), get full camera records, capture live snapshots to disk or inline, and list PTZ presets/patrols.
Event search: search motion/smart detections/rings over any time range, fetch full event metadata, fetch event thumbnails singly or up to 6 inline for visual review.
Footage export: export recorded video as MP4 files on disk with configurable channel/quality.
Devices: list lights, sensors (temp/humidity/light), viewers, chimes, liveviews, and user accounts.
Audit:
unifi_protect_check_settingsdetects inconsistent camera settings (e.g., detectors gated off, no recording, offline devices).Write tools (only registered with
UNIFI_PROTECT_ALLOW_WRITES=1): update cameras (name, detection types, recording mode), reboot cameras/NVR, control lights, update sensors/viewers/chimes, and change NVR settings.Escape hatch:
unifi_protect_requestcalls any private Protect API path directly (GET-only unless writes enabled).
Provides tools for interacting with a self-hosted UniFi Protect console, including cameras, recorded events and smart detections, snapshots, video export, lights, sensors, viewers, chimes, and system information.
Click on "Deploy 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., "@@mgcrea/mcp-unifi-protectShow me motion events from the front door camera in the last hour"
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.
@mgcrea/mcp-unifi-protect
Model Context Protocol server for a self-hosted UniFi Protect console — cameras, recorded events and smart detections, snapshots, footage export, and the lights, sensors, viewers and chimes attached to it. Read-only by default: the tools that change anything are not registered at all unless you ask for them.
Features
Search recorded events over any time range — motion, person / vehicle / animal / package / licence-plate detections, doorbell rings — with each result already carrying its camera's name, not just an id.
Snapshots and footage — capture a frame now, pull an event's thumbnail, export an MP4. All written to disk by default, so a still frame does not silently cost you a context window.
Devices — cameras, lights, sensors (with their temperature, humidity and light readings), viewers, chimes, live views and users.
Shaped responses. A console camera record is 8-15 KB of JSON; a list of ten is over 100 KB. List tools return the fifteen fields anyone actually asks about.
get_*returns everything.Stays up with no credentials, reporting what to configure through
unifi_protect_auth_statusrather than exiting and showing in your client as a bareConnection closed.
Related MCP server: UniFi MCP Server
Two ways to connect
|
| |
Reaches the console | directly on your LAN | via |
Credentials | host + username + password | API key + console id |
Auth mechanism | UniFi OS login → session cookie + CSRF token |
|
TLS | console's self-signed cert — pinned on first use | a real certificate, nothing to do |
Works off-LAN | no | yes |
Session state on disk | yes, mode | none |
Both modes expose exactly the same tools, because both speak the same private
Protect API — the connector forwards the whole /proxy/protect/... tree, the private
API included. That is not obvious and is worth stating plainly: Ubiquiti's official
Integration API has no historical query capability whatsoever, so if the connector only
carried that, cloud mode could not answer a single question about the past. It carries
the private API too, verified against a live console — bootstrap, events, cameras
and binary snapshots all answer 200.
So cloud mode is a full alternative, not a reduced one, and it removes the local account, the password, the session file, the CSRF handshake and the self-signed certificate problem in one go.
# cloud — no local account at all
UNIFI_PROTECT_API_KEY=… # unifi.ui.com → Settings → API Keys
UNIFI_PROTECT_CONSOLE_ID=… # curl -H "X-API-KEY: $KEY" https://api.ui.com/v1/hosts
# local — on the LAN
UNIFI_PROTECT_HOST=192.168.1.1
UNIFI_PROTECT_USERNAME=mcp
UNIFI_PROTECT_PASSWORD=…
# TLS needs nothing: the console's certificate is pinned on the first request.UNIFI_PROTECT_MODE is inferred as cloud when an API key and a console id are both
set, so it usually needs no setting. console/unifios/lan and
remote/site-manager/connector are accepted as synonyms, and an unrecognised value
is reported through unifi_protect_auth_status rather than killing the server.
Two traps in cloud mode. A key that works for /v1/hosts can still return
403 user cannot access host in the organization for a console outside the
organization it was issued in — valid key, wrong org, and the message reads nothing like
a credentials problem. And API keys are per-console: a key created on your Network
gateway or a UNAS does not authenticate against the NVR running Protect, and the NVR
rejects it exactly as it rejects a made-up key.
Why local mode needs a username and password
An API key looks like it ought to work here, and it is the obvious thing to reach for. It does not, and the reason is worth writing down so nobody spends an afternoon on it.
A key created on the console itself (UniFi OS → Control Plane → Integrations) is recognised — but only by Ubiquiti's official Integration API. It is refused by the private API this server depends on. Tested against a UNVR on Protect 7.2.105 with a key issued on that console:
Endpoint | With a console API key |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A fabricated key returns 401 on the official API too, so the 200s above confirm the
key really was valid — the private API simply does not accept key auth.
Putting all three paths together:
Path | Authenticates with | Private API (event history, snapshots) |
| session cookie + CSRF | ✅ |
|
| ❌ |
|
| ✅ |
The asymmetry is not arbitrary. Over the connector, api.ui.com authenticates you by
key and then reaches the console over its own trusted channel, so the console is never
asked to accept a key on a private path. On the LAN there is no such intermediary, and
the private API only knows the session the web app itself uses.
So a local-only deployment needs a username and password. That is a property of Protect, not a shortcut taken here. Use a dedicated Local-Access-Only account with View Only rights, as described below, and the credential's blast radius stays small.
The one thing a console API key would unlock is the PTZ move commands
(ptz/goto, ptz/patrol/start, ptz/patrol/stop), which exist only on the official
Integration API — see Not implemented.
Security
Supply chain. Three runtime dependencies: the MCP SDK, zod, and
@mgcrea/unifi-protect — the console client,
which is ours and whose own dependencies are undici, ws and zod. There is no HTTP client
wrapper, no logger, no crypto library. That client used to be a copy inside this repo; the copy
and the original had already begun to drift, which is a bad way to hold knowledge about an
undocumented API. Published from CI with provenance via OIDC trusted publishing; the container
image is multi-arch, carries an SBOM, and is signed with cosign.
Your credentials. The username and password come from the environment or a config file, and
never leave this process except in the login request to your console. The resulting session
cookie is cached at ~/.config/unifi-protect/session.json with mode 600.
Certificate verification is ON by default, and now needs no setup. The console's certificate
is pinned: on the first request the server reads it, records its SHA-256 fingerprint and PEM in
~/.config/unifi-protect/trust.json (mode 600), and every connection afterwards verifies against
that one certificate as its own anchor. The host name check is replaced — not skipped — by a
fingerprint comparison, so addressing the console by IP is fine and a swapped certificate fails
hard.
This is what changed. The certificate is issued to unifi.local with no IP SAN, so reached by
an IP address, host name verification could never pass however the certificate was trusted; the
old advice was to set NODE_EXTRA_CA_CERTS and address the console by a resolvable name, or
give up and disable verification. Pinning removes both requirements.
Pinning is trust-on-first-use, and it is only as good as that first moment. The fingerprint is
logged when it is learned and reported by unifi_protect_auth_status; set
UNIFI_PROTECT_FINGERPRINT to make the trust explicit instead, and a console that presents
anything else is then refused rather than adopted. If the console legitimately reissues its
certificate, delete the trust file.
UNIFI_PROTECT_VERIFY_TLS=false still turns checking off entirely, scoped to this server's own
requests through an undici dispatcher — it is not NODE_TLS_REJECT_UNAUTHORIZED, so nothing else
in the process is affected. There should no longer be a reason to use it; the startup banner
prints tls=UNVERIFIED on every run when you do.
Blast radius. With the defaults, the worst an agent can do is read your cameras and write
image files into the snapshot directory. With UNIFI_PROTECT_ALLOW_WRITES=1 it can additionally
reconfigure devices, stop a camera recording, and reboot a camera or the whole console. Use a
Local-Access-Only account with View Only rights, and leave writes off unless you need them.
Configure
Variable | Required | Default | What it does |
| yes | — | Console IP or hostname. |
| yes | — | Console login |
| yes | — | Its password |
| no | — | 2FA code. Expires in ~30s — prefer |
| no |
| Verify the console's certificate. Off disables pinning entirely |
| no | — | Pin this SHA-256 up front instead of learning it on first use |
| no |
| Where the pinned certificate is remembered, mode 600 |
| no |
| Register the 12 mutating tools |
| no |
| Cached session, mode 600 |
| no |
| Where images and exports are written |
| no |
| Config file location |
| no |
| Retries on 401 / 429 / 5xx |
| no |
| Refuse a download larger than this |
| no |
| Camera id→name cache lifetime, seconds |
| no | — | Verbose request logging to stderr |
The config file mirrors these as camelCase JSON (host, username, verifyTls, …). It is
strict: an unknown key is an error rather than a silent no-op. Environment variables win over
the file, field by field, so a one-off UNIFI_PROTECT_ALLOW_WRITES=0 still beats a file that
says true.
Create an account for it
UniFi OS → Settings → Admins & Users → Add User → Local Access Only, with Protect permissions and View Only unless you plan to enable writes.
Use a local account rather than your Ubiquiti (SSO) one. Cloud accounts frequently cannot log in locally at all, and a scoped local account keeps this server away from the rest of the console.
Quick start
A. npx
UNIFI_PROTECT_HOST=192.168.1.1 UNIFI_PROTECT_USERNAME=mcp UNIFI_PROTECT_PASSWORD=… \
npx -y @mgcrea/mcp-unifi-protectB. Docker (stdio)
docker run --rm -i \
-e UNIFI_PROTECT_HOST=192.168.1.1 \
-e UNIFI_PROTECT_USERNAME=mcp \
-e UNIFI_PROTECT_PASSWORD=… \
ghcr.io/mgcrea/mcp-unifi-protectC. From source
pnpm install && pnpm build
node dist/cli.jsInspect the tools
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"cli","version":"0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| node dist/cli.js 2>/dev/null | jq -r '.result.tools[]?.name'Tools
22 read tools, plus 10 more when writes are enabled.
Tool | What it does | Writes |
| Log in and make a real call, reporting whether the console is reachable and on what Protect version | — |
| Force a fresh login; the only way to supply a 2FA code | — |
| Drop the cached session and delete the session file | confirm |
| Console model, Protect version, storage, device counts | — |
| Every camera, summarized | — |
| One camera's complete record (large) | — |
| Capture a frame now, to a file or inline | — |
| A PTZ camera's saved preset slots | — |
| A PTZ camera's saved patrol routes | — |
| Audit every camera for inconsistent or self-defeating settings | — |
| Search recorded events over any time range | — |
| One event's full detection metadata | — |
| The frame that triggered a detection | — |
| Up to 6 frames at once, inline — how you tell a person from a branch | — |
| Export footage as an MP4 on disk | — |
| Floodlights, with state and brightness | — |
| Sensors, with temperature / humidity / light readings | — |
| Viewport devices and what each displays | — |
| Chimes, volume, paired doorbells | — |
| Saved camera grid layouts | — |
| Who can sign in to Protect | — |
| Escape hatch: call any private endpoint directly | GET only unless writes |
| Name, mic, status LED, OSD overlays | ✅ |
| Which objects and sounds a camera detects — the gate below | ✅ |
|
| ✅ |
| Reboot one camera | ✅ confirm |
| Brightness, on/off, PIR sensitivity | ✅ |
| Name, which capabilities report | ✅ |
| Put a live view on a screen | ✅ |
| Volume, name | ✅ |
| Console name, timezone, global recording | ✅ |
| Reboot the console | ✅ confirm |
Resources and prompts
Three resources carry the standing facts a question needs before a tool is chosen, so a client can attach them once instead of spending a call per question:
Resource | Why it exists |
| The console's time zone, so "1am" is read as the local clock rather than UTC |
| What each camera will actually detect, what its zones ask for, and where they differ |
| Named groups of cameras, so a question about a place resolves to ids |
Two prompts carry the procedure, which is the part a tool list cannot express:
check_camera_settings— run the audit and interpret it, changing nothing. Several findings have two valid opposite fixes, and which is right depends on what the camera is for.who_passed— find who was present in a window, and fall back to motion frames on any camera whose detector is off rather than reporting a zero count as an absence. It takes the question as one free-text argument, so quote it: slash-command arguments are split shell-style and mapped positionally, sowho_passed in front of the house last night?arrives as just"in", whilewho_passed "in front of the house last night?"arrives whole. A single-word question is treated as that truncation and refused rather than answered.
Worked example: what happened at the front door last night
// 1. Which cameras are there?
{"name": "unifi_protect_list_cameras", "arguments": {}}
// → [{ "id": "661a…", "name": "Front Door", "hasSmartDetect": true,
// "smartDetectTypes": ["person","package"], "recordingMode": "detections", … }]
// 2. People seen overnight. Note the camera NAME comes back resolved.
{"name": "unifi_protect_list_events", "arguments": {
"start": "2026-08-29T22:00:00Z", "end": "2026-08-30T07:00:00Z",
"types": ["smartDetectZone"], "smartDetectTypes": ["person"]}}
// → { "count": 3, "events": [
// { "id": "9f3c1a02-…", "start": "2026-08-30T02:14:07.000Z", "camera": "Front Door",
// "smartDetectTypes": ["person"], "score": 94, "hasThumbnail": true }, … ] }
// 3. Look at the one at 02:14 — pass the event's own id.
{"name": "unifi_protect_get_event_thumbnail",
"arguments": {"eventId": "9f3c1a02-…", "output": "image"}}
// 4. Pull the footage around it.
{"name": "unifi_protect_export_video", "arguments": {
"cameraId": "661a…", "start": "2026-08-30T02:13:30Z", "end": "2026-08-30T02:15:00Z"}}
// → { "path": "/Users/you/.cache/unifi-protect/front-door-….mp4", "bytes": 18432000 }Traps worth knowing
This wraps Protect's private API, not the official one. Ubiquiti publishes an Integration
API at /proxy/protect/integration/v1 with an OpenAPI spec and an X-API-KEY header. It is not
used here, because it has no historical query capability at all — the only query parameters
in its entire spec are channel, highQuality and qualities, and events exist solely as a
live WebSocket. "What happened last night" is unanswerable through it. The private API answers
that, at the cost of being undocumented and liable to change between Protect releases. This was
built and verified end-to-end against a live UNVR running Protect 7.2.105.
unifi_protect_get_system_info reports the version you are actually running, and
unifi_protect_request reaches any endpoint that moves.
Two shapes already changed between 6.x and 7.x, both found by running this against a real console, and both now handled in either form:
Storage moved. 6.x had
nvr.storageInfowithtotalSize/totalSpaceUsed. By 7.2 that key is gone; the numbers live undernvr.systemInfo.storageandnvr.storageStats, with per-disk health insystemInfo.ustorage.disks.A camera has no
ledLevel. The 0-6 brightness that looks like it belongs there is a floodlight field; a camera's LED is the on/offledSettings.isEnabled. Sub-objects also deep-merge on PATCH, so setting one OSD overlay preserves the others — verified by writing to a live camera and reading it back.An event's
thumbnailfield is not a thumbnail id you can use here. It readse-<eventId>and belongs to thethumbnails/<id>endpoint;events/<eventId>/thumbnail— the one this server calls — wants the bare event id. Passing the console's own value returns 404. So list results reporthasThumbnail: truerather than an id, andunifi_protect_get_event_thumbnailtakes the event'sid(though it tolerates ane-…value too).
Smart detection is gated in two places, and only one of them is obvious.
smartDetectSettings.objectTypes on the device is the master switch;
smartDetectZones[].objectTypes says what each zone asks for. A zone can ask for person while
the device list omits it, and the console then reports nothing at all — no error, no warning,
just an empty result forever. On the console this was built against, a doorbell had
zone: [person, vehicle, animal] against device: [animal], so a person search returned zero
across seven days while people walked past nightly.
Zero results are therefore never reported bare. unifi_protect_list_events cross-checks the
requested detection types against each camera's device list and returns a warnings array
saying the detector was off — the difference between "nobody was there" and "nothing was
looking". unifi_protect_check_settings finds the same misconfiguration across the whole
system, and unifi_protect_set_camera_detections fixes it, keeping the zones in step.
One limit worth knowing: the check reflects the camera's setting now, so a historical search over a period when the detector was off but has since been enabled gets no warning.
Some settings are reported on read but refused on write. smartDetectSettings.audioTypes
comes back containing smoke_cmonx, and a PATCH containing it fails with
400 The smart detection feature is not enabled for: smoke_cmonx. Any read-modify-write that
echoes the list back therefore breaks. unifi_protect_set_camera_detections filters against
featureFlags.smartDetectAudioTypes and reports what it dropped.
Camera filtering happens on the console, and the parameter must be repeated. /events
accepts cameras=<id>, repeated once per camera. A comma-separated list is accepted and
silently matches nothing. This mattered more than it looks: filtering client-side instead
fetches the newest limit events across all cameras and discards the rest, so a quiet camera
over a long window came back empty while reporting a successful search.
Times are milliseconds, and getting it wrong fails silently. The console takes JavaScript
millisecond timestamps. A Unix seconds value is not rejected — it is read as a moment in 1970,
so the query succeeds and returns an empty list, which reads as "nothing happened". Every time
argument here accepts ISO 8601, a relative expression ("2h ago", "30m", "7d") or "now",
and a ten-digit number is refused with the corrected value in the error.
Local forms are also accepted — "1am", "01:30", "2026-08-30 01:00" — and read in the
console's own time zone, because a question about last night is a question about the clock
where the cameras are. A bare time of day resolves to its most recent occurrence, and start
anchors to the window's end, so "1am to 6am" stays one coherent night however late it is asked.
Event search is always filtered by type. Omitting types entirely triggers a pagination bug
in Protect where the console ignores the window and returns the wrong slice. unifi_protect_list_events
always sends an explicit list, defaulting to motion, smart detections and rings.
Footage only exists if the camera was recording. An empty event search may mean the camera's
recording mode is never, not that nothing happened. unifi_protect_list_cameras shows the mode.
Snapshots are forced. Without that the console can return a cached frame minutes old, which is indistinguishable from a current one.
A cloud account may not work. Ubiquiti SSO accounts frequently cannot log in locally. Create a Local Access Only user.
Troubleshooting
The server does not appear, or shows Connection closed. It should never exit on missing
credentials — run it by hand with the same environment and read stderr. Everything it logs goes
to stderr, because stdout is the protocol channel.
Only unifi_protect_auth_status is listed. No console is configured. Call that tool; it
returns the setup steps as data.
A tool I expected is missing. The write tools are not registered unless
UNIFI_PROTECT_ALLOW_WRITES=1. That is the design, not a bug — an absent tool cannot be called,
whereas a refused one invites an agent to keep trying.
self-signed certificate errors. These should no longer happen: the certificate is pinned on the first request, which works against an IP address. If one appears, the console is presenting a different certificate than the one pinned — delete ~/.config/unifi-protect/trust.json if it was legitimately reissued, and investigate if it was not
unless you have installed a trusted certificate on the console.
Cloud mode returns 403 user cannot access host in the organization. The key is
valid but was issued in an organization that does not contain that console. Check the
console appears in curl -H "X-API-KEY: $KEY" https://api.ui.com/v1/hosts; if the web
dashboard shows it but that call does not, they are different organizations.
I set an API key for local mode and everything returns 401. Local mode cannot use an
API key — see Why local mode needs a username and password.
Set UNIFI_PROTECT_USERNAME and UNIFI_PROTECT_PASSWORD, or switch to cloud mode,
where a key is all you need.
A local API key returns 401 on everything. API keys are per-console. A key created
on your Network gateway is not valid on the NVR running Protect — and the NVR rejects an
unknown key with exactly the same 401 it gives a fabricated one, so the message cannot
distinguish "wrong console" from "wrong key". Create the key on the console you are
addressing, or use cloud mode.
Everything returns 401. Check the account is a local one, and that it has Protect
permissions. unifi_protect_auth_status distinguishes "cannot log in" from "logged in but
forbidden".
A tool that used to work now returns 404. Compare the Protect version from
unifi_protect_get_system_info against 7.2.105 above; an upgrade may have moved the endpoint.
unifi_protect_request is the workaround while it is fixed — it reaches any path under
/proxy/protect/api directly, which is how both of the 6.x→7.x changes above were pinned down.
Storage shows as nearly full. That is normal on an NVR: isRecycling: true means the
console continuously overwrites the oldest footage rather than stopping. get_system_info says
so inline so it does not read as a fault.
What has been verified against real hardware
Cloud mode was verified end-to-end against a live console over the Site Manager
connector: auth_status reachable, camera list, and event search returning real
detections with their camera names resolved — all authenticated by an API key alone,
with no local account anywhere in the picture.
Local mode was built and exercised end-to-end against a live UNVR4 on Protect 7.2.105 with 12 cameras, one floodlight and one chime. Every read tool was run; the write tools were exercised with no-op writes — each value set to the value it already held — and the device state read back unchanged afterwards. That run is also what caught three bugs this README's earlier drafts described wrongly: the storage layout, the event-thumbnail id, and two camera fields that do not exist.
Certificate pinning was verified against that same console on 2026-08-31, addressed by IP
(192.168.6.3) with verification ON — the case that was impossible before. Confirmed in one run:
the certificate is captured and its fingerprint logged on first contact; the trust file is written
mode 600; a restart reuses the pin without re-capturing; a correct UNIFI_PROTECT_FINGERPRINT
(colons and all) is accepted; and a wrong one is refused with a message naming both fingerprints,
surfacing as a failed tool call rather than a server that never starts. auth_status reported
reachable: true on Protect 7.2.105 throughout, and a session cached by the previous release was
restored unchanged — the on-disk format did not move.
Two tools remain unverified for want of hardware, and are marked here rather than left to look tested:
unifi_protect_update_sensor— no UP Sense device on the test console (sensorsis empty). Note that a Protect floodlight has its own built-in PIR, reported asisPirMotionDetectedand tuned viapirSensitivity; that is part of the light, so it isunifi_protect_update_lightthat controls it, not this tool. A garden lamp is not a sensor device.unifi_protect_update_viewer— no Viewport device on the test console.
Both follow the same PATCH shape as the tools that were verified, so they are likely correct, but "likely" is the honest word until someone runs them.
Not implemented
The realtime WebSocket at /proxy/protect/ws/updates is not wired up here, though
@mgcrea/unifi-protect now implements it — connectEventStream and ProtectStore are a
decoded frame stream and a live device cache, and this server deliberately uses neither.
The reason is that they are the wrong shape for an MCP server. Both want a long-lived process
that bootstraps at start-up and stays subscribed; this server must start with no credentials and
no connectivity at all, answering unifi_protect_auth_status until it is configured. It would
also gain little: because this wraps the private API, event history is already available over
REST through unifi_protect_list_events, which is what a subscription would have been for.
Adopting it would mean deferring the bootstrap until credentials appear — worth doing if a tool
ever needs push rather than poll, and not before.
Develop
pnpm install
pnpm lint && pnpm format:check && pnpm typecheck && pnpm test && pnpm buildPublish:
pnpm release minor # bump, commit, tag (patch|minor|major)
git push --follow-tags # CI publishes to npm + GHCR from the tagLicense
MIT
Available Tools
22 toolsunifi_protect_auth_loginA
Force a fresh login to the console, replacing any cached session. Normally unnecessary — the server logs in on demand and re-authenticates automatically on a 401. Use it to supply a two-factor code, which cannot be done unattended: the code is single-use and expires in about 30 seconds, so it is passed here once and the resulting session is then cached and reused.
| Name | Required | Description | Default |
|---|---|---|---|
| totp | No | Current code from your authenticator app. Omit if the account has no 2FA. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only indicate non-read-only/non-idempotent/non-destructive, so the description carries the behavioral burden. It discloses session replacement, caching/reuse of the resulting session, and the critical single-use/30-second expiry nature of the 2FA code. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with the core behavior and main caveat front-loaded. Every clause adds information; no filler or repetition of schema/defaults.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-optional-parameter auth tool with no output schema, this is complete: it states what the tool does, when it is and isn't needed, the 2FA constraints, and the session lifecycle. An agent can decide correctly whether to call it and how to pass the parameter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents totp at 100% coverage, including the pattern and omission rule. The description adds valuable extra semantics: the code is single-use, expires in about 30 seconds, and must be passed exactly once — details the schema cannot convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action ('Force a fresh login to the console') with a clear side effect ('replacing any cached session'). Distinguishes itself from routine automatic re-authentication by explaining when this explicit login is actually needed (2FA).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Normally unnecessary' and describes the server's automatic on-demand login and 401 re-authentication as the default path. Then gives the exact condition for using this tool: supplying a two-factor code that cannot be supplied unattended.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_auth_logoutADestructiveIdempotent
Drop the cached session and delete the session file. The next call logs in again from the configured username and password, so this does not lock anything out — use it to clear a session after changing accounts, or to remove the cookie from disk.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | Yes | Must be true. Explicit acknowledgement that this changes the console's state. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavioral context beyond the annotations: it reveals that the session file is deleted, that the next call will re-authenticate, and that this action is not a lockout. This complements the idempotentHint and destructiveHint annotations without contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with no filler. The core action and effect come first, followed by practical use cases. Every phrase earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter logout tool, the description fully covers behavior, side effects, and usage intent. Annotations and schema cover safety and state-change acknowledgement, so nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already documents the sole 'confirm' parameter as requiring an explicit true acknowledgement. The description adds no additional parameter-level detail, which is acceptable given the schema carries the full meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the action ('Drop the cached session and delete the session file') and the resource (cached session/session file). It distinguishes this from auth_login and auth_status by focusing on clearing the session, making its purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit use cases: 'clear a session after changing accounts, or to remove the cookie from disk.' It also clarifies a non-effect ('does not lock anything out'). It does not explicitly contrast with sibling tools, but the when-to-use guidance is strong enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_auth_statusARead-only
Check whether this server can actually reach your UniFi Protect console. By default it logs in and makes a real call, so the answer reflects the console rather than cached state — this is the tool to run when something is not working. Reports the host, the account, the Protect version, whether TLS is verified, and whether writes are enabled; when nothing is configured it returns the exact setup steps instead. Call this first when a tool you expected is not listed: an absent tool means missing configuration or writes being off, not a bug.
| Name | Required | Description | Default |
|---|---|---|---|
| probe | No | Actually contact the console (logging in if needed) rather than only reporting what is already cached. Set false for a fast, purely local answer. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavior beyond the readOnlyHint annotation: it logs in and makes a real call by default, distinguishes live results from cached state, reports whether writes are enabled, and explains that absent tools indicate configuration issues rather than bugs. This is rich, honest behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet information-dense, with the core purpose front-loaded in the first sentence. Every sentence earns its place: behavior, outputs, failure mode, and usage guidance are all included without repetition or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter diagnostic tool with a readOnlyHint annotation and no output schema, the description is complete: it covers default behavior, configurable fast path, reported values, unconfigured setup steps, and when to call it first. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents the single 'probe' parameter. The tool description reinforces the default behavior and hints at the fast cached path, but does not add significant meaning beyond the schema's own parameter description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource ('Check whether this server can actually reach your UniFi Protect console') and clearly distinguishes this from data-listing siblings by framing it as a connectivity/diagnostic tool. It also enumerates the reported fields, leaving no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: run it when something is not working, and call it first when an expected tool is missing. It does not explicitly state when not to use it or name alternatives, but the diagnostic context is clear enough for an agent to select it correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_check_settingsARead-only
Check every camera and the console for settings that are inconsistent, or that mean the system is not doing what someone believes it is. Finds detectors that look enabled but are gated off, cameras keeping no footage, offline devices, motion detection switched off, and storage about to stop recording. This is the tool for "are my camera settings correct" — the checks encode traps that are invisible in the Protect UI, notably a detection zone asking for an object type the device list blocks. Read-only: it reports findings and names the tool that would fix each one, and changes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description states that it 'changes nothing' and explains its actual behavior as 'reports findings and names the tool that would fix each one'. This gives an accurate, non-contradictory picture of side effects and output semantics, adding value beyond the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence carries distinct content: scope, finding categories, intended use, and read-only behavior. The key intent phrase is placed before the read-only clarification, and there is no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no parameters and only a readOnlyHint annotation, the description covers invocation, behavioral contract, and the nature of the result ('reports findings and names the tool that would fix each one'). It is sufficient for an agent to select and call the tool correctly without further documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero properties, and the description explicitly confirms that no configuration is needed by stating it checks 'every camera and the console'. With zero parameters the baseline is 4; the description adds conceptual meaning by framing the tool as a whole-system scan rather than a targeted query.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb, 'Check', against a clear resource ('every camera and the console') and enumerates concrete problem categories such as gated-off detectors, cameras keeping no footage, and storage about to stop recording. It also explicitly identifies itself as the tool for 'are my camera settings correct', which distinguishes it from the sibling get/list/export tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit intended-query mapping — 'This is the tool for "are my camera settings correct"' — which tells an agent when to select it. It does not enumerate exclusions or name alternative tools for the same job, but none of the sibling tools perform this cross-device consistency check, so the omission is minor.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_export_videoARead-only
Export recorded footage from one camera over a time range as an MP4 file on disk. Always writes to a file and returns the path — video is never returned inline. Size grows quickly with the window: expect tens of megabytes per minute at full quality, and the call fails rather than exhausting memory if the export exceeds UNIFI_PROTECT_MAX_DOWNLOAD_BYTES. Footage only exists if the camera was recording at the time, so check the recording mode before concluding that nothing happened.
| Name | Required | Description | Default |
|---|---|---|---|
| end | Yes | End of the footage to export. Accepts ISO 8601 ("2026-08-29T22:00:00Z"), a relative expression ("2h ago", "30m", "7d"), or "now". Converted to the millisecond epoch the console requires — do not pass Unix seconds, which would silently query 1970 and return nothing. | |
| start | Yes | Beginning of the footage to export. Accepts ISO 8601 ("2026-08-29T22:00:00Z"), a relative expression ("2h ago", "30m", "7d"), or "now". Converted to the millisecond epoch the console requires — do not pass Unix seconds, which would silently query 1970 and return nothing. | |
| channel | No | Encoder channel: 0 is the high-quality stream, higher numbers are progressively lower bitrate. Use a higher channel to keep a long export manageable. | |
| cameraId | Yes | Camera id — the `id` from unifi_protect_list_cameras, a 24-character hex string. Not the camera's name and not its MAC address. | |
| savePath | No | Absolute path to write the MP4 to. Defaults to a timestamped file under UNIFI_PROTECT_SNAPSHOT_DIR. Parent directories are created. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses several behaviors beyond the readOnlyHint annotation: it always writes to disk, never returns video inline, grows tens of megabytes per minute, fails instead of exhausting memory when exceeding UNIFI_PROTECT_MAX_DOWNLOAD_BYTES, and may return nothing if the camera was not recording. This gives the agent concrete expectations and failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three focused sentences, with the core action front-loaded and no filler. Each sentence earns its place by covering purpose, output/scale behavior, failure mode, and a practical prerequisite.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description clearly explains what the tool returns (a file path), how large the output can get, when it fails, and a common reason for an empty result. Combined with the thorough input schema, an agent has everything needed to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already documents parameter formats, defaults, and pitfalls (e.g., ISO 8601, relative expressions, not using Unix seconds). The tool description does not add additional parameter-level semantics beyond the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Export'), resource ('recorded footage from one camera over a time range'), and output ('MP4 file on disk'). It distinguishes this tool from siblings like get_camera_snapshot by emphasizing video export to a file, not inline or still images.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for when to use this tool: 'Always writes to a file and returns the path — video is never returned inline' implies this is the disk-export tool. It also advises checking the recording mode before interpreting an empty result. However, it does not explicitly name sibling alternatives or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_get_cameraARead-only
Get one camera's complete record — every setting the console holds, including encoder channels, motion and smart-detection zones, privacy masks, OSD and LED settings, ISP tuning and live statistics. This is large (roughly 8-15 KB of JSON). Prefer unifi_protect_list_cameras unless you specifically need a field it does not carry.
| Name | Required | Description | Default |
|---|---|---|---|
| cameraId | Yes | Camera id — the `id` from unifi_protect_list_cameras, a 24-character hex string. Not the camera's name and not its MAC address. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adss value beyonnd that by disclosing the payoff load size (8-15 KB) and inner composition (including live statistics) — info the agent needs to expect a heavy response. It does not mention auth/rate-limit context, but for a read-only get those are lower stakes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no filler: purpose is front-loaded, the size warning follows, and the sibling routing closes. Each sentence earns its place and nothing is repeated from the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-record get with one well-documented required parameter and a readOnly annotation, the description is complete. It compensates for the missing output schema by describing both the payload's contents and its approximate size, so an agent knows what to expect.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents cameraId thoroughly, incluing its source (from list_cameras), format (24-char hex), and negative guidance (not name/MAC). The description itself adds no param-level detail; per baseline with high coverage, 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource ('Get one camera's complete record') and immediately enumerates what it covers (encoder channels, zones, masks, OSD/LED, ISP tuning, live statistics). It is clearly differentiated from siblings, especially by naming 'every setting the console holds', which distinguishes it from unifi_protect_list_cameras' summary view and unifi_protect_get_camera_snapshot.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit ruouting: 'Prefer unifi_protect_list_cameras nless you speccifically need a field it does not carry.' It also provides a cost sentiment ('large, roughly 8-15KB') that juustifies preferring the lighter alternative. No inference is required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_get_camera_snapshotARead-only
Capture a still frame from a camera as it looks right now. Writes the JPEG to disk and returns its path, size and content type by default. Set output="image" to get the frame inline instead so a vision model can actually look at it — that costs roughly 300,000 to 700,000 characters of context per call, so choose it deliberately rather than by default. A fresh capture is forced; without that the console can hand back a cached frame that is minutes old.
| Name | Required | Description | Default |
|---|---|---|---|
| output | No | Where the frame goes. "file" writes it to disk and returns the path — cheap, and you can read the file later if it turns out to matter. "image" returns it inline for a model to look at, at a large cost in context. | file |
| cameraId | Yes | Camera id — the `id` from unifi_protect_list_cameras, a 24-character hex string. Not the camera's name and not its MAC address. | |
| savePath | No | Absolute path to write the JPEG to. Defaults to a timestamped file under UNIFI_PROTECT_SNAPSHOT_DIR. Parent directories are created. | |
| highQuality | No | Request the camera's full resolution rather than a scaled frame. Larger and slower; with output="image" it multiplies an already expensive call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the sparse readOnlyHint=true annotation: it discloses the disk write and returned fields, the large context cost of inline mode, and the forced-fresh-capture behavior that prevents stale cached frames. The local disk write is a client-side side effect rather than a mutation of the Protect system, so it doesn't contradict readOnlyHint=true. This is exactly the kind of behavioral context an agent needs that annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences with the core action front-loaded, followed by mode guidance, cost warning, and the cache-freshness caveat — each sentence earns its place. The cost figure is unusually specific but directly shapes agent decision-making, so it's justified. Slightly longer than the tightest possible version, but nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and only readOnlyHint=true as annotation support, the description carries the full burden and covers the essentials: return fields, both output modes, cost implications, and the staleness pitfall. Minor gaps remain — no error-case behavior and no exact return JSON shape — but since it names the return fields explicitly and covers parameter interactions, it is sufficiently complete for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3 even with no description-side parameter info. The description adds value above that baseline: it quantifies the context cost of output="image", explains the multiplicative cost when highQuality is combined with output="image", and names the default savePath directory (UNIFI_PROTECT_SNAPSHOT_DIR). These cross-parameter interactions are not present in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource pair — 'Capture a still frame from a camera as it looks right now' — which both states the action and pins the tool to live capture. The phrase 'right now' plus the forced-fresh-capture note distinguishes it from siblings like get_event_thumbnail (historical frames) and export_video (video). The default return (path, size, content type) further disambiguates what the tool produces.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear in-tool mode guidance: use output="image" only when a vision model must inspect the frame, choose output="file" by default, backed by a concrete cost range (300,000–700,000 context characters) as the decision driver. It does not explicitly name sibling alternatives for historical captures, but the 'as it looks right now' phrasing implies the live-capture context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_get_eventARead-only
Get one event's full record, including detection metadata the search results leave out — per-object tracking, detected zones, licence plate text and vehicle attributes where the camera captured them. Use the id from unifi_protect_list_events.
| Name | Required | Description | Default |
|---|---|---|---|
| eventId | Yes | Event id — the `id` from unifi_protect_list_events. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already declares the operation is read-only, and the description's 'Get' verb is consistent. The description adds meaningful behavioral context about what the record contains and includes a caveat ('where the camera captured them'), setting expectations that metadata may be incomplete. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is just two sentences with no filler. It front-loads the core purpose, expands with specific metadata examples, and ends with the crucial usage instruction. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter read-only getter with no output schema, the description adequately covers what the tool returns and where to obtain the required id. It does not describe the response structure or error behavior, but those are less critical given the tool's simplicity and the readOnly annotation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the eventId parameter already has a clear description. The tool description reinforces it by instructing the agent to use the id from unifi_protect_list_events, adding cross-tool context that helps the agent correctly source the parameter value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Get one event's full record') and explicitly distinguishes this tool from the search-like sibling by listing detection metadata the search results leave out (per-object tracking, detected zones, license plate text, vehicle attributes). An agent can clearly tell it apart from unifi_protect_list_events and other getters.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a direct usage pointer: 'Use the `id` from unifi_protect_list_events,' establishing the prerequisite and source of the required parameter. It also contrasts with 'search results,' implying this is the tool to use when the full record with detection metadata is needed. It does not explicitly enumerate exclusions, but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_get_event_thumbnailARead-only
Fetch the still image Protect captured for an event — the frame that triggered the detection. Writes it to disk and returns the path by default; set output="image" to return it inline for a vision model to look at, which costs a large amount of context. Pass the event's id from unifi_protect_list_events; results showing hasThumbnail: true have one.
| Name | Required | Description | Default |
|---|---|---|---|
| output | No | "file" writes it to disk and returns the path; "image" returns it inline. | file |
| eventId | Yes | Event id — the `id` from unifi_protect_list_events. Results with `hasThumbnail: true` have an image; others return 404. A raw `e-…` value from the console's own payload is also accepted. | |
| savePath | No | Absolute path to write the JPEG to. Defaults to a file under UNIFI_PROTECT_SNAPSHOT_DIR. Parent directories are created. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses several important behaviors: it writes to disk by default, can return inline instead, consumes significant context in image mode, returns 404 for missing thumbnails, and creates parent directories for savePath. This is rich and useful behavioral information.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: purpose first, then output modes, then input source. Each sentence earns its place, and there is no redundant or vague wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description adequately explains the return behavior: file path by default or inline image. It also covers error conditions, input provenance, and parameter options, making the tool fully understandable for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the description still adds value: it explains that eventId comes from list_events, that hasThumbnail: true indicates an image exists, that output=image costs significant context for vision models, and that savePath parent directories are created. These go beyond the raw schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the verb and resource: fetch the still image captured for a Protect event. It distinguishes this from event metadata and from sibling tools like get_camera_snapshot or get_event_thumbnails by specifying the event-triggered still frame.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context on when to use the tool: pass an event id from unifi_protect_list_events and prefer events with hasThumbnail: true. It also explains the output=image alternative for vision models. It does not explicitly name sibling alternatives or state when not to use them, but the usage context is strong.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_get_event_thumbnailsARead-only
Fetch the still frames for SEVERAL events at once and return them inline to look at. This is the tool for answering who or what was actually there, and it matters most when a camera has no smart detection: motion events carry no classification, so the only way to tell a person from a branch is to look. Prefer this over calling unifi_protect_get_event_thumbnail repeatedly. Costs roughly 1-2K tokens per image, so it is capped at 6 — pick the events worth seeing from unifi_protect_list_events rather than sweeping a whole night. Events that have no thumbnail are reported by id instead of failing the call.
| Name | Required | Description | Default |
|---|---|---|---|
| output | No | "image" returns the frames inline for a vision model to look at, which is the point of this tool; "file" writes them to disk and returns paths instead. | image |
| eventIds | Yes | Event ids from unifi_protect_list_events, at most 6. Results showing `hasThumbnail: true` have an image. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses token cost per image, a hard cap of 6, and the non-failing behavior for events without thumbnails. This is significant context that an agent needs to predict cost and handle partial results correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the main purpose and each subsequent sentence adds decision-relevant guidance: use case, sibling preference, cost/cap, and failure behavior. No sentence is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read-only tool with no output schema, this description covers when to use it, how to select inputs, cost implications, and what happens when thumbnails are missing. Nothing essential is left out.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the input schema already documents both parameters, including the eventIds cap and the output enum. The description reinforces the cap and the inline-return intent but does not add substantial parameter semantics beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb and resource ('Fetch the still frames for SEVERAL events at once') and highlights that frames are returned inline. It also clearly distinguishes this batch tool from the singular unifi_protect_get_event_thumbnail sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says to prefer this tool over calling unifi_protect_get_event_thumbnail repeatedly, and tells the agent to pick worthwhile events from unifi_protect_list_events instead of scanning a whole night. It also explains the key use case: identifying who or what was there when smart detection is unavailable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_get_system_infoARead-only
Overview of the console: model, Protect version, firmware, timezone, uptime, storage use and how many devices of each type are adopted. Worth calling first on an unfamiliar system. The reported Protect version matters: this server talks to Protect's private API, which Ubiquiti changes between releases, so a version that differs from the one in the README is the first thing to check if a tool starts returning 404.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description adds valuable behavior beyond that: it explains that the Protect version reported matters because the server talks to a private API that Ubiquiti changes between releases, and that version mismatch is a first diagnostic for 404 errors. This is genuinely useful operational context not inferable from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: the first defines return content, the second gives usage timing, the third explains a critical troubleshooting link. Dense but clear, with the most actionable information up front.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-parameter, read-only informational tool, the description fully covers its contents, when to call it, and why a specific returned field (Protect version) matters for subsequent operations. No output schema exists, but the enumerated list substitutes adequately.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema is trivially complete and the description need not explain parameter semantics. The baseline of 4 applies because there is nothing the description must compensate for.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific resource ('the console') and enumerates the exact data returned: model, Protect version, firmware, timezone, uptime, storage, and device type counts. This clearly distinguishes it from sibling tools like list_cameras or list_users by focusing on system-level overview rather than entity lists.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly recommends calling this tool 'first on an unfamiliar system,' which is strong situational guidance. It does not name alternatives or exclusions, but the tool is unique in scope among siblings, so no alternative is needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_camerasARead-only
List every camera on the console with its id, name, connection state, recording mode, firmware and what it can do (PTZ, package camera, smart detection, and which object types it detects). Returns a summary rather than the console's full camera record, which runs to thousands of fields across encoder profiles, zones and feature flags — use unifi_protect_get_camera when you need all of it for one camera.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already signals a safe read operation, and the description adds important behavioral context by disclosing that the result is a summary, not the full console record. It also explains why this matters, which helps the agent set correct expectations about response size and scope.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: the first sentence states the action and the exact output fields, and the second sentence adds the summary-vs-full distinction and sibling alternative. Every sentence earns its place without padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given zero parameters and no output schema, the description provides sufficient context by listing the expected fields, clarifying the summary nature, and pointing to the sibling tool for fuller detail. Nothing essential for invoking this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has no parameters, so the description is not required to explain parameter behavior. The zero-parameter baseline of 4 applies, and the description adds no conflicting or redundant parameter information.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List every camera on the console') and enumerates the fields returned: id, name, connection state, recording mode, firmware, and capabilities. It also explicitly contrasts itself with unifi_protect_get_camera, making the tool's scope and differentiation clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description states when to use this tool versus the alternative: it returns a summary rather than the full camera record, and explicitly directs the agent to use unifi_protect_get_camera when the full record for one camera is needed. This gives clear selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_chimesARead-only
List UniFi Protect chimes, their volume, and which doorbell cameras each is paired to.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already establishes that this is a safe read operation, and the description adds that the result includes volume and paired doorbell cameras. However, it does not mention whether the response is an array, whether pagination applies, or any authentication requirements, so some behavioral detail remains implicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence that is front-loaded with the action and resource, followed by the relevant output details. Every word earns its place; there is no filler or redundant restatement of the tool name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only list operation, the description is sufficient: it states what is listed, which fields are included, and the description is unambiguous given the sibling context. With no output schema, the description still conveys the essential return-value content without overcomplicating the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool accepts zero parameters, so there is no parameter confusion and schema coverage is trivially complete. The description therefore does not need to explain argument semantics; the baseline for a zero-parameter tool is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb 'List' and identifies the exact resource: UniFi Protect chimes. It also states the meaningful output fields (volume and paired doorbell cameras), which distinguishes it clearly from sibling list tools that target cameras, lights, sensors, or viewers.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The resource scope is explicit: use this tool when you need chime information, not camera, light, sensor, or viewer data. It does not name alternative tools or provide when-not conditions, but the noun 'chimes' supplies clear context for routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_eventsARead-only
Search recorded events over any time range — motion, smart detections (person, vehicle, animal, package, licence plate), doorbell rings, and camera connection changes. This is the tool for questions like "what happened at the front door last night". Each result carries its camera's NAME as well as its id, so no second lookup is needed. Times may be given in the console's own local clock ("1am"), which is what a question about last night means. READ ANY warnings IN THE RESULT BEFORE REPORTING A COUNT: a camera with the detector switched off returns zero matches, which is not the same as nothing having happened, and this tool says which case it is. Narrow with types, smartDetectTypes and cameraIds wherever you can: a busy system logs thousands of motion events a day.
| Name | Required | Description | Default |
|---|---|---|---|
| end | No | End of the search window. Accepts ISO 8601 ("2026-08-29T22:00:00Z"), a relative expression ("2h ago", "30m", "7d"), or "now". Converted to the millisecond epoch the console requires — do not pass Unix seconds, which would silently query 1970 and return nothing. | |
| limit | No | Maximum number of items to return (1-500). Defaults to 50. A busy system logs thousands of motion events a day, so raise this deliberately. | |
| order | No | Which end of the window to return first. | newest |
| start | No | Beginning of the search window. Accepts ISO 8601 ("2026-08-29T22:00:00Z"), a relative expression ("2h ago", "30m", "7d"), or "now". Converted to the millisecond epoch the console requires — do not pass Unix seconds, which would silently query 1970 and return nothing. | |
| types | No | Event types to include. Defaults to motion, smart detections and rings. `smartDetectZone` is the object-detection type — pair it with smartDetectTypes to ask for people or vehicles specifically. | |
| cameraId | No | Restrict to one camera — the `id` from unifi_protect_list_cameras. Omit for all cameras. Use cameraIds for several. | |
| location | No | A configured place name, e.g. "front". Resolves to the cameras covering it — the console has no idea where anything is, so this comes from UNIFI_PROTECT_LOCATIONS. Read unifi-protect://locations to see what is defined. | |
| cameraIds | No | Restrict to several cameras by id. Filtering happens on the console, so a narrow camera list over a long window returns that camera's events rather than whatever survived a fleet-wide limit. | |
| smartDetectTypes | No | What the camera classified, e.g. ["person"] or ["vehicle","licensePlate"]. Only meaningful for smartDetectZone / smartDetectLine events; the alrm* values are audio detections. Cameras without smart detection never produce these. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true; the description carries the behavioral burden and succeeds. It discloses that result warnings must be checked before reporting counts, that a disabled detector returns zero matches rather than proving nothing happened, that times follow the console's local clock, and that results include the camera name to avoid a second lookup. This is exactly the kind of non-obvious behavior an agent needs to interpret results correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but every sentence earns its place: purpose, use case, result-field convenience, timezone nuance, warning behavior, and filtering advice. It is front-loaded with the core action and structured so the most critical warning is set off in caps. The busy-system point is slightly redundant with the limit parameter description but still reinforces the needed behavior.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description explains what results carry and warns about the most failure-prone interpretation, zero matches versus disabled detection. It also addresses timezone semantics and camera-name availability. The schema covers parameter detail, so an agent has enough context to invoke the tool and interpret its result correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers all parameters with detailed descriptions, enums, defaults, and constraints, so the baseline is 3. The description's mention of types, smartDetectTypes, and cameraIds adds strategic emphasis but no new parameter semantics beyond what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Search recorded events over any time range', then enumerates the event categories covered. It clearly positions this as the list/search tool for recorded events, distinguishing it from sibling tools like get_event, get_event_thumbnail, and export_video. The example use case 'what happened at the front door last night' removes any ambiguity about the tool's role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a concrete use case and tells the agent to narrow with types, smartDetectTypes, and cameraIds, and to check warnings before reporting counts. It does not explicitly state when to prefer alternatives such as get_event or unifi_protect_get_event_thumbnail, but the context is clear enough for an agent to select this tool for broad historical queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_lightsARead-only
List UniFi Protect floodlights with their connection state, whether the light is currently on, whether PIR motion is being detected, and brightness.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint=true, the safety profile is already covered by annotations. The description adds useful behavioral context by specifying exactly which floodlight attributes are exposed, including connection state, on/off state, PIR motion detection, and brightness. It does not go into response envelope or error semantics, but for a simple list operation this is adequate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence that starts with the verb and object, then lists the four key output attributes without filler. Every element adds information needed to select and use the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-parameter, read-only listing operation, the description is complete: it identifies the resource, the type of operation, and the meaningful data fields in the result. There is no output schema, but the description supplies the essential return semantics that an agent needs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and the schema has no properties, so there are no parameters to document. With 0 params the baseline is 4; the description appropriately focuses on what the list returns rather than on parameter explanations.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and clear resource ('UniFi Protect floodlights'), and explicitly enumerates the data returned: connection state, light-on status, PIR motion detection, and brightness. This cleanly distinguishes it from sibling listing tools for cameras, sensors, chimes, viewers, and liveviews.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is for retrieving floodlight status/results, which is a clear selection context among the sibling tools. It does not name alternative tools or explicitly state when not to use it, but the resource-specific wording leaves little ambiguity for an agent matching intent to tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_liveviewsARead-only
List the saved live views — the named camera grid layouts shown on viewers and in the Protect app. The returned id is what unifi_protect_update_viewer needs to put a layout on a screen.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already establishes the read-only safety profile. The description adds useful behavioral context by explaining that live views are named grid layouts and that the returned id serves a specific purpose in unifi_protect_update_viewer, going beyond the annotation without contradicting it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler: the first states the action and defines the resource, the second explains why the return value matters. Information is front-loaded and every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only list tool with no output schema, the description covers what the tool does, what its domain term means, and what the returned id will be used for. No essential detail an agent needs to invoke this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters, so there are no parameter semantics for the description to add; per the 0-param baseline this is adequately handled. The description's mention of the returned id is output-focused, not parameter-focused, and doesn't need to compensate for any schema gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and a specific resource ('saved live views'), and defines the term as 'named camera grid layouts shown on viewers and in the Protect app.' This clearly separates it from sibling list tools like list_viewers and list_cameras, and the second sentence connects the output to a downstream tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states that the returned id is exactly what unifi_protect_update_viewer needs to place a layout on a screen, which gives a clear calling context. It does not explicitly name alternatives or when not to use the tool, but the downstream reference is sufficient for a list-with-no-filters tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_ptz_patrolsARead-only
List a PTZ camera's saved patrol routes. See unifi_protect_list_ptz_presets for why there is no tool to start or stop one.
| Name | Required | Description | Default |
|---|---|---|---|
| cameraId | Yes | Camera id — the `id` from unifi_protect_list_cameras, a 24-character hex string. Not the camera's name and not its MAC address. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already provide readOnlyHint=true, so the description does not need to restate safety. The added note that there is no start/stop tool and the pointer to unifi_protect_list_ptz_presets gives behavioral context beyond the annotation by warning agents not to search for a mutating patrol control.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences with no filler. The purpose is front-loaded, and the second sentence adds a relevant cross-reference rather than empty prose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter, read-only list operation, the description is complete enough: it names the resource, implies the required camera identifier, and routes around a nonexistent start/stop operation. The absent output schema is acceptable because the 'List' verb makes the return shape inferable and the annotations/schema cover the rest.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, cameraId, is fully documented in the schema with a precise explanation of where to find it, its format, and what it is not. The tool description adds no param-specific detail, but with 100% schema coverage the schema already carries that burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb ('List') and a concrete resource ('a PTZ camera's saved patrol routes'), so an agent immediately knows what the tool does. It also references unifi_protect_list_ptz_presets, which helps distinguish patrol routes from presets and explains the absence of a start/stop operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly states the action: list saved patrol routes for a PTZ camera. The cross-reference to unifi_protect_list_ptz_presets signals that an agent should look there to understand why start/stop tools do not exist, providing useful context for selecting among related tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_ptz_presetsARead-only
List a PTZ camera's saved preset positions, with the slot number each one lives at. Only meaningful for cameras reporting hasPtz: true in unifi_protect_list_cameras. There is no tool to MOVE a PTZ camera or run a patrol: those commands exist only on Ubiquiti's official Integration API (a separate X-API-KEY auth this server does not use), not on the private API this server wraps — presets are created and driven from the Protect app itself.
| Name | Required | Description | Default |
|---|---|---|---|
| cameraId | Yes | Camera id — the `id` from unifi_protect_list_cameras, a 24-character hex string. Not the camera's name and not its MAC address. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations provide readOnlyHint=true, and the description adds substantial context beyond that: this server wraps a private API, the official Integration API (separate X-API-KEY auth) is out of scope, and preset creation/driving happens in the Protect app. This prevents an agent from attempting write-style PTZ operations that would fail.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is front-loaded in sentence one, and each of the three sentences carries distinct value. The final sentence is dense, packing the API-scope and no-movement caveats with a colon, parenthetical, and em-dash, and could be split into two sentences without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read-only list tool with a fully documented schema, the description is nearly complete: it states return content, applicability condition, and surrounding API constraints. The only gap is unspecified behavior when given a non-PTZ camera (empty list vs. error), which is minor.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% — the cameraId property already documents that it must be the 24-character hex id from unifi_protect_list_cameras, not the name or MAC. The tool description adds no parameter details, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — lists a PTZ camera's saved preset positions plus the slot number each occupies. It differentiates from siblings by tying meaning to hasPtz: true and implicitly distinguishing presets from patrols (unifi_protect_list_ptz_patrols is a sibling).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly conditions usage on cameras reporting hasPtz: true in unifi_protect_list_cameras, telling the agent which precondition to check first. It also names what NOT to attempt — moving the camera or running a patrol — and explains those operations exist only on Ubiquiti's official Integration API with separate auth, routing the agent away from a dead end.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_sensorsARead-only
List UniFi Protect sensors with their current readings — temperature, humidity, light level — plus open/closed state, motion, and battery percentage. The readings are lifted out of the console's per-metric history arrays, which are far larger than the values themselves.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already signals a safe read, and the description adds meaningful behavioral context by explaining that readings are extracted from the console's per-metric history arrays rather than returning the full raw arrays. This helps the agent anticipate a compact, filtered response and understand why the tool behaves that way.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no wasted words: the first states the operation and output, the second gives a useful implementation detail. It is front-loaded with the most important information and remains compact.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless listing tool with no output schema, the description fully specifies the returned data categories and even the data source and transformation behavior. An agent has everything needed to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are no parameters, so the schema requires no explanation. The description adds nothing beyond the schema, but with zero parameters this is not a gap; the baseline of 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource, 'List UniFi Protect sensors', and immediately differentiates the tool from sibling list tools by enumerating the sensor-specific readings it returns. Even without an explicit sibling comparison, no agent could confuse this with list_cameras or list_lights.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description makes the use case clear: call this when you need current sensor readings and states such state such as temperature, humidity, light, open/closed, motion, and battery. It does not explicitly name alternatives or exclusion conditions, but no sibling tool covers sensors, so the context is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_usersARead-only
List the accounts that can sign in to Protect, with their role and last login. Useful for auditing who has access to the cameras.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotation readOnlyHint: true already declares the operation safe, so the description does not need to restate that. The description adds useful context about the returned data (role, last login), but does not disclose any additional behavioral details such as ordering, pagination, authentication requirements, or error behavior. It does not contradict the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences carry all the essential information: what the tool does and why an agent would use it. There is no repetition of the tool name or schema, and the primary function is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read-only list tool, the description is sufficient for an agent to select and invoke it correctly. It names the resource, the key output fields, and the intended use case. With no output schema present, the description adequately conveys what the agent should expect.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so parameter-level elaboration is unnecessary. The schema coverage is effectively complete with no properties to document, and the description adds no parameter semantics because none are needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') with a clear resource ('accounts that can sign in to Protect') and adds detail about what is returned (role and last login). It clearly distinguishes this from the camera/event/liveview listing siblings by focusing on user accounts and access auditing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a clear use case: 'Useful for auditing who has access to the cameras.' This tells the agent when to choose this tool, but it does not explicitly name alternatives or state exclusions. For a simple zero-parameter listing tool, this is sufficient but not maximally explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_list_viewersARead-only
List UniFi Protect Viewport devices and which live view each is currently displaying.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already covers the safety profile, so the description's job is to add context beyond it. It does add value by disclosing the return content (device list plus current live view per device). However, it doesn't mention whether offline/unadopted devices are included or whether authentication is required. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence with zero waste. The verb, resource, and output detail are all front-loaded in natural reading order. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only listing tool, the description is nearly complete: it names the resource and the key output attribute. No output schema exists, but the description adequately communicates what the agent will receive. Minor gaps like pagination, ordering, and handling of offline devices prevent a 5, though these are low-stakes for a simple list operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and 100% schema coverage, so the schema is trivially complete. The description need not explain parameters, and it correctly doesn't. Baseline 4 for zero-parameter tools is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('List'), a specific resource ('UniFi Protect Viewport devices'), and a distinctive output detail ('which live view each is currently displaying'). This distinguishes it from siblings like list_cameras, list_users, and list_liveviews, since it targets Viewport hardware rather than the live views themselves.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use or when-not-to-use guidance is provided, and no alternative tools are named. However, the usage is reasonably implied by the precise name and description: use this when you need informmation about Viewport devices and their currently assigned live view. The distinction from list_liveviews is left for the agent to infer rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unifi_protect_requestARead-only
Escape hatch: call any private Protect API endpoint directly, relative to /proxy/protect/api. This exists because the private API is undocumented and Ubiquiti moves endpoints between Protect releases — when a wrapped tool starts returning 404, this reaches the replacement without waiting for a new version of this server. Responses are returned RAW and unshaped, so a broad endpoint like bootstrap can return hundreds of kilobytes; prefer the wrapped tools, which summarize. Writes are DISABLED: only GET is permitted. Set UNIFI_PROTECT_ALLOW_WRITES=1 to allow mutations.
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | JSON request body, for POST and PATCH. | |
| path | Yes | Path relative to /proxy/protect/api, without a leading slash, e.g. "cameras", "nvr", "events/abc123". Not an absolute URL. | |
| query | No | Query parameters as a flat string map, e.g. {"start":"1756500000000"}. Remember that Protect times are milliseconds since the epoch. | |
| method | No | HTTP method. | GET |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations readOnlyHint=true align with the description's 'only GET is permitted'. The description adds important extra behavior not in annotations: responses are returned raw and unshaped, can be hundreds of kilobytes, and writes can be enabled via UNIFI_PROTECT_ALLOW_WRITES=1. This is exactly the kind of context agents need. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the 'escape hatch' framing, then explains rationale, warns about raw responses, and states write restrictions. Each sentence earns its place; slightly long but justified for an undocumented endpooint escape hatch.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the description covers return behavior, size caveats, write restrictions, and the conditional override. Combined with a fully described schema, an agent has enough to call and interpret this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents path, query, body, and method. The description itself does not add parameter-level details, but it does clarify the write-disabled context and raw response behavior. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific purpose: directly call any private Protect API endpoint relative to /proxy/protect/api. It differentiates from the wrapped sibling tools by framing itself as an 'escape hatch' and explicitly says 'prefer the wrapped tools, which summarize.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit when-to-use: when a wrapped tool starts returning 404 because Ubiquiti moved an endpoint. It also tells the agent to prefer wrapped tools and warns against broad endpoints like bootstrap. This is strong routing guidance relative to the siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
22 tool updates
v0.3.0- First observed
unifi_protect_auth_login - First observed
unifi_protect_auth_logout - First observed
unifi_protect_auth_status - First observed
unifi_protect_check_settings - First observed
unifi_protect_export_video - First observed
unifi_protect_get_camera - First observed
unifi_protect_get_camera_snapshot - First observed
unifi_protect_get_event - First observed
unifi_protect_get_event_thumbnail - First observed
unifi_protect_get_event_thumbnails - First observed
unifi_protect_get_system_info - First observed
unifi_protect_list_cameras - First observed
unifi_protect_list_chimes - First observed
unifi_protect_list_events - First observed
unifi_protect_list_lights - First observed
unifi_protect_list_liveviews - First observed
unifi_protect_list_ptz_patrols - First observed
unifi_protect_list_ptz_presets - First observed
unifi_protect_list_sensors - First observed
unifi_protect_list_users - First observed
unifi_protect_list_viewers - First observed
unifi_protect_request
TDQS
Scored across 22 tools
Each tool targets a distinct resource and action: cameras, events, PTZ, auth, lights, sensors, viewers, and chimes are cleanly separated. Pairs like get_camera/list_cameras and get_event_thumbnail(s)/get_camera_snapshot are clearly differentiated by scope in the descriptions.
The vast majority follow unifi_protect_<verb>_<noun> (list_cameras, get_event, export_video), and the prefix is consistent. The auth_* group reverses the order (auth_login, auth_status) and the generic request tool lacks a resource, which are minor but visible deviations.
At 22 tools the server is on the heavy side of the 16-25 borderline range, though the breadth of UniFi Protect resource types explains much of the count. A few could arguably be consolidated, but the scope is broad enough that this is defensible rather than chaotic.
Core read/retrieval workflows are well covered: cameras, snapshots, events, thumbnails, video export, and peripheral devices. However, there are notable gaps in direct write/control operations—no camera settings update or liveview update despite list_liveviews explicitly referencing unifi_protect_update_viewer—and the raw request escape hatch only helps if writes are explicitly enabled.
Maintenance
Related MCP Connectors
- mytesla.ioOAuthio.mytesla
Control your Tesla from your AI assistant - climate, charging, access, and security.
LLM chat, text tools, image generation, editing, batch image jobs, and asynchronous video generation
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
YouTube transcripts, search, channel/playlist listings and upload tracking for AI agents.
Related MCP Servers
- AlicenseDqualityDmaintenanceEnables comprehensive management of UniFi network infrastructure through the UniFi Cloud API, including device control, client management, camera settings, and access door control through natural language.3918 npmApache 2.0
- AlicenseBqualityDmaintenanceEnables AI assistants to manage and monitor UniFi Network Controllers through natural language. Provides 25 read-only tools for discovering devices and clients, viewing security configurations, analyzing network statistics, and exporting configuration data.41MIT
- AlicenseBqualityDmaintenanceEnables AI assistants to manage UniFi network infrastructure through 50+ tools covering devices, clients, networks, WiFi, firewall rules, and guest access using the official UniFi Network API.5243 npm5MIT
- FlicenseNot gradedqualityDmaintenanceProvides AI assistants with access to UniFi Network and Protect infrastructure for managing devices, monitoring clients, analyzing network health, viewing camera snapshots, and getting optimization recommendations across multiple UniFi controllers.2-