Skip to main content
Glama
mgcrea
by mgcrea

@mgcrea/mcp-unifi-network

npm version build status

Model Context Protocol server for the UniFi Network API — sites, devices, clients, hotspot vouchers, networks, WiFi and firewall configuration, plus an optional tier for the parts the official API does not cover. Read-only by default: the mutating tools are not registered at all until you opt in, so an agent cannot call them.

Features

  • Wraps the official Network Integration API (X-API-KEY, no cookie, no CSRF) as the primary transport, with server-side filtering pushed down to the console.

  • Accepts a site as its UUID, its legacy 8-character name, or its display name and translates — so the obvious guess works on the first call instead of returning a 400.

  • Registers tools against the console's actual Network version. This API had 7 endpoints in 9.0 and 44 in 10.3, so an older console is offered only what it can serve.

  • Optional legacy controller tier for what the official API lacks: blocking, unblocking and reconnecting clients, events, alarms, health, port forwarding and adoption.

  • Native fetch, no HTTP client. Two runtime dependencies plus undici — see Security.

Related MCP server: UniFi MCP Server

Security

Supply chain. Three runtime dependencies: the MCP SDK, Zod, and undici. The last is a deviation from the rest of this fleet, taken deliberately: local UniFi consoles ship self-signed certificates, Node's native fetch ignores a node:https agent, and the only scoped way to relax verification is an undici dispatcher. The alternative, NODE_TLS_REJECT_UNAUTHORIZED=0, is process-global and would silently disable verification for every other request the process makes. undici is the same engine Node's own fetch already runs.

TLS. Verification is on by default, and UNIFI_INSECURE_TLS is refused in cloud mode where it would be a pure downgrade. When it is on, the banner says tls=INSECURE on every start. Verifying instead takes two things together, and either alone achieves nothing: the certificate is self-signed, so NODE_EXTRA_CA_CERTS must point at it; and it is issued to unifi.local with no IP SAN, so UNIFI_HOST must be a host name that resolves to the console rather than its IP. See .env.example for both commands.

Your credentials. The API key and, if the legacy tier is enabled, the console password come from the environment or from ~/.config/unifi/config.json, which is warned about if it is group-readable. The legacy session cookie is a full console-admin credential and is held in memory only — never written to disk. Nothing is sent anywhere but your console.

Configuration never kills the server. A contradictory setting is resolved to the safe option and reported through the startup banner and unifi_auth_status, rather than throwing — a server that exits at startup appears in the client as a bare Connection closed with stderr swallowed, taking its own explanation with it.

Blast radius. With the defaults, this server can only read. Turning on UNIFI_ALLOW_WRITES adds: restart a device, power-cycle a PoE port, authorize and unauthorize guest access, create and delete vouchers, and — with the legacy tier — block, unblock and reconnect clients. Every irreversible one requires an explicit confirm: true that the schema enforces before the handler runs. Firewall and network configuration are read-only in every configuration: a wrong policy locks you out of the console you are managing it through, with no undo.

Configure

Variable

Default

Meaning

UNIFI_HOST

The console. A pasted browser URL is accepted and split.

UNIFI_API_KEY

Settings → Control Plane → Integrations → Create API Key.

UNIFI_MODE

inferred

unifios · cloud · classic.

UNIFI_CONSOLE_ID

Cloud mode: the console id from unifi.ui.com.

UNIFI_SITE

Default site. UUID, legacy name or display name.

UNIFI_ALLOW_WRITES

false

Register the mutating tools.

UNIFI_INSECURE_TLS

false

Disable certificate verification, this server only.

UNIFI_ENABLE_LEGACY

false

Register the unifi_legacy_* tools.

UNIFI_USERNAME / UNIFI_PASSWORD

Legacy tier fallback only. A local admin, not SSO.

UNIFI_APP_VERSION

probed

Pin the version instead of probing at startup.

UNIFI_PAGE_LIMIT / UNIFI_MAX_PAGES / UNIFI_MAX_RETRIES

50 / 20 / 3

Tuning.

UNIFI_CONFIG

~/.config/unifi/config.json

JSON alternative to all of the above.

.env.example is the annotated version. Environment variables beat the config file per field, so a one-off UNIFI_ALLOW_WRITES=0 overrides a file that says true without discarding the rest.

Quick start

npx -y @mgcrea/mcp-unifi-network

or from source:

pnpm install && pnpm build
UNIFI_HOST=192.168.1.1 UNIFI_API_KEY=… node dist/cli.js

The banner on stderr reports what it resolved:

unifi-mcp connected (mode=unifios, host=192.168.1.1, version=10.6.101, tier=full (probe),
  sites=1, integration=on, legacy=off, tls=verified, writes=disabled)

Wire into Claude Code

Copy .mcp.json.example to .mcp.json (gitignored) and fill it in.

Inspect the tools

npx @modelcontextprotocol/inspector node dist/cli.js

Tools

W = registered only with UNIFI_ALLOW_WRITES=1. ⚠️ = requires confirm: true. Needs = the minimum UniFi Network version.

Tool

What it does

Needs

unifi_auth_status

Configuration, console version, and what to set

unifi_get_console_info

Version, tier, and which capabilities are gated off here

9.0

unifi_list_sites

Every site with all three of its identifiers

9.0

unifi_list_clients

Currently connected clients

9.0

unifi_get_client

One client in full

9.3

unifi_list_devices

Adopted devices, state, model, firmware

9.0

unifi_get_device

One device in full

9.0

unifi_get_device_stats

CPU, memory, uptime, uplink throughput

9.0

unifi_list_vouchers

Hotspot guest vouchers

9.3

unifi_list_networks

Networks / VLANs (read-only)

10.0

unifi_list_wlans

WiFi broadcasts / SSIDs (read-only)

10.0

unifi_list_firewall_zones

Firewall zones (read-only)

10.0

