Unifi MCP Server — 98 tools
contains code
The Unifi DADL turns Unifi's API into an MCP server that Claude, GPT or any MCP-compatible agent can consume directly. One YAML file declares all 98 tools — firewall, device, client, wlan, network, radius, and more — and ToolMesh serves them at runtime. No Python boilerplate, no per-endpoint code, no separate MCP server process.
Below: the endpoint coverage matrix, a two-block ToolMesh setup, the full tool reference grouped by Unifi feature area, required credential scopes.
Source: Ubiquiti UniFi Network Controller API
Which Unifi endpoints are covered?
61% (98 of ~160 endpoints)
Focus: Self-hosted UniFi Network Application API: devices (access points, switches, gateways, radio settings, adoption, restarts, firmware), WLANs (SSIDs, roaming, PMF, minimum data rates, AP groups), clients (active, known, blocking), firewall (rules, groups, NAT port forwarding, static routes), networks (LANs, VLANs, WAN), system log (client events, device alerts, admin activity), statistics (DPI, traffic reports, site health), hotspot (vouchers, authorizations), RADIUS, backups.
Missing: WebSocket events, v2 traffic rules and firewall policies, v2 client connection timeline, IPS/IDS management, hotspot portal config, VoIP extensions, spectrum scan results, switch port overrides, UniFi OS console login (/api/auth/login)
How do you configure the Unifi DADL?
- Install or access your UniFi Network Controller (self-hosted or UDM/UDM Pro)
- Create a local admin account or use an existing one (Settings -> Admins)
- For dedicated API access, create a limited-admin account with read-only or site-admin role
- Note: UniFi uses session-based auth (username + password), not API keys
- Store the username and password in your ToolMesh credential store
Environment variable: CREDENTIAL_UNIFI_USERNAME / CREDENTIAL_UNIFI_PASSWORD
Written for the self-hosted UniFi Network Application: set url to https://<host>:8443. The file logs in at <url>/api/login. UniFi OS consoles (UDM, UCG, Cloud Key Gen2+) serve the Network API under https://<console>/proxy/network but log in at /api/auth/login on the console root, which this file does not configure; they are untested. Self-signed TLS certs are common -- configure TLS skip if needed. The health check calls GET /api/self and so tests the login with the stored credential.
How do you install the Unifi MCP server with ToolMesh?
Add to your backends.yaml:
- name: unifi
transport: rest
dadl: unifi.dadl
url: "https://unifi.example.com:8443"
Set the credential:
CREDENTIAL_UNIFI_USERNAME / CREDENTIAL_UNIFI_PASSWORD=your-token-hereWhat 98 tools does the Unifi DADL expose?
GETget_server_statusGet controller server status (unauthenticated). Returns server version, UUID, up status.GETget_selfGet currently logged-in admin user infoGETget_sysinfoGet controller system info: version, hostname, uptime, memoryGETget_healthGet site health status for all subsystems (wan, wlan, lan, vpn)GETget_dashboardGet dashboard metrics. Optional scale param for time granularity.GETlist_sitesList all sites accessible to current admin (basic info)GETlist_sites_statsList all sites with health data, device counts, and alertsPOSTsite_commandSite management commands: add-site, delete-site, update-site, get-admins, move-device, delete-deviceGETlist_adminsList all administrators across all sites with permissionsPOSTadmin_commandAdmin commands: invite-admin, assign-existing-admin, update-admin, revoke-admin, delete-adminGETlist_devices_basicList all adopted devices with limited fields (fast)POSTlist_devicesList adopted devices (APs, switches, gateways) as a compact projection: _id, mac, name, type (uap, usw, ugw, udm), model, state (1 = connected), version, ip, uptime, traffic rates, num_sta, satisfaction, plus per radio the settings (radio_table: radio, name, channel, ht, tx_power_mode, tx_power, min_rssi_enabled, min_rssi) and the live radio stats (radio_table_stats: radio, channel, tx_power, num_sta, satisfaction, cu_total, cu_self_rx, cu_self_tx, tx_retries_pct). Switches have empty radio arrays. Port tables, LLDP, uplink and the remaining radio fields are not included: use get_device (one MAC) or get_device_detail for the full raw document. Optional macs (array) filters by MAC.
POSTget_device_detailFull raw device documents, no projection: port_table, lldp_table, uplink, vap_table, radio_table (all fields), radio_table_stats, config_network and more, about 30 kB per access point. Pass macs as an ARRAY, also for a single device: {macs: ["00:00:5e:00:53:01"]} returns exactly that device. There is no singular mac parameter; without macs the controller returns every device of the site. For one device get_device (GET by MAC) returns the same document. Returns an array of devices. The documents contain device keys (x_authkey, x_vwirekey): do not copy them into output.
GETget_deviceGet one device by MAC address: the full raw document (same content as get_device_detail with one MAC), including radio_table and radio_table_stats for access points and port_table for switches. Returns an array with one element. Works on self-hosted controllers (verified on Network 10.6). The document contains device keys (x_authkey, x_vwirekey): do not copy them into output.
PUT_update_device_rawInternal: raw device update. Sends only the fields given. radio_table REPLACES the whole radio array: a partial array loses the settings of the radios left out. Use the update_device composite instead, which reads the current radio_table and merges. Every call re-provisions the device (its clients drop briefly).
POSTdevice_commandDevice management commands: adopt, restart, force-provision, power-cycle, upgrade, set-locate, unset-locate, speedtest, speedtest-status, migrate, cancel-migrate, spectrum-scanGETlist_active_clientsList all currently connected clientsGETget_clientGet details of a single connected client by MACGETget_userGet client/user details including offline clientsGETlist_known_clientsList all known/configured clients (including offline)POSTlist_all_usersList all users (active and historical). Filter by type and time range.POSTlist_guestsList guest clients within a time rangePOSTclient_commandClient management commands: block-sta, unblock-sta, kick-sta, forget-staPUTupdate_clientUpdate client: name, note, fixed IP, network_id, usergroup_idGETlist_networksList all networks (LANs, VLANs, WAN)POSTcreate_networkCreate a new networkPUTupdate_networkUpdate network configurationDELETEdelete_networkDelete a network. WARNING: may disconnect clients on this network.GETlist_wlansList all WLAN configurations (SSIDs)POSTcreate_wlanCreate a new WLAN (SSID)PUTupdate_wlanUpdate a WLAN (SSID): pass only the fields to change; top-level fields not sent stay as they are. Array fields (wlan_bands, ap_group_ids) are replaced as a whole, so pass the complete list. Read the current values with list_wlans first. Every update re-provisions all APs that broadcast this SSID (clients drop briefly). Roaming: bss_transition (802.11v), fast_roaming_enabled (802.11r). Security: pmf_mode, wpa_mode, wpa3_support, wpa3_transition. Bands: wlan_bands. AP selection: ap_group_mode + ap_group_ids (list_ap_groups). The minrate_* values are used when minrate_setting_preference is manual. Returns the updated WLAN document, which includes x_passphrase: do not copy it into output.
DELETEdelete_wlanDelete a WLAN. All clients on this SSID will be disconnected.GETlist_wlan_groupsList WLAN groups (radio profiles)GETlist_ap_groupsList AP groups (v2 API): each entry has _id, name and device_macs (the member APs). A WLAN with ap_group_mode groups is broadcast only by the APs in the groups listed in its ap_group_ids (see update_wlan). Returns a bare array.
GETlist_firewall_rulesList all user-defined firewall rulesPOSTcreate_firewall_ruleCreate a new firewall rulePUTupdate_firewall_ruleUpdate a firewall ruleDELETEdelete_firewall_ruleDelete a firewall ruleGETlist_firewall_groupsList firewall groups (address groups, port groups)POSTcreate_firewall_groupCreate a firewall group (address-group, port-group, ipv6-address-group)PUTupdate_firewall_groupUpdate firewall group membersDELETEdelete_firewall_groupDelete a firewall groupGETlist_port_forwardsList all port forwarding rulesPOSTcreate_port_forwardCreate a port forwarding rulePUTupdate_port_forwardUpdate a port forwarding rule (enable/disable, change ports)DELETEdelete_port_forwardDelete a port forwarding ruleGETlist_routesList user-defined static routesGETget_active_routesGet active routing table from gateway devicePOSTcreate_routeCreate a static routePUTupdate_routeUpdate a static routeDELETEdelete_routeDelete a static routeGETget_dpi_statsGet site-wide Deep Packet Inspection statsPOSTget_site_dpiGet filtered site DPI stats by application or categoryPOSTget_client_dpiGet per-client DPI stats. Filter by MAC addresses.POSTstat_report_siteGet site-level traffic report for a time intervalPOSTstat_report_deviceGet per-AP traffic report for a time intervalPOSTstat_report_userGet per-user traffic report for a time intervalPOSTstat_report_gatewayGet gateway/WAN traffic report for a time intervalPOSTclear_dpiClear/reset DPI countersPOSTlist_system_logRead the controller's system log (v2 API), where Network 10.x keeps the event history: client connects, disconnects and roams, device alerts and state changes, WAN and update events, admin activity, threat alerts. Replaces list_events and list_alarms, which Network 10.6 no longer serves. The POST is a query and changes nothing. timestampFrom and timestampTo are Unix epoch MILLISECONDS and are required, for the last 24 hours e.g. timestampFrom: Date.now() - 86400000, timestampTo: Date.now(). Results are paged and newest first: pageNumber starts at 0, pageSize sets the entries per page. Returns the raw page object {data: [...], page_number, total_element_count, total_page_count}. Entries carry key (e.g. a client-connected key), category, severity, timestamp (ms), a message template (message_raw, e.g. "{CLIENT} connected to {WLAN}") and parameters with the referenced CLIENT, DEVICE, WLAN etc. Class all returns every entry and can be narrowed with categories; the UniFi UI sends class-specific filter arrays for the other classes (see the params).
GETlist_eventsLegacy: list events from stat/event (newest first, max 3000) on older controllers. Network 10.6 answers HTTP 404; use list_system_log there. Use _limit and _start for paging.GETlist_alarmsLegacy: list alarms from stat/alarm (newest first, max 3000) on older controllers. Network 10.6 answers HTTP 404; use list_system_log (class device-alert or critical) there.GETlist_alarms_unarchivedLegacy: list unarchived alarms from rest/alarm (oldest first) on older controllers. Network 10.6 answers HTTP 400 api.err.InvalidObject; use list_system_log there.POSTarchive_all_alarmsArchive all active alarms of the legacy alarm store (older controllers, where list_alarms works). Untested on Network 10.6, which no longer serves the alarm list.POSTcreate_backupGenerate a controller backupPOSTlist_backupsList available auto-backupsPOSTdelete_backupDelete a backup file by filenamePOSTauthorize_guestAuthorize a guest client for network accessPOSTunauthorize_guestRevoke guest authorizationPOSTcreate_voucherCreate hotspot voucher(s) for guest accessPOSTrevoke_voucherRevoke a voucher by its _idPOSTlist_vouchersList all vouchers. Optionally filter by creation timestamp.GETlist_paymentsList hotspot payment historyPOSTlist_sessionsList client sessions with time range and optional MAC filterGETlist_user_groupsList user groups (bandwidth profiles)POSTcreate_user_groupCreate a user group with bandwidth limitsPUTupdate_user_groupUpdate user group bandwidth limitsDELETEdelete_user_groupDelete a user groupGETget_settingsGet all site settings (country, locale, mgmt, guest_access, etc.)PUTupdate_settingUpdate a specific site setting by key (country, locale, snmp, mgmt, guest_access, ntp, connectivity)POSTset_ledToggle site-wide device LED on/offGETlist_radius_profilesList RADIUS profilesPOSTcreate_radius_profileCreate a RADIUS profileGETlist_radius_accountsList RADIUS user accountsPOSTcreate_radius_accountCreate a RADIUS user accountGETlist_tagsList all tags (MAC address groups)POSTcreate_tagCreate a tag to group MAC addressesPUTupdate_tagUpdate tag membersDELETEdelete_tagDelete a tagPOSTlist_rogue_apsList neighboring/rogue APs detected within a time rangeGETlist_known_roguesList known/acknowledged rogue APsGETget_ddns_statusGet Dynamic DNS status (current IP, last change)GETget_ddns_configGet Dynamic DNS configurationPUTupdate_ddns_configUpdate Dynamic DNS configurationGETlist_port_profilesList switch port profiles (native VLAN, tagged VLANs, PoE mode)GETget_country_codesList available country codesGETget_wifi_channelsList available WiFi channels for the site's country settingGETget_sdn_statusGet UniFi SDN/Cloud connection statusWhat composite workflows does the Unifi DADL provide? ⚠ contains code
FNupdate_deviceUpdate a device (AP, switch, gateway): name, led_override, disabled, and per-radio settings of an access point. Pass only what should change. For radio_table pass one entry per radio you want to change, keyed by radio (ng = 2.4 GHz, na = 5 GHz, 6e = 6 GHz), with only the fields to change, e.g. [{radio: "ng", channel: 11, ht: 20}, {radio: "na", min_rssi_enabled: true, min_rssi: -80}]. The controller replaces the whole radio_table on every write, so the composite reads the device (get_device), merges the changes into the current entries and sends the complete array; other radios and fields keep their values. Settable radio fields: channel ("auto" or a channel number), ht (20, 40, 80, 160, 240, 320), tx_power_mode (auto, high, medium, low, custom, disabled; disabled switches the radio off), tx_power (dBm, with custom), min_rssi_enabled, min_rssi (-90 to -67 dBm), sens_level_enabled, sens_level (-90 to -50), antenna_gain, antenna_id, vwire_enabled, hard_noise_floor_enabled, loadbalance_enabled, maxsta, dfs. Unknown fields are rejected before anything is sent. If nothing differs from the current state, no write happens. dry_run true returns the PUT body without writing. Every write re-provisions the device: its clients drop for a few seconds. Returns {_id, written, changed, radio_table (settings after the merge), result} or, with dry_run, {_id, written: false, dry_run: true, changed, body}.