mirror of
https://github.com/b3nw/nginx-proxy-manager-mcp.git
synced 2026-05-19 23:35:47 -05:00
feat: Add proxy host creation and access list tools
- Add AccessList model for authentication/IP restrictions - Add get_access_lists() client method to query access lists - Add create_proxy_host() client method with full parameter support - Add list_access_lists MCP tool for reference data - Add create_proxy_host MCP tool for automated proxy creation This enables automated proxy host creation as part of docker agent setup workflows.
This commit is contained in:
@@ -13,6 +13,7 @@ from .exceptions import (
|
||||
NpmNotFoundError,
|
||||
)
|
||||
from .models import (
|
||||
AccessList,
|
||||
AuditLogEntry,
|
||||
Certificate,
|
||||
HealthStatus,
|
||||
@@ -92,9 +93,7 @@ class NpmClient:
|
||||
raise NpmAuthenticationError("Invalid credentials")
|
||||
|
||||
if response.status_code != 200:
|
||||
raise NpmApiError(
|
||||
f"Login failed: {response.text}", status_code=response.status_code
|
||||
)
|
||||
raise NpmApiError(f"Login failed: {response.text}", status_code=response.status_code)
|
||||
|
||||
data = response.json()
|
||||
token_response = TokenResponse(**data)
|
||||
@@ -172,9 +171,7 @@ class NpmClient:
|
||||
raise NpmNotFoundError(f"Resource not found: {endpoint}")
|
||||
|
||||
if response.status_code >= 400:
|
||||
raise NpmApiError(
|
||||
f"API error: {response.text}", status_code=response.status_code
|
||||
)
|
||||
raise NpmApiError(f"API error: {response.text}", status_code=response.status_code)
|
||||
|
||||
return response
|
||||
|
||||
@@ -259,3 +256,70 @@ class NpmClient:
|
||||
)
|
||||
data = response.json()
|
||||
return [AuditLogEntry(**entry) for entry in data]
|
||||
|
||||
async def get_access_lists(self) -> list[AccessList]:
|
||||
"""Get all access lists."""
|
||||
response = await self._request("GET", "/nginx/access-lists")
|
||||
data = response.json()
|
||||
return [AccessList(**item) for item in data]
|
||||
|
||||
async def create_proxy_host(
|
||||
self,
|
||||
domain_names: list[str],
|
||||
forward_host: str,
|
||||
forward_port: int,
|
||||
forward_scheme: str = "http",
|
||||
certificate_id: int | None = None,
|
||||
ssl_forced: bool = True,
|
||||
hsts_enabled: bool = True,
|
||||
hsts_subdomains: bool = False,
|
||||
http2_support: bool = True,
|
||||
block_exploits: bool = True,
|
||||
caching_enabled: bool = False,
|
||||
allow_websocket_upgrade: bool = True,
|
||||
access_list_id: int = 0,
|
||||
advanced_config: str = "",
|
||||
meta: dict | None = None,
|
||||
) -> ProxyHost:
|
||||
"""Create a new proxy host.
|
||||
|
||||
Args:
|
||||
domain_names: List of domain names for this host
|
||||
forward_host: Backend host to forward to
|
||||
forward_port: Backend port to forward to
|
||||
forward_scheme: http or https
|
||||
certificate_id: SSL certificate ID (0 for none, use list_certificates to find)
|
||||
ssl_forced: Force SSL/HTTPS
|
||||
hsts_enabled: Enable HSTS
|
||||
hsts_subdomains: Include subdomains in HSTS
|
||||
http2_support: Enable HTTP/2
|
||||
block_exploits: Enable exploit blocking
|
||||
caching_enabled: Enable caching
|
||||
allow_websocket_upgrade: Allow WebSocket upgrades
|
||||
access_list_id: Access list ID (0 for none, use list_access_lists to find)
|
||||
advanced_config: Custom nginx configuration
|
||||
meta: Additional metadata
|
||||
|
||||
Returns:
|
||||
Created ProxyHost object
|
||||
"""
|
||||
payload = {
|
||||
"domain_names": domain_names,
|
||||
"forward_host": forward_host,
|
||||
"forward_port": forward_port,
|
||||
"forward_scheme": forward_scheme,
|
||||
"certificate_id": certificate_id or 0,
|
||||
"ssl_forced": ssl_forced,
|
||||
"hsts_enabled": hsts_enabled,
|
||||
"hsts_subdomains": hsts_subdomains,
|
||||
"http2_support": http2_support,
|
||||
"block_exploits": block_exploits,
|
||||
"caching_enabled": caching_enabled,
|
||||
"allow_websocket_upgrade": allow_websocket_upgrade,
|
||||
"access_list_id": access_list_id,
|
||||
"advanced_config": advanced_config,
|
||||
"meta": meta or {},
|
||||
}
|
||||
|
||||
response = await self._request("POST", "/nginx/proxy-hosts", json=payload)
|
||||
return ProxyHost(**response.json())
|
||||
|
||||
@@ -34,6 +34,18 @@ class Owner(BaseModel):
|
||||
roles: list[str]
|
||||
|
||||
|
||||
class AccessList(BaseModel):
|
||||
"""Access list for authentication/IP restrictions."""
|
||||
|
||||
id: int
|
||||
created_on: datetime
|
||||
modified_on: datetime
|
||||
owner_user_id: int = 0
|
||||
name: str
|
||||
satisfy_any: bool = False
|
||||
pass_auth: bool = False
|
||||
|
||||
|
||||
class Certificate(BaseModel):
|
||||
"""SSL Certificate information."""
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ async def get_system_health() -> str:
|
||||
try:
|
||||
await client._ensure_authenticated()
|
||||
result.append("Authenticated: ✅")
|
||||
|
||||
|
||||
# Try to get settings (admin only)
|
||||
try:
|
||||
settings_list = await client.get_settings()
|
||||
@@ -253,11 +253,103 @@ async def list_certificates() -> str:
|
||||
expiry = f" (expires: {cert.expires_on.strftime('%Y-%m-%d')})"
|
||||
|
||||
result.append(
|
||||
f"[{cert.id}] {cert.nice_name} ({cert.provider})\n"
|
||||
f" Domains: {domains}{expiry}"
|
||||
f"[{cert.id}] {cert.nice_name} ({cert.provider})\n Domains: {domains}{expiry}"
|
||||
)
|
||||
|
||||
return f"Found {len(certs)} certificate(s):\n\n" + "\n\n".join(result)
|
||||
|
||||
except Exception as e:
|
||||
return _format_error(e)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def list_access_lists() -> str:
|
||||
"""List all access lists configured in Nginx Proxy Manager.
|
||||
|
||||
Returns a summary of all access lists including their IDs and names.
|
||||
Use these IDs when creating proxy hosts that require access control.
|
||||
"""
|
||||
try:
|
||||
client = get_client()
|
||||
access_lists = await client.get_access_lists()
|
||||
|
||||
if not access_lists:
|
||||
return "No access lists configured."
|
||||
|
||||
result = []
|
||||
for al in access_lists:
|
||||
result.append(f"[{al.id}] {al.name}")
|
||||
|
||||
return f"Found {len(access_lists)} access list(s):\n\n" + "\n".join(result)
|
||||
|
||||
except Exception as e:
|
||||
return _format_error(e)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def create_proxy_host(
|
||||
domain_names: list[str],
|
||||
forward_host: str,
|
||||
forward_port: int,
|
||||
forward_scheme: str = "http",
|
||||
certificate_id: int = 0,
|
||||
ssl_forced: bool = True,
|
||||
block_exploits: bool = True,
|
||||
allow_websocket_upgrade: bool = True,
|
||||
access_list_id: int = 0,
|
||||
advanced_config: str = "",
|
||||
) -> str:
|
||||
"""Create a new proxy host in Nginx Proxy Manager.
|
||||
|
||||
Args:
|
||||
domain_names: List of domain names (e.g., ["app.ext.ben.io"])
|
||||
forward_host: Backend host/IP to forward to (e.g., "192.168.1.100" or "container-name")
|
||||
forward_port: Backend port to forward to (e.g., 8080)
|
||||
forward_scheme: Backend protocol - "http" or "https" (default: "http")
|
||||
certificate_id: SSL certificate ID. Use list_certificates to find available certs.
|
||||
Use 0 for no SSL, or the ID of a matching wildcard cert.
|
||||
ssl_forced: Force HTTPS redirect (default: True)
|
||||
block_exploits: Enable common exploit blocking (default: True)
|
||||
allow_websocket_upgrade: Allow WebSocket connections (default: True)
|
||||
access_list_id: Access list ID for authentication. Use list_access_lists to find.
|
||||
Use 0 for no access restrictions (default: 0)
|
||||
advanced_config: Custom nginx configuration block (default: "")
|
||||
|
||||
Returns:
|
||||
JSON with created proxy host details including the new host ID.
|
||||
|
||||
Example:
|
||||
create_proxy_host(
|
||||
domain_names=["myapp.ext.ben.io"],
|
||||
forward_host="10.0.0.50",
|
||||
forward_port=3000,
|
||||
certificate_id=24, # *.ext.ben.io wildcard
|
||||
ssl_forced=True
|
||||
)
|
||||
"""
|
||||
try:
|
||||
client = get_client()
|
||||
host = await client.create_proxy_host(
|
||||
domain_names=domain_names,
|
||||
forward_host=forward_host,
|
||||
forward_port=forward_port,
|
||||
forward_scheme=forward_scheme,
|
||||
certificate_id=certificate_id,
|
||||
ssl_forced=ssl_forced,
|
||||
block_exploits=block_exploits,
|
||||
allow_websocket_upgrade=allow_websocket_upgrade,
|
||||
access_list_id=access_list_id,
|
||||
advanced_config=advanced_config,
|
||||
)
|
||||
|
||||
domains = ", ".join(host.domain_names)
|
||||
return (
|
||||
f"Successfully created proxy host!\n\n"
|
||||
f"ID: {host.id}\n"
|
||||
f"Domains: {domains}\n"
|
||||
f"Forward: {host.forward_scheme}://{host.forward_host}:{host.forward_port}\n"
|
||||
f"SSL: {'Enabled' if host.ssl_forced else 'Disabled'}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return _format_error(e)
|
||||
|
||||
Reference in New Issue
Block a user