unifi_list_firewall_policies

Zone policies, and their ordering per zone pair

10.0

unifi_request

Escape hatch for any unwrapped endpoint

W for non-GET

9.0

unifi_restart_device

Reboot a device

W ⚠️

9.0

unifi_power_cycle_port

Reboot whatever is on a PoE port

W ⚠️

9.3

unifi_authorize_guest

Let a client onto the guest network

W

9.3

unifi_unauthorize_guest

Cut a guest's access immediately

W ⚠️

9.3

unifi_create_vouchers

Generate guest vouchers

W

9.3

unifi_delete_vouchers

Delete one voucher, or every match of a filter

W ⚠️

9.3

With UNIFI_ENABLE_LEGACY=1. On a UniFi OS console this needs no extra credential — the console accepts UNIFI_API_KEY on the legacy paths too, so the flag alone is enough:

Tool

What it does

unifi_diagnose_client

"Why will this device not connect?" — one call, with a verdict

unifi_health_check

"Is my network OK?" — ranked findings across every subsystem

unifi_legacy_list_known_clients

Every client ever seen, which are blocked, and what is new

unifi_legacy_get_health

Raw per-subsystem health

unifi_legacy_list_events

Controller event log

unifi_legacy_list_alarms

Open alarms

unifi_legacy_request

Escape hatch: port forwarding, adoption, upgrades, DPI

W ⚠️ non-GET

unifi_legacy_unblock_client

Let a blocked client back on

W

unifi_legacy_block_client

Block a client by MAC

W ⚠️

unifi_legacy_reconnect_client

Kick a client so it reassociates

W ⚠️

Prompts

Clients surface these as slash commands. They carry the order to call things in and the wrong conclusions to avoid on the way — the part no single tool description can hold, because it spans several tools.

Prompt

Argument

For

/diagnose-client

device

"Why won't my lawnmower connect?"

/new-devices

days (default 7)

"Did anything new join the network?"

/network-health

"Is everything OK?"

All three are registered unconditionally, so they work before anything is configured.

Resources

unifi://troubleshooting — field notes on the API behaviours that return a successful, plausible, wrong answer. Registered unconditionally, including with no credentials, because several of them describe failures that occur before anything is configured. Read it before concluding that a device is absent, that nothing is blocked, or that a client has been offline for months.

A worked example: a device that will not connect

unifi_diagnose_client { "device": "husqvarna" }
  → { found: false, verdict: "absent", explanation: "NOT KNOWN TO THIS CONSOLE AT ALL …" }

absent is a diagnosis, not a dead end. A client record is written on association, which happens before the password is checked — so a device refused at the 802.11 authentication frame appears nowhere in the API: no record, no event, nothing. The usual cause is an orphaned block: blocking a client writes its MAC to /etc/persistent/cfg/blocked_sta on every AP, and deleting the client from the controller afterwards leaves that file behind with no way to undo it in the UI. It survives reboots and re-provisioning.

unifi_legacy_unblock_client is safe on a MAC the controller has never heard of and clears exactly this. To confirm before or after, read the AP logs the gateway already collects:

ssh <gateway> 'grep -a "<mac>" /srv/unifi/logs/remote/*.log | tail -20'

auth: disallowed by ACL is a block. Silence means the device never reached the AP at all.

A worked example: find and reboot a stuck access point

unifi_list_devices { "state": "OFFLINE" }
  → [{ id: "…", name: "Garage AP", model: "U6LR", state: "OFFLINE", … }]

unifi_get_device_stats { "deviceId": "…" }
  → { uptimeSec: 32, cpuUtilizationPct: 94, … }

unifi_restart_device { "deviceId": "…", "confirm": true }

The first call filters on the consolestate.eq('OFFLINE') goes down as a query parameter, so nothing is fetched and discarded here.

Traps worth knowing

All of these are baked into the tool descriptions, but they explain the shape of this server.

  1. unifi_list_clients returns only what is connected right now. It is not a device inventory. A blocked client, or one that has not been on the network for a week, is simply absent — so "is anything blocked?" and "why will this thing not connect?" cannot be answered from it, and an empty result reads like an all-clear when it is nothing of the kind. The Integration API has no historical view at all: there is no known-clients, blocked-clients or event endpoint anywhere in it. unifi_legacy_list_known_clients is the answer, and it is the main reason to turn the legacy tier on.

    Worse, the obvious workaround does not work. A server-side filter for the blocked state returns an empty set rather than an error for a value that does not exist:

    filter=access.type.eq('BLOCKED')          → 0 results
    filter=access.type.eq('NOT_A_REAL_VALUE') → 0 results

    So a zero from that query is not evidence of absence, and it is very easy to report a false all-clear from it. Whenever a filtered count is load-bearing, check it against a value you know is fake before you trust the zero.

  2. There are two kinds of API key. A cloud key from unifi.ui.com is not a local console key, and using one against a local console gives a 401 that looks like a typo. See Configure. A local key is accepted on the legacy paths too, which is why the legacy tier needs no console password on UniFi OS.

  3. Cloud mode cannot reach a console the Site Manager API does not list. UNIFI_CONSOLE_ID has to come from GET https://api.ui.com/v1/hosts, and that listing is not the same as what unifi.ui.com shows you. A console grouped into a Fabric — several consoles (Network, Protect, NAS) presented under one name — appears in the web UI but not in /v1/hosts, even with cloudConnected: true on the console itself. Observed on a UDM-Pro that the portal showed and the API did not, on both /v1/hosts and /ea/hosts, with no pagination involved. For such a console there is no host id, so cloud mode is unavailable and you need a local Integration key with UNIFI_MODE=unifios.

  4. siteId is a UUID, not default. A site has three identifiers: the UUID this API's paths take, the legacy 8-character internalReference that appears in every controller URL and forum post, and a display name. Every tool accepts all three. The legacy tools need the internalReference, and that translation happens for you too.

  5. The endpoint set depends on the console's version. 7 paths in 9.0, 12 in 9.3, 32 in 10.0, 44 in 10.3. The server probes GET /v1/info at startup and registers accordingly, so the tool list can differ between two runs against different consoles. unifi_get_console_info says why. If the console cannot be reached at startup the server still comes up, assumes the newest version, and lets any gap surface as an error naming the version it needs — a visible failure beats a silently missing tool.

  6. Local consoles use self-signed certificates, and pinning one is not enough. The certificate is issued to unifi.local with no IP SAN, so a console addressed by IP fails verification however the certificate is trusted — you need a host name too. See Security.

  7. The classic self-hosted controller has no Integration API. API keys are UniFi OS only, so port 8443 means the legacy tier or nothing. The config refuses the contradictory combination rather than failing later at request time.

  8. The legacy API reports errors with HTTP 200. {"meta":{"rc":"error"}} is a failure however healthy the status line looks. That is unwrapped for you in one place.

  9. Legacy payloads are enormous — a stat/device object declares ~423 fields and one UDM-Pro is 50–150 KB. Legacy responses are projected down, and unifi_legacy_request refuses anything over 5 MB rather than parsing it. Pass attrs and _limit.

  10. Login is rate-limited. The legacy session is established once per server start and reused; a 429 on login is never retried, because retrying deepens the lockout.

