unifi-network-mcp 0.3.2__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/.gitignore +1 -0
  2. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/PKG-INFO +5 -3
  3. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/README.md +2 -0
  4. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/pyproject.toml +13 -4
  5. unifi_network_mcp-0.4.0/src/_version.py +34 -0
  6. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/runtime.py +17 -1
  7. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/network.py +12 -1
  8. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools_manifest.json +1 -1
  9. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/.well-known/mcp-server.json +0 -0
  10. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/LICENSE +0 -0
  11. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/bootstrap.py +0 -0
  12. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/config/config.yaml +0 -0
  13. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/jobs.py +0 -0
  14. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/main.py +0 -0
  15. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/client_manager.py +0 -0
  16. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/connection_manager.py +0 -0
  17. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/device_manager.py +0 -0
  18. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/event_manager.py +0 -0
  19. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/firewall_manager.py +0 -0
  20. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/hotspot_manager.py +0 -0
  21. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/network_manager.py +0 -0
  22. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/qos_manager.py +0 -0
  23. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/routing_manager.py +0 -0
  24. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/stats_manager.py +0 -0
  25. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/system_manager.py +0 -0
  26. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/traffic_route_manager.py +0 -0
  27. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/usergroup_manager.py +0 -0
  28. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/managers/vpn_manager.py +0 -0
  29. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/schemas.py +0 -0
  30. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tool_index.py +0 -0
  31. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/clients.py +0 -0
  32. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/config.py +0 -0
  33. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/devices.py +0 -0
  34. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/events.py +0 -0
  35. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/firewall.py +0 -0
  36. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/hotspot.py +0 -0
  37. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/port_forwards.py +0 -0
  38. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/qos.py +0 -0
  39. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/routing.py +0 -0
  40. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/stats.py +0 -0
  41. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/system.py +0 -0
  42. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/traffic_routes.py +0 -0
  43. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/usergroups.py +0 -0
  44. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/tools/vpn.py +0 -0
  45. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/utils/confirmation.py +0 -0
  46. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/utils/diagnostics.py +0 -0
  47. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/utils/lazy_tool_loader.py +0 -0
  48. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/utils/meta_tools.py +0 -0
  49. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/utils/permissions.py +0 -0
  50. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/utils/tool_loader.py +0 -0
  51. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/validator_registry.py +0 -0
  52. {unifi_network_mcp-0.3.2 → unifi_network_mcp-0.4.0}/src/validators.py +0 -0
@@ -3,6 +3,7 @@
3
3
 
4
4
  # Python
5
5
  __pycache__/
6
+ src/_version.py
6
7
  *.py[cod]
7
8
  *$py.class
8
9
  *.so
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unifi-network-mcp
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Unifi Network MCP Server
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.13
7
7
  Requires-Dist: aiohttp>=3.8.5
8
- Requires-Dist: aiounifi>=87.0.0
8
+ Requires-Dist: aiounifi>=88
9
9
  Requires-Dist: jsonschema>=4.17.0
10
- Requires-Dist: mcp[cli]>=1.21.2
10
+ Requires-Dist: mcp[cli]>=1.23.0
11
11
  Requires-Dist: omegaconf>=2.3.0
12
12
  Requires-Dist: python-dotenv>=1.0.0
13
13
  Requires-Dist: pyyaml>=6.0
