Code-mode MCP server for the UniFi Network Integration API and Site Manager (cloud) API. Two tools (search + execute) with QuickJS WASM sandbox plus an optional Cloudflare Workers entry. Single-user (env) and multi-user (per-request HTTP headers) modes.
Two tools with detailed descriptions and clear purpose, but significant schema and parameter documentation gaps. The 'search' and 'execute' tools have rich narrative descriptions (500+ chars each) explaining globals, examples, and limits, which is excellent for LLM understanding. However, both tools expose only a single 'code' parameter with minimal schema detail. Input schemas lack type constraints, format specifications, and validation rules. No output schemas are documented, LLMs cannot predict what structure to expect from search or execute results. Error handling is mentioned in execute (hard ceiling, sandbox limits) but not formalized as recovery guidance. Tool composition is sound (search for discovery, execute for action), but parameter descriptions do not explain constraints like max code size or sandbox memory bounds.
Run UniFi API calls by writing JavaScript that uses the `unifi` namespace. Surfaces: - `unifi.local` — UniFi Network Integration API (per-controller). Available only if local credentials are configured (env or `X-Unifi-Local-*` headers). - `unifi.cloud` — UniFi Site Manager API at `https://api.ui.com`. Available only if a cloud key is configured. - `unifi.cloud.network(consoleId)` — Network Integration API tunneled through the Site Manager connector. Same shape as `unifi.local`. - `unifi.local.protect` — UniFi Protect Integration API on a controller running Protect. Available only when both a Protect spec and local credentials are configured. - `unifi.cloud.protect(consoleId)` — Protect Integration API tunneled through the Site Manager connector at `/v1/connector/consoles/{consoleId}/proxy/protect/integration`. URL pattern is officially documented by Ubiquiti. Mock-verified end-to-end; live verification against a Protect-enabled console pending. Each namespace exposes: - `unifi.<ns>.<tag>.<operationId>(args)` — typed operation call, e.g. `unifi.local.sites.listSites({ limit: 200 })`. Args are auto-routed: keys matching path or query params from the spec are placed correctly; remaining keys form the JSON body if the operation accepts one. To override, pass `{ pathParams: {...}, query: {...}, body: {...}, headers: {...} }`. - `unifi.<ns>.callOperation(operationId, args)` — flat lookup by id. - `unifi.<ns>.request({ method, path, pathParams?, query?, body?, headers? })` — raw HTTP escape hatch. Use this for endpoints not in the loaded spec. - `unifi.<ns>.spec` — `{ title, version, sourceUrl, operationCount }` for diagnostics. Operations are async — use `await`. The final expression is the tool result. ## Examples ```javascript const sites = await unifi.local.sites.listSites({ limit: 200 }); return sites.data.map(function (s) { return { id: s.id, name: s.name }; }); ``` ```javascript const counts = await Promise.all( (await unifi.local.sites.listSites({ limit: 200 })).data.map(async function (site) { const devices = await unifi.local.devices.listDevices({ siteId: site.id }); return { site: site.name, devices: devices.data.length }; }), ); return counts; ``` ```javascript // Raw escape hatch — call an endpoint not present in the spec const r = await unifi.local.request({ method: 'GET', path: '/v1/info' }); return r; ``` ## Limits - Hard ceiling on API calls per execute; exceeded → error. - Sandbox memory + time bounded. - Credentials never enter the sandbox.
No input schema constraints on 'code' parameter. Missing max length, format validation, and sandbox limits documentation in parameter description.
Output schemas not documented. LLMs cannot predict return structure (e.g., does search return array of operations or console.log output? Does execute return raw API response or formatted result?).
Error handling lacks recovery guidance. 'Hard ceiling on API calls' and 'sandbox memory bounded' are mentioned but not formalized as actionable error messages or retry strategies.
Parameter description for 'code' does not specify MAX_CODE_SIZE or MAX_RESULT_SIZE limits (referenced in imports but not exposed to LLM).
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 53 | <=2025-11-25 | v2 |
Search the UniFi OpenAPI specs (local Network Integration, cloud Site Manager, and Protect Integration) by writing JavaScript. The sandbox is read-only — no network. Use this tool to **discover** what to call before invoking `execute`. ## Globals - `spec.local`, `spec.cloud`, and `spec.protect` — each: `{ title, version, sourceUrl, serverPrefix, operations[] }` Operations are compact: `{ operationId, method, path, tag, summary, parameters, hasRequestBody, deprecated }`. Any namespace may be `null` if not configured (no local controller, no cloud key, or Protect not loaded). - `searchOperations(namespace, query, limit?)` — text-ranked search; namespace is `"local"`, `"cloud"`, or `"protect"`. - `getOperation(namespace, operationId)` — full operation including spec parameter detail. Pass either an `operationId` or `"METHOD /path"`. - `findOperationsByPath(namespace, substring)` — list operations whose path contains the substring (case-insensitive). - `console.log()` — captured into the tool output. ## Examples ```javascript // All operations on sites in the local API spec.local.operations.filter(function (op) { return op.tag === 'sites'; }); ``` ```javascript // Top 10 hits for "voucher" searchOperations('local', 'voucher', 10); ``` ```javascript // Full detail on listSites getOperation('local', 'listSites'); ```
No idempotency or confirmation pattern for 'execute' tool, which can modify state (WRITE risk). Agents may retry failed calls and cause duplicate side effects.