Troubleshooting

The server does not appear / Connection closed. Run node dist/cli.js by hand with the same environment and read stderr — this server is built never to exit on missing configuration, so a real crash is visible there.

A tool I expected is missing. Call unifi_auth_status, then unifi_get_console_info. It is almost always the version gate or the write flag, both of which unregister rather than refuse.

401 on every call. The key is per-console and shown only once. Re-create it under one of two different kinds of key, which are not interchangeable and which produce a confusing 401 when mixed up:

  • Local Integration key — created on the console itself at https://<console>/network/default/settings/control-plane/integrations. This is what UNIFI_MODE=unifios needs. Use the URL rather than hunting the sidebar: on Network 10.6 Control Plane lives under a heading named after your console at the bottom of the settings sidebar, below System, which is why it gets reported as missing.

  • Cloud Site Manager key — created at https://unifi.ui.com/settings/api-keys. A console's local API rejects this with a 401. It is used with UNIFI_MODE=cloud and UNIFI_CONSOLE_ID, which also works behind CGNAT and needs no TLS workaround at all.

Either kind is shown once and can afterwards only be renamed or deleted, any admin can create one, and creation sometimes errors on the first attempt, so retry before assuming it is broken.

fetch failed / certificate errors. Self-signed certificate; see Security.

Develop

pnpm dev            # tsdown --watch
pnpm test           # vitest, offline, no credentials needed
pnpm typecheck
pnpm lint && pnpm format

Release:

pnpm dlx release-it        # bump, commit, tag
git push --follow-tags     # CI publishes to npm and cuts the GitHub release

The offline suite covers the registration matrix, the confirm gates, site resolution and both error envelopes. The real-console check is the curl probe in .env.example plus the inspector.

License

MIT

Available Tools

14 tools
unifi_auth_statusUniFi: Auth StatusA
Read-only

Report whether this server can reach a UniFi console, which transport and site it uses, what Network version the console runs, and — when something is missing — exactly what to set. Call this FIRST whenever a tool you expected is not in the list: on this API the available endpoints depend on the console's version, so an absent tool usually means an older console or missing configuration rather than a bug.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true, and the description confirms it reports/checks rather than mutates. It adds valuable context beyond the flag: it describes what is probed (reachability, transport, site, Network version) and that it returns settings guidance when something is missing. No hidden side effects or auth requirements are disclosed, but read-only is already covered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, each serving a distinct purpose: the first enumerates the report content, the second gives an actionable usage directive. The most important usage hint is placed at the start of the second sentence, with minimal waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only diagnostic with no output schema, the description fully covers what the tool does, when to use it, and what it reports. It even includes edge-case interpretation (absent tool = older console/missing config), so an agent has enough to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With zero parameters and vacuous 100% schema coverage, there are no parameter semantics for the description to add. The baseline for a no-parameter tool is 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Report') and resource (connectivity/auth status to a UniFi console), and enumerates the exact facts it returns: transport, site, Network version, and remediation. This distinguishes it from sibling data-fetching tools like unifi_list_clients and from unifi_request.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly directs the agent to 'Call this FIRST whenever a tool you expected is not in the list,' and explains the reason (endpoints depend on console version). This gives a clear decision rule for when to use this tool instead of assuming an API bug.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_get_clientUniFi: Get ClientA
Read-only

Get the full detail of one connected client by its id from unifi_list_clients. Returns everything the console knows about that session, including its uplink device and guest-access state.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
clientIdYesThe client's `id` from `unifi_list_clients` — a UUID, NOT its MAC address. Client ids change when a client reconnects, so list first rather than reusing an old one.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint already present, the description adds useful behavioral context by explaining what the call returns: the full session detail, including uplink device and guest-access state. It does not cover stale-ID error behavior, but that is a minor gap for a simple read operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no filler. It front-loads the action and identifier source, then adds a compact but valuable summary of the return payload.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only get-by-id tool with thorough schema documentation and a readOnlyHint annotation, the description is complete. It explains what it returns and the provenance of the required id, and no output schema is needed because the return behavior is summarized clearly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and both parameters are already fully documented in the schema, including the UUID-not-MAC caveat and site alias resolution. The tool description adds little beyond reinforcing the id-from-list relationship, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Get'), a precise resource ('one connected client'), and the key identifier source ('from unifi_list_clients'). It clearly distinguishes this single-item lookup from the list-oriented sibling tools like unifi_list_clients.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description and clientId parameter make the intended workflow clear: first call unifi_list_clients, then pass the returned id to this tool. It does not explicitly name alternatives or exclusions, but the 'list first' guidance and read-only nature provide clear context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_get_console_infoUniFi: Get Console InfoA
Read-only

