unifi-network-config-tools
Skill294 starsChanged 18 days agoApache-2.0View on GitHub
Use when adding, changing or testing a UniFi Network configuration MCP tool (networks, WiFi, WAN, DHCP, DNS, QoS, port profiles, switching, VPN) - covers URL spaces and site ID translation, read-modify-write updates, the write pattern, caching, test fixtures and firmware gotchas.
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 3 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 28
Every public async def is a tool (register_module_tools() in src/tool_registry.py).
- Very specific: line 29
All 11 modules are in _LOCAL_TOOL_MODULES (src/main.py).
- Distinctive: line 40
Add a new module to the right set.
- Distinctive: line 41
No module checks the Network version in code.
- Very specific: line 53
Do not hardcode /proxy/network/api/s/....
- Very specific: line 59
Only WANConnection and TrafficRoute set extra="allow".
- Very specific: line 65
Call validate_confirmation(confirm, "<operation>", dry_run) first.
- Distinctive: line 66
Do not add audit code; the registry wrapper audits every mutating call.
- Very specific: line 98
When a tool checks the API mode, set settings.api_type = APIType.LOCAL on the settings fixture.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
UniFi Network Configuration Tools
These tools read and change network configuration on the local gateway. Most of them use the legacy Network API, and a few use the integration API. Read this before you change one. For the general add-a-tool workflow, see unifi-mcp-tool-builder.
Module map
| Module | Tools | Examples |
src/tools/networks.py | 4 | get_network_details, list_vlans, get_subnet_info |
src/tools/network_config.py | 3 | create_network, update_network, delete_network |
src/tools/wifi.py | 5 | list_wlans, update_wlan, get_wlan_statistics |
src/tools/wans.py | 6 | list_wan_connections, create_dynamic_dns |
src/tools/dhcp_reservations.py | 5 | create_dhcp_reservation, remove_dhcp_reservation |
src/tools/dns_management.py | 4 | list_wan_dns, update_dns_filter |
src/tools/qos.py | 3 | list_traffic_routes, configure_smart_queue |
src/tools/port_profiles.py | 8 | update_port_profile, set_device_port_overrides |
src/tools/switching.py | 6 | list_switch_stacks, get_lag_details (reads only) |
src/tools/vpn.py | 2 | list_vpn_tunnels, list_vpn_servers |
src/tools/site_vpn.py | 3 | get_site_to_site_vpn, update_site_to_site_vpn |
Registration and modes
- Every public
async defis a tool (register_module_tools()insrc/tool_registry.py).port_profiles.get_device_by_macis public, so it is a tool too. Name helpers with a leading_. - All 11 modules are in
_LOCAL_TOOL_MODULES(src/main.py). None of them exists in cloud modes. - Some tools also refuse non-local modes at run time with a
_ensure_local_api()helper: DHCP, DNS, Dynamic DNS and traffic routes. UNIFI_PROFILEsets insrc/main.py:Profile Modules networkdhcp, dns, network_config, networks, wans, wifi devicesport_profiles, switching securitysite_vpn, vpn systemqos Add a new module to the right set.
- No module checks the Network version in code. When a feature needs Network 9.0+, say so in the tool docstring.
URL spaces
| Space | How to build the path | Site ID |
| Legacy | Write /ea/sites/{site_id}/<resource>. UniFiClient rewrites it to /proxy/network/api/s/{site_name}/<resource> in local mode | UUID or name; the client maps a UUID to the site name |
| v2 | settings.get_v2_api_path(site_id) | the client maps a UUID to the site name |
| Integration | settings.get_integration_path(f"sites/{site_id}/...") | the client maps a name to the UUID; client.resolve_site_id() returns the UUID |
Legacy resources in use: rest/networkconf, rest/wlanconf, rest/portconf, rest/user, rest/dynamicdns, get/setting/ips. The client also rewrites devices to stat/device and sta to stat/sta.
Do not hardcode /proxy/network/api/s/.... The client passes such a path through unchanged, so a site UUID is never turned into a site name (see Known defects).
Models
- Models:
src/models/network.py,src/models/wan.py,src/models/vpn.py,src/models/port_profile.py,src/models/switching.py,src/models/qos_profile.py. - Legacy records use
idwithalias="_id"andpopulate_by_name=True. Integration models use camelCase aliases. - Only
WANConnectionandTrafficRoutesetextra="allow". The rest drop unknown keys. - The legacy API calls the VLAN field
vlan.Networkaliasesvlan_idtovlan, butnetwork_configreturns raw controller records, which containvlan. The two modules return different shapes.
Write tools
- Signature:
confirm: bool | str = False, dry_run: bool | str = False.test_every_mutating_tool_declares_dry_run(tests/unit/test_tool_registry.py) requiresdry_runon every write. - Call
validate_confirmation(confirm, "<operation>", dry_run)first. Return the dry run before you open a client. - Do not add audit code; the registry wrapper audits every mutating call.
Read-modify-write. These tools GET the whole record, merge the changes and PUT the whole record back:
update_networkupdate_wlanupdate_port_profileset_device_port_overrides(PUTs the whole device)update_site_to_site_vpn
Rules for them:
- Optional update arguments must default to
None. A default such asFalseoverwrites the stored value on every update. - Take a record from a list reply with
first_response_item()(src/utils/helpers.py), never with[0]. - The controller rewrites some fields on its own.
port_profilesre-reads the record and reports the differences (_stored_value_warnings). Do the same when you add a read-modify-write tool. set_device_port_overrides(merge=False)replaces every override, and thedns_filterslist inupdate_dns_filterreplaces the whole list. Say so in the docstring of any argument that replaces a list.
Partial updates. update_wan_dns and configure_smart_queue send only the changed fields to rest/networkconf. DHCP sends partial fields to rest/user.
Responses, errors and cache
UniFiClientreturns{"data": ...}for a reply that has a data key, and{}for an empty body. Handle a list or a dict.- 404 raises
ResourceNotFoundError, 401 and 403 raiseAuthenticationError, and other errors raiseAPIError. - The Redis read cache covers only the
networksandwifimodules (CACHED_MODULESinsrc/cache.py). Tool names containing "statistics" are never cached. - Any write that is not a dry run clears every cached read for that controller. A write tool needs no cache code of its own.
src/tools/wifi.pypasses WLAN output through_redact_wlan(). Keep that for any tool that returns WLAN records, because they contain passphrases.
Tests
- Files:
tests/unit/tools/test_<module>_tools.py. Switching tests are intests/unit/test_switching.py. Representative:tests/unit/tools/test_network_config_tools.py. - Patch the module's client name:
patch.object(nc_module, "UniFiClient", ...). Use aMagicMockclient withAsyncMockmethods, and make__aexit__returnFalse, or exceptions inside the tool are swallowed. - Assert the request body:
client.put.call_args[1]["json_data"]. - When a tool checks the API mode, set
settings.api_type = APIType.LOCALon the settings fixture.
Firmware gotchas
- Mock-only tests once passed for endpoints that do not exist (
rest/qosprofile,rest/wanconf). Before you add an endpoint, find it indocs/UNIFI_API.mdor a spec snapshot inscripts/, and say if it is not verified on hardware. - Smart Queue rates are stored in kbps; the UI shows Mbps.
- DNS-over-TLS has no REST field on firmware 10.2.x.
- POSTing a MAC the controller already knows fails with
api.err.MacUsed, so DHCP looks the client up first. Settingfixed_ipwithoutuse_fixedipdoes nothing. - A network's
purposecannot change after creation. - The integration API WAN list returns only
idandname.
Known defects (not fixed)
src/tools/site_vpn.pysends/ea/sites/{site_id}/rest/networkconf, which the client translates to the legacy path with the site name, so a site UUID and a site name both work.- The confirmation checks are not the same everywhere.
site_vpnreturns{"error": ...}where other modules raise, and the DHCP and DNS modules raiseValueErrorwhere others raiseValidationError. - Dry-run replies differ between modules:
{"dry_run": True, "would_create": ...}innetwork_configand{"status": "dry_run", ...}indhcp_reservations. qos.pymaps site UUIDs to names by hand throughclient._site_uuid_to_name, which the client now does for v2 paths anyway.
In this repository52 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .agents/skills/fastmcp/CLAUDE.mdCLAUDE.md
- CLAUDE.mdCLAUDE.md
- .claude/skills/fastmcp/CLAUDE.mdCLAUDE.md
- .cursor/rules/common-mistakes.mdcCursor rule
- .cursor/rules/core-principles.mdcCursor rule
- .cursor/rules/environment-setup.mdcCursor rule
- .cursor/rules/mcp-tools.mdcCursor rule
- .cursor/rules/project-context.mdcCursor rule
- .cursor/rules/README.mdCursor rule
- .cursor/rules/unifi-api.mdcCursor rule
- .cursor/rules/workflow.mdcCursor rule
- FastMCP DevelopmentSkill
- mcp-builderSkill
- source-command-build-dockerSkill
- source-command-check-depsSkill
- source-command-cleanSkill
- source-command-docsSkill
- source-command-formatSkill
- source-command-lintSkill
- source-command-prSkill
- source-command-securitySkill
- source-command-setupSkill
- source-command-testSkill
- source-command-unifi-api-docs-update-2Skill
- source-command-unifi-api-docs-updateSkill
- source-command-unifi-mcp-add-toolSkill
- source-command-unifi-mcp-release-prepSkill
- source-command-unifi-mcp-test-coverageSkill
- source-command-unifi-mcp-update-docsSkill
- unifi-cloud-fleet-toolsSkill
- unifi-mcp-tool-builderSkill
- unifi-network-config-toolsSkill
- unifi-protect-toolsSkill
- unifi-security-toolsSkill
- FastMCP DevelopmentSkill
- mcp-builderSkill
- unifi-cloud-fleet-toolsSkill
- unifi-mcp-tool-builderSkill
- unifi-network-config-toolsSkill
- unifi-protect-toolsSkill
- unifi-security-toolsSkill
- unifiSkill
- unifi-channel-planningSkill
- unifi-cloud-fleet-toolsSkill
- unifi-devicesSkill
- unifi-mcp-tool-builderSkill
- unifi-networkSkill
- unifi-protect-toolsSkill
- unifi-securitySkill
- unifi-security-toolsSkill
- unifi-systemSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

