"""DHCP server tools: leases, server config, static mappings.""" from __future__ import annotations import json from typing import Any from mcp.server.fastmcp import FastMCP from pfsense_mcp.audit import audit_log from pfsense_mcp.client import get_client from pfsense_mcp.config import settings from pfsense_mcp.guards import is_dry_run_response, require_writes def register(mcp: FastMCP) -> None: @mcp.tool() def pfsense_list_dhcp_leases(limit: int = 200, offset: int = 0, query: str = "", target: str = "", url: str = "", api_key: str = "", verify_ssl: bool | None = None) -> str: """List DHCP leases. query example: 'hostname__contains=nas' or 'ip__contains=10.77'.""" client = get_client(target=target, url=url, api_key=api_key, verify_ssl=verify_ssl) result = client.get( "status/dhcp_server/leases", params=client.list_params(limit, offset, query) ) return json.dumps(result, indent=2, default=str) @mcp.tool() def pfsense_list_dhcp_servers(limit: int = 50, offset: int = 0, target: str = "", url: str = "", api_key: str = "", verify_ssl: bool | None = None) -> str: """List DHCP server configurations per interface (ranges, options, enabled state).""" client = get_client(target=target, url=url, api_key=api_key, verify_ssl=verify_ssl) result = client.get("services/dhcp_servers", params={"limit": limit, "offset": offset}) return json.dumps(result, indent=2, default=str) @mcp.tool() def pfsense_list_dhcp_static_mappings( interface_id: str, limit: int = 200, offset: int = 0, target: str = "", url: str = "", api_key: str = "", verify_ssl: bool | None = None, ) -> str: """List DHCP static mappings (reservations) for a DHCP server. interface_id: parent interface (e.g. 'lan').""" client = get_client(target=target, url=url, api_key=api_key, verify_ssl=verify_ssl) result = client.get( "services/dhcp_server/static_mappings", params={"parent_id": interface_id, "limit": limit, "offset": offset}, ) return json.dumps(result, indent=2, default=str) @mcp.tool() def pfsense_add_dhcp_static_mapping( interface_id: str, mac: str, ip: str, hostname: str = "", description: str = "", target: str = "", url: str = "", api_key: str = "", verify_ssl: bool | None = None, ) -> str: """Add a DHCP static mapping (reservation) on an interface's DHCP server.""" payload: dict[str, Any] = { "parent_id": interface_id, "mac": mac, "ipaddr": ip, "hostname": hostname, "descr": description, } if settings.dry_run: return json.dumps(is_dry_run_response("add_dhcp_static_mapping", payload), indent=2) require_writes("add_dhcp_static_mapping", payload) client = get_client(target=target, url=url, api_key=api_key, verify_ssl=verify_ssl) result = client.post("services/dhcp_server/static_mapping", data=payload) audit_log("add_dhcp_static_mapping", payload | {"result": result}) return json.dumps(result, indent=2, default=str) @mcp.tool() def pfsense_delete_dhcp_static_mapping(interface_id: str, mapping_id: int, target: str = "", url: str = "", api_key: str = "", verify_ssl: bool | None = None) -> str: """Delete a DHCP static mapping by id.""" payload = {"parent_id": interface_id, "id": mapping_id} if settings.dry_run: return json.dumps(is_dry_run_response("delete_dhcp_static_mapping", payload), indent=2) require_writes("delete_dhcp_static_mapping", payload) client = get_client(target=target, url=url, api_key=api_key, verify_ssl=verify_ssl) result = client.delete("services/dhcp_server/static_mapping", params=payload) audit_log("delete_dhcp_static_mapping", payload | {"result": result}) return json.dumps(result, indent=2, default=str)