Report the console's UniFi Network version and which tools it supports. This API gained most of its endpoints in Network 10.0, so on an older console a large part of the tool set simply does not exist. Call this when a tool is missing or returns a 404 — it names the version each capability needs and lists what is gated off here.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the safety profile is established. The description adds meaningful behavioral context beyond the annotation: it explains the API version dependency, that older consoles will have missing tools, and that this tool names required versions and gated capabilities. This helps the agent anticipate failures in related tools.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences with no filler. The core purpose is front-loaded, the version-context sentence explains why the tool exists, and the final sentence provides a clear call-to-action. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only diagnostic tool with no output schema, the description is complete. It states what the tool reports, why the information matters, and exactly when an agent should invoke it. There are no missing inputs, side effects, or complex output expectations to clarify.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, and the schema is empty, so the description carries no parameter burden. The baseline for zero-parameter tools is 4, and the description appropriately explains the tool's purpose without inventing unnecessary parameter details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool reports the console's UniFi Network version and its supported tool set, using a specific verb and resource. It is immediately distinguishable from sibling tools that operate on clients, devices, sites, or networks, as this is a meta-tool about the console's capabilities.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit usage conditions: call when a tool is missing or returns 404, and explains why (older consoles lack endpoints introduced in Network 10.0). This is direct, actionable guidance for an agent to decide when this tool is the right choice.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_get_deviceUniFi: Get DeviceA
Read-only

Get one adopted device in full, including its features, interfaces, uplink and adoption timestamps — everything unifi_list_devices trims away.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
deviceIdYesThe device's `id` from `unifi_list_devices` — a UUID, NOT its MAC address and NOT its name.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already establishes this as a safe read operation, and the description builds on that by clarifying the depth of data returned and its relationship to the list variant. It does not disclose behavior for missing devices, but for a read-only get-by-id tool the annotation plus the detail 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.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One well-structured sentence front-loads the core action and resource, then adds specific differentiators and the sibling comparison. Every clause earns its place with no filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only fetch tool with only one required parameter, the description plus the rich schema and readOnlyHint fully covers what an agent needs: which id to pass, which site semantics apply, and what richness to expect in the response.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and each parameter already has a rich description: site resolution rules and the deviceId's origin and format. The tool description adds no parameter-level meaning, but it does not need to because the schema carries the full burden.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

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 adopted device in full.' It also distinguishes itself from unifi_list_devices by naming the exact fields it adds (features, interfaces, uplink, adoption timestamps), making the purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly implies when to use this tool: when you need the full single-device payload that unifi_list_devices trims away. It names the alternative tool and what differentiates them, though it stops short of explicitly stating excluded cases such as 'use unifi_list_devices for summary lists.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_get_device_statsUniFi: Get Device StatsA
Read-only

Get a device's latest telemetry: uptime, CPU and memory utilization, load averages, uplink throughput and per-radio transmit retry rates. This is the current snapshot the console holds, not a time series — there is no history endpoint in this API.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
deviceIdYesThe device's `id` from `unifi_list_devices` — a UUID, NOT its MAC address and NOT its name.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint annotation, the description discloses a non-obvious behavioral trait: the returned data is the console's current snapshot and no history endpoint exists. This tells the agent the result will not be a time series and the API cannot fulfill historical requests. This is exactly the kind of extra context that helps invocation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The first sentence front-loads the tool's purpose and payload; the second adds a crucial caveat about snapshot-only semantics. Every sentence earns its place and the scope is instantly clear.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only telemetry tool, the combination is complete: schema explains both parameters, annotations mark it read-only, and the description states what data is returned and that it is snapshot-only. Without an output schema, the description still gives the agent enough understanding of the returned fields to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully explains deviceId (UUID from unifi_list_devices, not MAC/name) and site (three accepted forms, defaulting behavior, and unifi_list_sites reference). The description adds no parameter information, but with full schema coverage, baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

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 a device's latest telemetry', and enumerates exactly which metrics are included (uptime, CPU/memory utilization, load averages, uplink throughput, per-radio retry rates). It also distinguishes the tool from history/time-series endpoints and from generic device retrieval siblings like unifi_get_device.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'This is the current snapshot the console holds, not a time series — there is no history endpoint in this API' clearly tells the agent when this tool is not appropriate (when historical data is needed), while the 'latest telemetry' wording implies its intended use. It stops short of naming explicit alternative tools for historical queries, but the 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_list_clientsUniFi: List ClientsA
Read-only