@@ -538,6 +538,7 @@ The server merges settings from **environment variables**, an optional `.env` fi
538
538
  | `UNIFI_TOOL_REGISTRATION_MODE` | Tool loading mode: `lazy` (default), `eager`, or `meta_only`. See [Context Optimization](#context-optimization) |
539
539
  | `UNIFI_ENABLED_CATEGORIES` | Comma-separated list of tool categories to load (eager mode). See table below |
540
540
  | `UNIFI_ENABLED_TOOLS` | Comma-separated list of specific tool names to register (eager mode) |
541
+ | `UNIFI_MCP_ALLOWED_HOSTS` | Comma-separated list of allowed hostnames for reverse proxy support. Required when running behind Nginx/Cloudflare/etc. Default `localhost,127.0.0.1` |
541
542
 
542
543
  ### Tool Categories (for UNIFI_ENABLED_CATEGORIES)
543
544
 
@@ -809,6 +810,7 @@ See [docs/permissions.md](docs/permissions.md) for complete documentation includ
809
810
  * **Review permissions carefully** before enabling high-risk operations. Use environment variables for runtime control.
810
811
  * Create, update, and delete tools should be used with caution and only enabled when necessary.
811
812
  * Do not host outside of your network unless using a secure reverse proxy like Cloudflare Tunnel or Ngrok. Even then, an additional layer of authentication is recommended.
813
+ * **Reverse Proxy Configuration:** When running behind a reverse proxy, set `UNIFI_MCP_ALLOWED_HOSTS` to include your external domain (e.g., `localhost,127.0.0.1,unifi-mcp.example.com`) to bypass FastMCP's DNS rebinding protection.
812
814
 
813
815
  ---
814
816
 
@@ -522,6 +522,7 @@ The server merges settings from **environment variables**, an optional `.env` fi
522
522
  | `UNIFI_TOOL_REGISTRATION_MODE` | Tool loading mode: `lazy` (default), `eager`, or `meta_only`. See [Context Optimization](#context-optimization) |
523
523
  | `UNIFI_ENABLED_CATEGORIES` | Comma-separated list of tool categories to load (eager mode). See table below |
524
524
  | `UNIFI_ENABLED_TOOLS` | Comma-separated list of specific tool names to register (eager mode) |
525
+ | `UNIFI_MCP_ALLOWED_HOSTS` | Comma-separated list of allowed hostnames for reverse proxy support. Required when running behind Nginx/Cloudflare/etc. Default `localhost,127.0.0.1` |
525
526
 
526
527
  ### Tool Categories (for UNIFI_ENABLED_CATEGORIES)
527
528
 
@@ -793,6 +794,7 @@ See [docs/permissions.md](docs/permissions.md) for complete documentation includ
793
794
  * **Review permissions carefully** before enabling high-risk operations. Use environment variables for runtime control.
794
795
  * Create, update, and delete tools should be used with caution and only enabled when necessary.
795
796
  * Do not host outside of your network unless using a secure reverse proxy like Cloudflare Tunnel or Ngrok. Even then, an additional layer of authentication is recommended.
797
+ * **Reverse Proxy Configuration:** When running behind a reverse proxy, set `UNIFI_MCP_ALLOWED_HOSTS` to include your external domain (e.g., `localhost,127.0.0.1,unifi-mcp.example.com`) to bypass FastMCP's DNS rebinding protection.
796
798
 
797
799
  ---
798
800
 
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "unifi-network-mcp"
3
- version = "0.3.2"
3
+ dynamic = ["version"]
4
4
  description = "Unifi Network MCP Server"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
7
7
  dependencies = [
8
- "mcp[cli]>=1.21.2",
8
+ "mcp[cli]>=1.23.0",
9
9
  "aiohttp>=3.8.5",
10
- "aiounifi>=87.0.0",
10
+ "aiounifi>=88",
11
11
  "pyyaml>=6.0",
12
12
  "python-dotenv>=1.0.0",
13
13
  "omegaconf>=2.3.0",
@@ -16,9 +16,15 @@ dependencies = [
16
16
  ]
17
17
 
18
18
  [build-system]
19
- requires = ["hatchling"]
19
+ requires = ["hatchling", "hatch-vcs"]
20
20
  build-backend = "hatchling.build"
21
21
 
22
+ [tool.hatch.version]
23
+ source = "vcs"
24
+
25
+ [tool.hatch.build.hooks.vcs]
26
+ version-file = "src/_version.py"
27
+
22
28
  [tool.hatch.build.targets.wheel]
23
29
  packages = ["src"]
24
30
 
@@ -44,6 +50,7 @@ dev = [
44
50
  "pytest-asyncio>=0.21.0",
45
51
  "aioresponses>=0.7.0",
46
52
  "pytest-cov>=4.0.0",
53
+ "ruff>=0.8.0",
47
54
  ]
48
55
 
49
56
  [tool.rye]
@@ -53,6 +60,7 @@ dev-dependencies = [
53
60
  "pytest-asyncio>=0.21.0",
54
61
  "aioresponses>=0.7.0",
55
62
  "pytest-cov>=4.0.0",
63
+ "ruff>=0.8.0",
56
64
  ]
57
65
 
58
66
  [project.scripts]
@@ -61,6 +69,7 @@ unifi-network-mcp = "src.main:main"
61
69
 
62
70
  [tool.ruff]
63
71
  line-length = 120
72
+ exclude = ["src/_version.py"] # Auto-generated by hatch-vcs
64
73
 
65
74
  [tool.ruff.lint]
66
75
  select = ["E", "F", "I"]
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.4.0'
32
+ __version_tuple__ = version_tuple = (0, 4, 0)
33
+
34
+ __commit_id__ = commit_id = None
@@ -14,10 +14,12 @@ Lazy factories (`get_*`) are provided so unit tests can substitute fakes by
14
14
  monkey‑patching before the first call.
15
15
  """
16
16
 
17
+ import os
17
18
  from functools import lru_cache
18
19
  from typing import Any
19
20
 
20
21
  from mcp.server.fastmcp import FastMCP
22
+ from mcp.server.transport_security import TransportSecuritySettings
21
23
 
22
24
  from src.bootstrap import load_config, logger
23
25
  from src.managers.client_manager import ClientManager
@@ -50,7 +52,21 @@ def get_config():
50
52
  @lru_cache
51
53
  def get_server() -> FastMCP:
52
54
  """Create the FastMCP server instance exactly once."""
53
- return FastMCP(name="unifi-network-mcp", debug=True)
55
+ # Parse allowed hosts from environment variable for reverse proxy support
56
+ # Default to localhost only for backwards compatibility
57
+ allowed_hosts_str = os.getenv("UNIFI_MCP_ALLOWED_HOSTS", "localhost,127.0.0.1")
58
+ allowed_hosts = [h.strip() for h in allowed_hosts_str.split(",") if h.strip()]
59
+
60
+ # Configure transport security settings
61
+ transport_security = TransportSecuritySettings(allowed_hosts=allowed_hosts)
62
+
63
+ logger.debug(f"Configuring FastMCP with allowed_hosts: {allowed_hosts}")
64
+
65
+ return FastMCP(
66
+ name="unifi-network-mcp",
67
+ debug=True,
68
+ transport_security=transport_security,
69
+ )
54
70
 
55
71
 
56
72
  # ---------------------------------------------------------------------------
@@ -170,7 +170,7 @@ async def get_network_details(network_id: str) -> Dict[str, Any]:
170
170
 
171
171
  @server.tool(
172
172
  name="unifi_update_network",
173
- description="Update specific fields of an existing network (LAN/VLAN). Requires confirmation.",
173
+ description="Update specific fields of an existing network (LAN/VLAN). Requires confirmation. Note: Network isolation is only supported on 'corporate' networks, not 'guest' networks.",
174
174
  permission_category="networks",
175
175
  permission_action="update",
176
176
  )
@@ -193,9 +193,15 @@ async def update_network(network_id: str, update_data: Dict[str, Any], confirm:
193
193
  - dhcp_start (string): New DHCP start IP.
194
194
  - dhcp_stop (string): New DHCP stop IP.
195
195
  - enabled (boolean): Enable/disable the entire network.
196
+ - network_isolation_enabled (boolean): Enable network isolation (IMPORTANT: Only works on networks with purpose="corporate").
196
197
  # Add other relevant fields from NetworkSchema here if needed
197
198
  confirm (bool): Must be set to `True` to execute. Defaults to `False`.
198
199
 
200
+ Important Constraints:
201
+ - Network isolation (network_isolation_enabled) is ONLY supported on networks with purpose="corporate".
202
+ - Attempting to enable isolation on "guest" or other network types will fail with an API error.
203
+ - To isolate a guest network: (1) Change its purpose from "guest" to "corporate", then (2) enable network_isolation_enabled.
204
+
199
205
  Returns:
200
206
  Dict: Success status, ID, updated fields, details, or error message.
201
207
  Example (success):
@@ -317,6 +323,11 @@ async def create_network(network_data: Dict[str, Any], confirm: bool = False) ->
317
323
  - vlan (integer): VLAN ID (required if vlan_enabled is true)
318
324
  - dhcp_enabled (boolean): Whether DHCP is enabled (default: true)
319
325
  - enabled (boolean): Whether the network is enabled (default: true)
326
+ - network_isolation_enabled (boolean): Enable network isolation (IMPORTANT: Only works on networks with purpose="corporate")
327
+
328
+ Important Constraints:
329
+ - Network isolation (network_isolation_enabled) is ONLY supported on networks with purpose="corporate".
330
+ - It cannot be enabled on "guest" networks.
320
331
 
321
332
  Example:
322
333
  {
@@ -1239,7 +1239,7 @@
1239
1239
  }
1240
1240
  },
1241
1241
  {
1242
- "description": "Update specific fields of an existing network (LAN/VLAN). Requires confirmation.",
1242
+ "description": "Update specific fields of an existing network (LAN/VLAN). Requires confirmation. Note: Network isolation is only supported on 'corporate' networks, not 'guest' networks.",
1243
1243
  "name": "unifi_update_network",
1244
1244
  "schema": {
1245
1245
  "input": {