Skip to content

Releases: swkstudios/unifi-fabric-mcp-server

v0.6.2

Choose a tag to compare

@swkstudios-unifi-mcp-mirror swkstudios-unifi-mcp-mirror released this 17 Aug 00:36
5c08c09

Added

  • Optional filter argument on five Network Integration collection tools (list_networks, list_clients, list_site_devices, list_firewall_policies, list_wifi_broadcasts); expression forwarded unchanged to the upstream UniFi filter parameter; applied in both drain and single-page modes. (#187)
  • Two port-profile VLAN-tagging tools: allow_network_on_port_profile (removes a network from a profile's exclusion list, atomically fixing the D12 auto-exclusion footgun) and exclude_network_on_port_profile (inverse). Both require confirm=True. (#197)
  • Eight Mobility tools (workspace-scoped -- workspace_id/device_id, not host/site) for UMR mobile routers: 5 reads (list_mobility_workspaces, list_mobility_admins, list_mobility_devices, get_mobility_device, list_mobility_clients) and 3 guarded writes (update_mobility_device_name, update_mobility_device_network, update_mobility_device_wireless). Writes require confirm=true + UNIFI_ENABLE_MOBILITY_WRITE (default off, #186). (#196)
  • Eleven Carrier / ISP Fabric subscriber tools (org-scoped, no host/site): 4 reads (list_carrier_subscribers, get_carrier_subscriber, list_carrier_service_plans, get_carrier_service_plan) and 7 guarded writes (create_carrier_subscriber, update_carrier_subscriber, attach_carrier_subscriber_host, detach_carrier_subscriber_host, assign_carrier_subscriber_plan, suspend_carrier_subscriber, resume_carrier_subscriber). Writes require confirm=true + UNIFI_ENABLE_CARRIER_FABRIC_WRITE (default off). Not testable against the maintainer's live hardware; hermetic/spec-conformance tested only. (#201, #207)
  • Five Fabric connector relay tools (fabric_connector_get/post/put/patch/delete) -- a guarded escape hatch to reach console routes without a typed wrapper. GET always available; mutations require confirm=true + UNIFI_ENABLE_CONNECTOR_WRITE (default off). Guards: Registry-only identity, namespace allowlist, positive-charset path validation, scope guard, credential-field redaction, per-mutation audit log. (#189)
  • 41 Protect Integration API tools via the published v7.1.87 API: arm profiles (8), sirens (6), fobs (3), relays (4), speakers (4), bridges (3), link stations (3), alarm hubs (4), Protect users (2), ULP users (2), application metadata (1), POS ingestion (1). Write/action tools gated by UNIFI_PROTECT_MUTATIONS_ENABLED (default on); physical/irreversible actions additionally require confirm=true. (#185)
  • Three read-only stat convenience tools (no new controller routes): get_client_link_diagnostics (per-client link/QoS record from /stat/sta), get_device_port_state (port table + LLDP + thermal/power from /stat/device), get_device_stp_state (per-device and per-port STP/RSTP fields). (#183, #184, #188)
  • Five InnerSpace read tools completing the GET-only Integration API v1.3.23 coverage: list_innerspace_floor_plans, list_innerspace_access_points, list_innerspace_switches, list_innerspace_inventory, get_innerspace_asset (floor-plan image, base64 inline under 10 MiB cap).
  • Catalog regression check (unifi_fabric._catalog_audit, scripts/check_catalog_regression.py) comparing registered tools against what the MCP gateway serves -- catches phantom/stale drift the static checks cannot see.
  • Testing-procedure documentation: app-family testability matrix (noting which families cannot be live-tested on the maintainer's hardware) and namespace testing (/v1 vs. /ea) sections.
  • Testing procedure: added explicit Tier 2 rule prohibiting use of the MCP session tool list as gateway catalog evidence (session-start snapshot goes stale when a deployment lands mid-session); documented all three authoritative catalog sources with exact invocations (the deployed gateway's catalog database, the catalog-refresh job log, check_catalog_regression.py --live); added required verification protocol before filing a missing-tool finding; added new bullet to "What a reviewer must never do" for session-catalog escalation; added new "General Evidence Integrity Principle" section with three concrete dated examples of stale artifacts being mistaken for live state (2026-08-15). (#222)
  • Spec-drift watcher (unifi_fabric._spec_drift, scripts/check_spec_drift.py) that tracks the published UniFi OpenAPI contracts per app family. It parses the developer.ui.com/llms.txt root index (so a new family publishing an API for the first time is detected, not just version bumps within known ones), fetches each service's OpenAPI spec, and diffs the signed path+method set against a committed baseline snapshot -- reporting NEW / CHANGED / REMOVED endpoints, VERSION_DRIFT, NEW/REMOVED services, and (informational) published-vs-console version gaps. A per-service coverage overlay flags a CHANGED/REMOVED endpoint we implement as breakage and a NEW write route on a covered family (e.g. the day InnerSpace grows one) as an unimplemented capability. Runs hermetically (--snapshot, CI-safe) or live (--live, public specs, no credentials), mirroring the check_catalog_regression.py precedent (#190 / PR #195). (#199)
  • URL-pinning tests for all five Site Manager families asserting the exact /v1/ path each tool constructs (in test_site_manager.py, test_registry.py, test_aggregation.py), so a future namespace regression fails at Tier 1 rather than in a live run. (#198)
  • Testing procedure: documented Tier-2 host-scoped coverage requirements (required env vars per family, ephemeral run recipe); documented intentional exclusion of live VLAN CRUD under the standing no-live-mutation constraint, distinguishing it from STP reads and per-console Network reads that must run; added source-pinning section requiring all test claims to cite code at the build-under-test revision. (#228)

Changed

  • allow_network_on_port_profile and exclude_network_on_port_profile now require confirm=True -- each writes to a shared port profile affecting every assigned port. (#197)
  • Port-profile tools (list_port_profiles, get_port_profile, update_port_profile) resolve networkconf id fields (excluded_networkconf_ids, native_networkconf_id, voice_networkconf_id) to {id, name, vlan} objects instead of bare 24-hex ids. Write path unchanged. (#197)
  • create_network responses carry a warnings.D12_AUTO_EXCLUSION entry listing every custom-tagged port profile that UniFi silently auto-excluded the new network from. (#197)
  • InnerSpace read tools now try the documented Integration API path first, falling back to the legacy /api path on a 403; error message now names both plausible causes and both attempted paths.
  • create_firewall_policy / update_firewall_policy docstrings document the trafficFilter schema and the port-filter placement footgun (source vs. destination port targeting). (swkstudios#17)
  • update_port_profile and execute_port_action docstrings gained explicit scope guards (shared-profile and operational-action-only warnings respectively).
  • get_innerspace_asset docstring clarifies that plan_id is the asset-group id from image_url, not the floor plan's own id.
  • Versioning scheme: dev-branch pushes now cut semver pre-release tags
    (vX.Y.Z-dev.N) against the in-flight release target declared in pyproject.toml,
    instead of bumping the patch and cutting a full release tag on every push. The clean
    release tag (vX.Y.Z, no suffix) is cut only as a deliberate release step. Docker
    stable aliases (:latest, :X.Y) move only for clean releases; pre-release builds
    publish an immutable :X.Y.Z-dev.N image plus the :dev branch tag. The public
    release publish rejects pre-release tags outright.
  • Migrated the five Site Manager endpoint families (hosts, sites, devices, isp-metrics, sd-wan-configs) from the Early Access /ea/ namespace to the stable Official /v1/ namespace in place — same tool names, no behavioral change. Call sites updated in tools/site_manager.py, registry.py, and tools/aggregation.py. Live-verified against a test console: /ea/* and /v1/* return identical response envelopes, and both /ea/sites and /v1/sites return the same Fabric ObjectId siteId (disproving the earlier assumption that /v1/sites returns a UUID — the UUID used in proxy URLs comes only from the per-console connector /sites endpoint and is unaffected). Codifies the standing policy: new Site Manager tools target /v1/; /ea/ is permitted only where no /v1/ equivalent is served and must carry an "Early Access — endpoint may change" note in its docstring. (#198, #192)
  • list_sites / server tool guidance no longer describes siteId as an "EA-internal" ID; it is a Site Manager Fabric ObjectId served on /v1/sites. (#192)
  • Reworded the exclude_network_on_port_profile docstring (and the generated docs/TOOLS.md) to describe it as the inverse of allow_network_on_port_profile; the paired tools/network_services_proxy.py implementation docstring was updated to match. Documentation-only cross-reference wording; no behavioral change.
  • Replaced real captured InnerSpace asset-group and plan GUIDs in the get_innerspace_asset docstring example and test fixtures with synthetic RFC 4122 values; reworded the example so it is not presented as an observed live value; replaced a truncated fragment of the same real GUID in filename examples. Pre-publication hygiene; no behavior change. (#256)
  • Replaced a QA label and possessive phrasing in tool docstrings, the AI-facing INSTRUCTIONS block, and a runtime gated-write message with plain language describing the testability limitation ("not testable against the maintainer's live hardware; hermetic/spec-conformance tested only") -- wording already used in the README. docs/TOOLS.md regenerated accordingly. The technical caveat (spec/hermetic-conformance tested, never exercised against real hardware) is preserved; n...
Read more

v0.6.0

Choose a tag to compare

@swkstudios-unifi-mcp-mirror swkstudios-unifi-mcp-mirror released this 03 Aug 21:13
ccec959

Added

  • list_protect_events gained server-side event filters, each verified against the
    live API to actually narrow the returned set (not silently accepted and ignored):
    • smart_detect_types — filter smart-detect events by subtype: person,
      vehicle, animal, package, face, licensePlate, and the audio-alarm
      subtypes alrmSpeak, alrmSiren, alrmBark, alrmCarHorn. This is a distinct
      parameter from types; the API only applies it when types is also set to the
      relevant event type(s), so passing it alone — which the API silently ignores —
      is rejected with a clear, actionable error instead of returning everything.
    • categories — filter by event category (for example motion, smart, iot,
      admin). Values the API does not recognise are ignored by the API upstream.
    • without_descriptions — opt-in flag to ask the API to omit each event's
      description block for a smaller payload. Off by default; full-fidelity records
      remain the default and descriptions are never dropped automatically.
    • cameras now accepts camera names as well as IDs, resolved case-insensitively
      (matching the name-or-ID convention already used for host). An unknown name
      raises a clear error listing the available cameras instead of silently matching
      nothing.
  • Face and vehicle recognition tools for UniFi Protect consoles, surfacing Protect's
    built-in subject recognition through five read-only tools:
    • list_recognition_groups — list recognised subjects (for example enrolled faces),
      each with its label, sighting count, and first/last-seen timestamps. Supports
      filtering to named subjects only and server-side sorting; the complete set is
      returned by default rather than just the first page.
    • get_recognition_group_counts — aggregate totals (how many subjects exist, how
      many are named, and so on).
    • get_recognition_group_image — the reference crop for a subject, returned as a
      base64-encoded JPEG.
    • list_recognition_detections — every sighting of a subject, each with its match
      confidence and a thumbnail reference. Accepts an optional time window (verified
      against the live API to filter server-side), so a caller can ask for a specific
      range — the last hour, 30 days, or 90 days — directly; the complete set for the
      window is returned by default.
    • get_thumbnail — the crop for an individual sighting, returned as a base64-encoded
      JPEG.
  • Registry.resolve_key_for_host() — resolves which configured API key owns a
    given host (by id, hostname, or name) for MSP multi-key deployments. Queries
    each key's cached host list concurrently with per-key failure isolation;
    raises only if every key fails. Single-key deployments short-circuit with no
    extra API calls. This is the foundation for threading the owning key through
    the per-host tools (follow-up).
  • InnerSpace floor-plan tools (read-only) over the console connector proxy:
    • get_innerspace_summary — structural inventory (shape counts by type,
      per-floor plans and scales, product/material dictionary sizes) without the
      full payload.
    • get_innerspace_project — full project geometry, with a mode parameter
      (2D/3D, default 3D; only 3D carries real metric device heights).
    • list_innerspace_devices — placed device shapes with position and rotation.
    • Responses are returned verbatim, including device meta.mac/meta.ip and
      floor-plan image/asset URLs (the server is a faithful pass-through). A
      truncated host id (which the API rejects with 403 forbidden: host not found) now returns an explanation pointing at the composite host id rather
      than implying InnerSpace is unavailable.
  • History and session tools (read-only) surfacing data beyond the live snapshot:
    • list_client_sessions — per-site session history via the classic REST
      /stat/session endpoint (~90-day retention, epoch-seconds timestamps).
    • get_historical_stats — bucketed traffic reports from /stat/report
      (5-minute, hourly, or daily buckets; timestamps are epoch-milliseconds at
      the wire level, but callers always pass epoch-seconds — conversion is
      handled internally).
    • list_known_clients — full per-site client roster including offline
      devices, from /stat/alluser (GET).
    • list_protect_events — historical Protect events via the private REST
      proxy path (the Integration API exposes events over WebSocket only; this
      tool uses the REST fallback). Sensor events promote metadata.sensorId.text
      to the top level for easier filtering.
    • All responses are returned verbatim, including MAC addresses, IPs,
      hostnames, and client names (the server is a faithful pass-through).
  • Schema<->instructions cross-check: a test now parses every tool name referenced
    in the server instructions block and asserts each one is actually registered,
    so the agent-facing documentation can no longer advertise a tool that does not
    exist. Runs on every change with no live server needed; a live variant against a
    running server is also available for pre-publication checks.
  • Parameter-scope cross-check: a second static test now parses the "Parameter Scope
    Quick Reference" buckets in the server instructions block and asserts each
    explicitly-named tool's declared host/site scope matches its registered schema, so a
    tool filed under the wrong bucket (e.g. "ID only" for a tool that actually requires
    host/site) fails CI. The phantom-name check's blind spot — it verifies tool
    names, not the claims around them — is now documented in the audit module and the
    testing procedure.

Changed

  • Breaking (tool parameters): get_radius_profile, update_vpn_server,
    delete_vpn_server, update_hotspot_operator, and delete_hotspot_operator
    now require host and site arguments.
    These tools previously took only an
    item ID because they targeted a (non-functional) global /ea/ path. The
    working routes are per-console and per-site, so the console/site must now be
    named. get_vpn_server already required host/site and is unchanged.
    Callers of the affected tools must add host and site.

  • Response shape: recognition list tools stay resource-named, not data.
    list_recognition_groups returns its array under groups (and
    list_recognition_detections under detections), matching the codebase
    convention of naming a list's array after its resource. This is deliberately
    left unchanged; only the Network Integration offset-paginated proxy lists use
    the {data, totalCount} shape. The list_recognition_groups docstring now
    states this explicitly so consumers key off groups rather than assuming data.

  • All tools now return complete upstream data, including identifier and
    credential fields.
    The server is a faithful pass-through: whatever the
    UniFi API returns for a request is returned to the caller unchanged. Runtime
    response filtering has been removed everywhere it existed:

    • Client/session/roster and Protect event responses now include MAC
      addresses, IPs, hostnames, and client/sensor/subject names verbatim
      (previously replaced with [REDACTED]).
    • Controller settings, Dynamic DNS, WLAN configs, and RADIUS account
      responses now include credential fields such as x_passphrase,
      x_password, and API tokens verbatim (previously replaced with
      [REDACTED]).
    • InnerSpace project geometry now includes device meta.mac/meta.ip and
      floor-plan image/asset URLs verbatim (previously replaced with
      [REDACTED], which broke floor-plan image retrieval).
    • Site Manager host records now include reportedState GPS coordinates
      (latitude, longitude, geoInfo) verbatim.

    User-visible behavior change: responses are larger and contain data
    earlier versions withheld. A deployment that needs to restrict what reaches a
    consumer should layer that policy on top of the server (a proxy or wrapper),
    which can always narrow a faithful response — whereas data the server chose
    to withhold could never be recovered downstream.

  • Removed the include_secrets parameter from list_dynamic_dns,
    get_dynamic_dns, list_wlan_configs, get_wlan_config, list_accounts,
    and get_account, and the include_gps parameter from list_hosts and
    get_host. These opt-in flags gated the now-removed filtering and are
    redundant — the fields they exposed are always returned. Calls that passed
    either parameter should drop it. (Removing the _token-suffix credential
    match also fixes a latent bug where the pagination cursor nextToken was
    being replaced with [REDACTED].)

  • List tools drain all pages by default and return the complete result set.
    When called without explicit pagination parameters, list tools aggregate all
    available pages before returning. Typical queries — "list all sites", "list
    all devices" — complete without requiring callers to follow next-page tokens
    manually.

    To retrieve a single page instead, supply explicit pagination parameters:
    for cursor-based tools, pass page_token=<token> from a prior response; for
    offset-based tools, pass both offset and limit.

    When UNIFI_PAGINATE_MAX_PAGES is set and the page cap is reached before
    results are exhausted, the response includes "incomplete": true and an
    "incompleteReason" string. Callers should surface this to users so they
    know results may be partial.

    User-visible behavior change: tools that silently stopped at one page now
    return every matching record.
    Affected endpoints, each confirmed paginated
    against the live API:

    • list_protect_events — historical Protect events are offset-paginated; a
      wide time window can hold tens of thousands of events, and an unpaginated
      request times out on the device. The tool now pages through them (200 at a
      time) and returns the complete, time-ordered set. Pass `offs...
Read more

v0.3.155

Choose a tag to compare

@TeknoCentral TeknoCentral released this 23 May 17:52

Fixed

  • update_setting now detects silent no-op writes and returns a clear
    error when the controller rejects a payload (#17)
  • update_network strips read-only fields from input, preventing the
    HTTP 400 that occurred during get-modify-put workflows (#17)

v0.3.154

Choose a tag to compare

@TeknoCentral TeknoCentral released this 23 May 18:18

Fixed

  • Pagination: preserve explicit zero params (#87)
  • Validate network service path IDs

Changed

  • fastmcp dependency now >=3.2,<3.4
  • Bump actions/create-github-app-token to v3

Docs

  • Clarify dispatch-based public publish flow (#85)
  • Regenerate TOOLS.md from runtime metadata

v0.3.153 — UniFi Fabric MCP Server

Choose a tag to compare

@TeknoCentral TeknoCentral released this 20 May 21:19

What's New

Features

  • Controllable server logging — new UNIFI_LOG_LEVEL env var to set log verbosity (DEBUG, INFO, WARNING, etc.)
  • Auto-pull workflow for approved public issues — approved issues filed on the public repo are automatically tracked upstream

Bug Fixes

  • Prevent Authorization header leak in exception messages — credentials are now redacted from error output
  • Idempotency check improvement — issue-pull deduplication now matches both full URLs and shorthand formats

Documentation

  • Added Troubleshooting section to README
  • Auto-generated TOOLS.md reference manual from @mcp.tool() exports
  • Documented retry/backoff behavior in docstrings and README
  • Added finding verification protocol to testing procedure

CI/CD

  • Mirror tag management improvements (orphan tag pruning, delete-tag workflow)

Full changelog: https://github.com/swkstudios/unifi-fabric-mcp-server/blob/main/CHANGELOG.md

v0.3.14 — UniFi Fabric MCP Server

Choose a tag to compare

@TeknoCentral TeknoCentral released this 04 May 00:42

Added

  • Optional MCP_BEARER_TOKEN env var for bearer-token authentication on HTTP transport
  • When set, all incoming MCP requests require Authorization: Bearer <token> header
  • Backward compatible: server runs without transport auth when unset (existing behavior)

Full changelog: https://github.com/swkstudios/unifi-fabric-mcp-server/blob/main/CHANGELOG.md