List the clients currently connected to a site — the devices on your network right now, not the historical list. Each entry carries the client id (a UUID the other client tools take), its name, type, IP, MAC and when it connected. Filtering happens on the console, so a filtered call is cheaper than reading pages and discarding them here.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoMatch the client name, with `*` as a wildcard — e.g. "*iphone*".
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
typeNoConnection type to filter by. Omit for all types.
limitNoMaximum items to return (1-200; the console rejects more). Defaults to UNIFI_PAGE_LIMIT (50). Prefer narrowing with `filter` over raising this — the console filters server-side, so a filtered request is both smaller and faster than a large page read here.
filterNoRaw server-side filter expression, applied by the console before it answers — much cheaper than fetching pages and filtering here. Syntax: `property.function(value)`, combined with `and(...)`, `or(...)` and `not(...)`. Strings take single quotes (double an embedded quote to escape it); `*` is the wildcard in `like`. Functions: eq, ne, gt, ge, lt, le, like, in, notIn, isNull, isNotNull, contains, containsAny, containsAll. Examples: `state.eq('OFFLINE')`, `firmwareUpdatable.eq(true)`, `and(type.eq('WIRED'),name.like('*lab*'))`. The structured arguments on this tool build the common expressions for you — use this only for what they cannot express, and do not pass both.
connectedSinceNoOnly clients connected at or after this ISO 8601 timestamp, e.g. "2026-08-30T00:00:00Z".

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already signals safety, and the description adds useful behavioral context: this is a live snapshot, not history, and filtering is performed on the console for cost efficiency. It does not cover rate limits or auth, but the annotation plus the cost-oriented note are sufficient for this read-only listing tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three dense sentences with no filler. The core scoping is front-loaded, the output fields are summarized, and the filtering cost behavior is stated in one efficient sentence. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description partially covers the return shape by listing the fields each entry carries. The rich parameter schema handles invocation details, and the filtering behavior is explained. Exact pagination or response envelope details are not stated, but for an optional-parameter list tool this is nearly complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

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 every parameter well. The description adds general context about server-side filtering and mention of the client id being reused by other tools, but it does not need to repeat per-parameter semantics. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource: 'List the clients currently connected to a site', and adds a clear distinction from the historical list. It also identifies what each entry contains, making the tool's purpose unambiguous and distinct from unifi_get_client and unifi_list_devices.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly frames when this tool is appropriate: for current network clients, not historical records, and it notes that server-side filtering is cheaper than fetching pages and discarding data. It does not explicitly name sibling alternatives and when to prefer them, but the context is strong enough for an agent to route correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_list_devicesUniFi: List DevicesA
Read-only

List the UniFi devices adopted by a site — access points, switches, gateways — with their state, model, IP, MAC and firmware. Use firmwareUpdatable: true to find what needs upgrading, or state: "OFFLINE" to find what is down. The nested features and interfaces blocks are omitted here; unifi_get_device returns the complete object for one device.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoMatch the device name, with `*` as a wildcard — e.g. "*garage*".
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
limitNoMaximum items to return (1-200; the console rejects more). Defaults to UNIFI_PAGE_LIMIT (50). Prefer narrowing with `filter` over raising this — the console filters server-side, so a filtered request is both smaller and faster than a large page read here.
stateNoOnly devices in this state.
filterNoRaw server-side filter expression, applied by the console before it answers — much cheaper than fetching pages and filtering here. Syntax: `property.function(value)`, combined with `and(...)`, `or(...)` and `not(...)`. Strings take single quotes (double an embedded quote to escape it); `*` is the wildcard in `like`. Functions: eq, ne, gt, ge, lt, le, like, in, notIn, isNull, isNotNull, contains, containsAny, containsAll. Examples: `state.eq('OFFLINE')`, `firmwareUpdatable.eq(true)`, `and(type.eq('WIRED'),name.like('*lab*'))`. The structured arguments on this tool build the common expressions for you — use this only for what they cannot express, and do not pass both.
firmwareUpdatableNoTrue to list only devices with a firmware update available.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true already covers the safety profile; the description adds the behavioral disclosure that nested features/interfaces blocks are omitted and points to unifi_get_device for the full object. It does not contradict the annotation and provides useful response-shape context beyond it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each earning its place: the core function, two high-value use cases, and the important omission/alternative note. The core purpose is front-loaded and there is no repetition of schema content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description still tells the agent what fields come back and what is deliberately omitted. The 100%-documented schema covers all six parameters and the annotations cover read-only safety, so nothing an agent needs to invoke this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with unusually rich per-parameter descriptions, so the baseline is 3. The description adds practical task-to-parameter mappings (firmwareUpdatable for upgrade hunting, state OFFLINE for downtime triage) that go slightly beyond the schema's own text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

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 the UniFi devices adopted by a site" — and enumerates the device types (access points, switches, gateways) and returned fields (state, model, IP, MAC, firmware). It also distinguishes itself from unifi_get_device by noting the omitted nested blocks, so an agent can tell the two apart without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives task-oriented guidance — "Use firmwareUpdatable: true to find what needs upgrading, or state: 'OFFLINE' to find what is down" — and explicitly routes the complete-object case to unifi_get_device. The alternative and the condition that selects it are both named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_list_firewall_policiesUniFi: List Firewall PoliciesA
Read-only

List the zone-based firewall policies on a site, with their action, source and destination zones, matching criteria and whether each is enabled. Policies are evaluated in order, so pass sourceZoneId and destinationZoneId to see the ordering that actually applies between one pair of zones. Read-only, deliberately: a wrong policy can lock you out of the console with no undo.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
limitNoMaximum items to return (1-200; the console rejects more). Defaults to UNIFI_PAGE_LIMIT (50). Prefer narrowing with `filter` over raising this — the console filters server-side, so a filtered request is both smaller and faster than a large page read here.
filterNoRaw server-side filter expression, applied by the console before it answers — much cheaper than fetching pages and filtering here. Syntax: `property.function(value)`, combined with `and(...)`, `or(...)` and `not(...)`. Strings take single quotes (double an embedded quote to escape it); `*` is the wildcard in `like`. Functions: eq, ne, gt, ge, lt, le, like, in, notIn, isNull, isNotNull, contains, containsAny, containsAll. Examples: `state.eq('OFFLINE')`, `firmwareUpdatable.eq(true)`, `and(type.eq('WIRED'),name.like('*lab*'))`. The structured arguments on this tool build the common expressions for you — use this only for what they cannot express, and do not pass both.
sourceZoneIdNoZone `id` from `unifi_list_firewall_zones`. Pass with `destinationZoneId` to get the evaluation order between that pair rather than the flat list.
destinationZoneIdNoZone `id` from `unifi_list_firewall_zones`. Pass with `sourceZoneId`.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint=true, and the description reinforces this with a behavioral warning: 'Read-only, deliberately: a wrong policy can lock you out of the console with no undo.' It also discloses the evaluation-order behavior, adding meaningful context beyond the annotation. No contradiction exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the primary action and return contents, then the key ordering behavior and a safety caveat. Every sentence earns its place; there is no redundant filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description compensates by listing what the response contains. It also explains evaluation ordering, the purpose of the two zone parameters, and the safety rationale for read-only use. Combined with the fully documented parameters, an agent has everything needed to call the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents each parameter. The description adds a brief behavioral note about ordering with sourceZoneId and destinationZoneId, but most parameter meaning is already carried by the schema. This meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'List the zone-based firewall policies on a site', and enumerates the returned attributes (action, source/destination zones, matching criteria, enabled state). It clearly differentiates from siblings like unifi_list_firewall_zones by focusing on policies rather than zones.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context on when to use the zone-pair parameters ('Policies are evaluated in order, so pass sourceZoneId and destinationZoneId to see the ordering') and signals a safe read-only usage context. It does not explicitly name alternative tools or exclusion conditions, but the usage context is strong enough for an agent to decide correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_list_firewall_zonesUniFi: List Firewall ZonesA
Read-only

List the firewall zones on a site — the named groups of networks that zone-based policies are written between. Read these first: a policy references zones by id, so the ids here are what make unifi_list_firewall_policies readable. This server never creates or modifies firewall configuration; make those changes in the UniFi UI, where a mistake can be undone before it locks you out.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
limitNoMaximum items to return (1-200; the console rejects more). Defaults to UNIFI_PAGE_LIMIT (50). Prefer narrowing with `filter` over raising this — the console filters server-side, so a filtered request is both smaller and faster than a large page read here.
filterNoRaw server-side filter expression, applied by the console before it answers — much cheaper than fetching pages and filtering here. Syntax: `property.function(value)`, combined with `and(...)`, `or(...)` and `not(...)`. Strings take single quotes (double an embedded quote to escape it); `*` is the wildcard in `like`. Functions: eq, ne, gt, ge, lt, le, like, in, notIn, isNull, isNotNull, contains, containsAny, containsAll. Examples: `state.eq('OFFLINE')`, `firmwareUpdatable.eq(true)`, `and(type.eq('WIRED'),name.like('*lab*'))`. The structured arguments on this tool build the common expressions for you — use this only for what they cannot express, and do not pass both.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Although readOnlyHint is already true, the description adds meaningful behavioral context: it clarifies the tool's read-only relationship to firewall configuration, tells the agent to make changes elsewhere, and explains why (mistakes can be undone before locking you out). This goes well beyond the annotation and helps the agent reason about safety and workflow.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences accomplish a lot: definition, workflow relationship to a sibling tool, and a safety-oriented usage exclusion. The most important information is front-loaded, and every sentence earns its place with no filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple list tool with zero required parameters, 100% schema coverage, and a read-only annotation, the description is complete. It explains what the tool returns conceptually (zones with ids), how those ids feed into unifi_list_firewall_policies, and where modifications should happen. No material gap remains for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

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 fully documents site, limit, and filter with detailed descriptions. The main tool description adds no parameter-level information, which is acceptable given the schema's thoroughness. Baseline 3 applies because the schema carries the burden.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

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: 'List the firewall zones on a site.' It further defines what these zones are ('named groups of networks') and distinguishes this tool from the related unifi_list_firewall_policies by explaining that zones are referenced by id in policies. This makes the tool's purpose immediately distinguishable from its siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit usage guidance: 'Read these first' before reading policies, since the zone ids are needed to make unifi_list_firewall_policies readable. It also states a clear exclusion — this server never creates or modifies firewall configuration and such changes should be made in the UniFi UI — so an agent knows when not to use this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_list_networksUniFi: List NetworksA
Read-only

List the networks (VLANs) configured on a site — their name, VLAN id, subnet, DHCP settings and purpose. Read-only: this server does not create or modify networks, because a wrong subnet or VLAN id disconnects every client on it with no undo. Make those changes in the UniFi UI.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
limitNoMaximum items to return (1-200; the console rejects more). Defaults to UNIFI_PAGE_LIMIT (50). Prefer narrowing with `filter` over raising this — the console filters server-side, so a filtered request is both smaller and faster than a large page read here.
filterNoRaw server-side filter expression, applied by the console before it answers — much cheaper than fetching pages and filtering here. Syntax: `property.function(value)`, combined with `and(...)`, `or(...)` and `not(...)`. Strings take single quotes (double an embedded quote to escape it); `*` is the wildcard in `like`. Functions: eq, ne, gt, ge, lt, le, like, in, notIn, isNull, isNotNull, contains, containsAny, containsAll. Examples: `state.eq('OFFLINE')`, `firmwareUpdatable.eq(true)`, `and(type.eq('WIRED'),name.like('*lab*'))`. The structured arguments on this tool build the common expressions for you — use this only for what they cannot express, and do not pass both.

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already marks this as read-only, and the description adds meaningful context: this server intentionally cannot modify networks because a wrong subnet or VLAN id would disconnect every client with no undo. This explains why the tool is constrained and reinforces safe behavior 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.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: the first sentence defines purpose and output, and the second justifies the read-only constraint. No sentence is wasted, and the safety rationale earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only list tool with no output schema, the description provides enough return-value context by naming the key fields, and the schema fully documents all three parameters. There is no missing information an agent needs to select and invoke this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, and each parameter has a rich description covering accepted formats, defaults, bounds, and filter syntax. The tool description itself adds no additional parameter semantics, so the schema carries the full burden, matching the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('List') on a specific resource ('networks (VLANs) configured on a site') and enumerates the returned fields: name, VLAN id, subnet, DHCP settings, and purpose. This clearly distinguishes the tool from siblings like unifi_list_wlans by scoping it to wired/VLAN networks.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives useful context by calling the operation read-only and directing modifications to the UniFi UI, but it does not explicitly name sibling tools or state when to prefer this tool over alternatives such as unifi_list_sites or unifi_list_wlans. Usage context is implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_list_sitesUniFi: List SitesA
Read-only

List the sites on this console with ALL THREE of their identifiers: the id (a UUID, which is what this API's paths take), the internalReference (the legacy 8-character name such as "default", which the unifi_legacy_* tools take) and the display name. Every other tool accepts any of the three for its site argument, so this is mostly useful when a site cannot be resolved or when you need the legacy name.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already declares the safe read-only nature. The description adds meaningful context beyond that by explaining what the returned identifiers are used for (API paths vs legacy tools), which helps the agent interpret results correctly. 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.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no fluff. The main action and purpose appear first, followed by precise identifier semantics and usage guidance. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

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 fully covers what will be returned and why it matters. It gives the agent enough to know when to invoke this tool and how to use the results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has no parameters, so there is nothing to document. The description appropriately focuses on the return values instead, which is the most useful semantic information an agent needs here.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('List') and resource ('sites on this console') plus the key output detail: all three identifiers. It clearly distinguishes this from sibling list tools that target clients, devices, networks, etc.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly explains when this tool is needed: when a site cannot be resolved or when the legacy internalReference name is required. It also clarifies that every other tool accepts any of the three identifiers, so the agent can decide when listing sites is actually necessary.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_list_vouchersUniFi: List VouchersA
Read-only

List the hotspot guest vouchers on a site, with their code, name, time and data limits, how many guests have used each, and when it activates and expires. An expired voucher is not deleted automatically — filter with expired: false for the ones still usable.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoMatch the voucher name, with `*` as a wildcard.
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
limitNoMaximum items to return (1-200; the console rejects more). Defaults to UNIFI_PAGE_LIMIT (50). Prefer narrowing with `filter` over raising this — the console filters server-side, so a filtered request is both smaller and faster than a large page read here.
filterNoRaw server-side filter expression, applied by the console before it answers — much cheaper than fetching pages and filtering here. Syntax: `property.function(value)`, combined with `and(...)`, `or(...)` and `not(...)`. Strings take single quotes (double an embedded quote to escape it); `*` is the wildcard in `like`. Functions: eq, ne, gt, ge, lt, le, like, in, notIn, isNull, isNotNull, contains, containsAny, containsAll. Examples: `state.eq('OFFLINE')`, `firmwareUpdatable.eq(true)`, `and(type.eq('WIRED'),name.like('*lab*'))`. The structured arguments on this tool build the common expressions for you — use this only for what they cannot express, and do not pass both.
expiredNoTrue for expired vouchers only, false for still-valid ones.

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint=true already covering the safety profile, the description adds genuine behavioral value: it discloses that expired vouchers are not deleted automatically and will still appear in results, which an agent would not otherwise assume. It also previews the return content, giving the agent expectations for what a successful call yields without an output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with zero waste. The first sentence front-loads the purpose and return fields; the second delivers the expiry gotcha and the corrective filter. Both sentences earn their place, and information density is high without becoming a wall of text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only list operation with zero required parameters, a complete schema, and a safety annotation, the description covers the essentials: what is returned, the key behavioral caveat (expired vouchers persist), and the filter that addresses it. Minor gaps — no explicit ordering, pagination hint beyond what the limit param already states — are negligible given the schema richness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema's own parameter descriptions are exceptionally rich (site resolution across three forms, full filter-expression syntax, limit defaults and rejection behavior). The main description adds only the `expired: false` hint, which largely restates the schema's 'false for still-valid ones.' The schema carries the weight here, so baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

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 — "List the hotspot guest vouchers on a site" — and enumerates the returned fields (code, name, time/data limits, guest usage count, activation/expiration). This clearly distinguishes it from sibling list tools like unifi_list_clients, unifi_list_devices, and unifi_list_sites, all of which target different resources.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives conditional filtering guidance ('filter with `expired: false` for the ones still usable') and explains why the filter is needed, which is useful context for when to apply it. However, it never names alternatives or states when NOT to use this tool versus a sibling list tool or unifi_request, leaving tool-selection reasoning mostly to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_list_wlansUniFi: List WLANsA
Read-only

List the WiFi broadcasts (SSIDs) on a site, with the network each is bridged to, its security mode, band and whether it is enabled. Read-only for the same reason as networks: a bad SSID change takes every wireless client offline at once.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoWhich site to act on. Accepts the site UUID, its `internalReference` (the legacy 8-character name, usually "default") or its display name ("Default") — all three are resolved for you, so a guess costs nothing. Defaults to UNIFI_SITE, or to the only site when this console has just one. `unifi_list_sites` shows all three for every site.
limitNoMaximum items to return (1-200; the console rejects more). Defaults to UNIFI_PAGE_LIMIT (50). Prefer narrowing with `filter` over raising this — the console filters server-side, so a filtered request is both smaller and faster than a large page read here.
filterNoRaw server-side filter expression, applied by the console before it answers — much cheaper than fetching pages and filtering here. Syntax: `property.function(value)`, combined with `and(...)`, `or(...)` and `not(...)`. Strings take single quotes (double an embedded quote to escape it); `*` is the wildcard in `like`. Functions: eq, ne, gt, ge, lt, le, like, in, notIn, isNull, isNotNull, contains, containsAny, containsAll. Examples: `state.eq('OFFLINE')`, `firmwareUpdatable.eq(true)`, `and(type.eq('WIRED'),name.like('*lab*'))`. The structured arguments on this tool build the common expressions for you — use this only for what they cannot express, and do not pass both.

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation is reinforced and enriched by the explanation that 'a bad SSID change takes every wireless client offline at once,' which gives the agent a concrete consequence justifying the safety profile. It also discloses the shape of the result (fields returned), which the annotations and output schema do not provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero waste: the first front-loads the action and return fields, the second justifies the safety posture. Every clause earns its place, and the read-only rationale is placed after the purpose rather than obscuring it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list tool, the package is complete: annotations cover the safety profile, parameter docs cover invocation details, and the description enumerates the return fields in the absence of an output schema. Only a minor gap exists around the exact response shape/pagination behavior, which the limit parameter largely covers.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and each parameter is richly documented (site name resolution, limit bounds with server rejection, full filter syntax with examples). The main description adds no parameter information, so the baseline of 3 applies — the schema does the heavy lifting and there is no gap to compensate for.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource — 'List the WiFi broadcasts (SSIDs) on a site' — and enumerates the distinguishing return fields (bridged network, security mode, band, enabled state). This clearly separates it from sibling list tools like unifi_list_networks and unifi_list_clients without needing to open any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The domain of use is implied ('this is the tool for SSIDs'), and the phrase 'same reason as networks' obliquely connects it to a sibling tool's rationale. However, it never explicitly states when to choose this over unifi_list_networks or other list tools, and offers no when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unifi_requestUniFi: RequestA
Read-only

Escape hatch: call any Integration API endpoint directly, for the parts of the API this server does not wrap — switch stacks, LAG, VPN servers, RADIUS profiles, WAN interfaces, device tags, ACL rules, DNS policies and the DPI reference tables. Paths are relative to the API root, so /sites and /sites/<uuid>/wans, and the site must be a real UUID here — this tool does NOT resolve site names, so get one from unifi_list_sites first. Writes are DISABLED: only GET is permitted. Set UNIFI_ALLOW_WRITES=1 to allow mutations, which also registers the purpose-built write tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
bodyNoJSON request body, for POST, PUT and PATCH. Sent verbatim.
pathYesPath relative to the Integration API root, starting with `/` — e.g. `/info`, `/sites`, `/sites/<site-uuid>/wans`. Not a full URL.
queryNoQuery parameters, e.g. `{"limit": 50, "filter": "state.eq('OFFLINE')"}`.
methodNoHTTP method. Only GET is available while writes are disabled.GET

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description adds concrete behavior beyond that: only GET is permitted, site names are not resolved, and UNIFI_ALLOW_WRITES=1 changes the behavior and registers purpose-built write tools. There is no contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three dense sentences deliver purpose, scope, path rules, UUID requirements, and write behavior without filler. The escape-hatch concept is front-loaded, and every sentence contributes meaningful guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an intentionally open-ended raw API tool with no output schema, the description gives all invocation-critical information: scope, relative path format, UUID prerequisite, method restriction, and the environment-variable switch for writes. No obvious required detail is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is already strong. The description adds one crucial parameter-level insight beyond the schema: paths are API-root-relative, not full URLs, and the site must be a real UUID because name resolution is not performed. This is above baseline but not exhaustive across all parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'call any Integration API endpoint directly,' and scopes it to 'the parts of the API this server does not wrap.' This cleanly distinguishes the tool from the sibling wrapper tools without requiring the agent to inspect them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says when to use the escape hatch — for unwrapped endpoints — and provides examples. It also warns that site names are not resolved and directs the agent to `unifi_list_sites` first, plus it explains the write-disabled default and how to enable mutations while pointing to purpose-built write tools.

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.

  1. 14 tool updatesv0.5.0
    • Changedunifi_auth_status1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_get_client1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_get_console_info1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_get_device1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_get_device_stats1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_list_clients1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_list_devices1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_list_firewall_policies1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_list_firewall_zones1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_list_networks1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_list_sites1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_list_vouchers1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_list_wlans1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedunifi_request1 field changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
  2. 14 tool updatesv0.4.0
    • First observedunifi_auth_status
    • First observedunifi_get_client
    • First observedunifi_get_console_info
    • First observedunifi_get_device
    • First observedunifi_get_device_stats
    • First observedunifi_list_clients
    • First observedunifi_list_devices
    • First observedunifi_list_firewall_policies
    • First observedunifi_list_firewall_zones
    • First observedunifi_list_networks
    • First observedunifi_list_sites
    • First observedunifi_list_vouchers
    • First observedunifi_list_wlans
    • First observedunifi_request

TDQS

A4.2/5.0

Scored across 14 tools

Disambiguation4/5

Most tools map cleanly to distinct resource/action pairs: list/get for devices and clients, list for networks, WLANs, vouchers, firewall objects, plus a generic escape hatch. The main ambiguity is between unifi_auth_status and unifi_get_console_info, since both report console version and supported-tool gaps, though their troubleshooting angles differ.

Naming Consistency4/5

The tools overwhelmingly follow a unifi_<verb>_<noun> pattern with list/get prefixes and snake_case throughout. Minor deviations are unifi_auth_status, which is noun-like rather than verb-driven, and unifi_request, a bare catch-all, but the overall convention remains readable and predictable.

Tool Count5/5

Fourteen tools is a reasonable, well-scoped size for a network management API covering sites, devices, clients, networks, WLANs, vouchers, firewall configuration, console diagnostics, and a raw request escape hatch. The count stays within the comfortable range and each tool has a clear role in the overall set.

Completeness4/5

For a deliberately read-only inventory and status server, the surface covers the primary UniFi Network resources well: sites, devices, clients, networks, WLANs, vouchers, firewall zones/policies, device stats, and console capabilities. Minor gaps remain around site-level health/history and write operations, though writes are intentionally disabled and unifi_request can reach unwrapped read endpoints.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    Enables 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.
    41
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables comprehensive management of UniFi Network infrastructure through 24 tools for monitoring and controlling devices, clients, wireless networks, security, and guest access. Supports network administration tasks like device restarts, client blocking, WLAN configuration, and backup creation.
    10 npm
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Exposes the UniFi Network Integration API as tools for managing sites, devices, clients, networks, WiFi, firewalls, ACLs, switching, DNS policies, hotspot vouchers, VPNs, and more.
    41
    266 npm
    1
    MIT