pfSense MCP

Model Context Protocol server for pfSense firewall management via the REST API v2 package (pfSense-pkg-RESTAPI). Sibling project to opnsense-mcp with the same safety model.

Features

  • 77+ tools covering system status, interfaces, firewall rules, aliases, NAT, DHCP, diagnostics, routing, VPN (OpenVPN/IPsec/WireGuard), and DNS resolver
  • Multi-firewall: connect to many pfSense boxes dynamically via pfsense_connect(url, api_key) — no static firewall in config required
  • Safety guardrails: writes disabled by default, dry-run mode, WAN any/any rule blocking, required description prefix
  • Audit logging: all mutations logged to JSONL
  • apply control: write tools accept apply=true to apply immediately, or batch changes and call the apply tool

Prerequisites

pfSense does not ship a REST API. Install the free REST API v2 package on the firewall first:

# On the pfSense shell (adjust version to your pfSense release — see pfrest.org)
pkg-static add https://github.com/jaredhendrickson13/pfsense-api/releases/latest/download/pfSense-2.8.0-pkg-RESTAPI.pkg

Then in the web UI: System → REST API → Settings (enable), and System → REST API → Keys to generate an API key.

Quick Start

1. Install

pip install -e /path/to/pfsense-mcp

Or with the Hermes venv:

/usr/local/lib/hermes-agent/venv/bin/pip install -e /root/projects/pfsense-mcp

2. Configure environment

cp .env.example .env
Variable Default Description
PFSENSE_URL https://192.168.1.1 Firewall base URL
PFSENSE_API_KEY REST API key (preferred)
PFSENSE_USERNAME / PFSENSE_PASSWORD Basic auth fallback
PFSENSE_VERIFY_SSL false Verify TLS certificate
PFSENSE_ALLOW_WRITES false Enable mutating operations
PFSENSE_DRY_RUN false Log writes without executing
PFSENSE_REQUIRE_DESCRIPTION_PREFIX mcp: Prefix for new firewall rules (descr)
PFSENSE_BLOCK_WAN_INBOUND_ANY true Block WAN any→any pass rules

3. Register with Hermes

No static firewall URL/key required — credentials are provided at runtime via pfsense_connect.

mcp_servers:
  pfsense:
    command: /usr/local/lib/hermes-agent/venv/bin/pfsense-mcp
    timeout: 120
    enabled: true
    env:
      PFSENSE_VERIFY_SSL: "false"
      PFSENSE_ALLOW_WRITES: "false"

Then hermes gateway restart. Connect to a firewall in chat:

pfsense_connect(url="https://10.77.50.1", api_key="...", name="branch-office")
pfsense_test_connection()
pfsense_list_firewall_rules()

Multi-firewall workflow

# Register firewalls (name optional — defaults from IP/hostname)
pfsense_connect("https://10.77.50.1", "key-a", name="hq")
pfsense_connect("https://10.77.60.1", "key-b", name="branch")

# List and switch
pfsense_list_targets()
pfsense_use_target("branch")

# Or pass target / inline credentials on any tool
pfsense_get_system_status(target="hq")
pfsense_get_gateway_status(url="https://10.77.60.1", api_key="key-b")
Tool Purpose
pfsense_connect Register + activate a firewall (url, api_key, name)
pfsense_use_target Switch active firewall
pfsense_list_targets List registered firewalls
pfsense_get_active_target Show current active target
pfsense_disconnect_target Remove a registered target

Optional env defaults (PFSENSE_URL, PFSENSE_API_KEY) still work as a fallback when no target is connected.

4. Register with Cursor (~/.cursor/mcp.json)

{
  "mcpServers": {
    "pfsense": {
      "command": "pfsense-mcp",
      "env": {
        "PFSENSE_URL": "https://your-pfsense",
        "PFSENSE_API_KEY": "your-api-key"
      }
    }
  }
}

Tool Categories

Read-only (always available with valid credentials)

Tool Description
pfsense_test_connection Verify API auth and connectivity
pfsense_get_system_status CPU, memory, disk, uptime
pfsense_get_interface_status Live interface status
pfsense_list_firewall_rules Filter rules (with query filters)
pfsense_list_firewall_states Active state table
pfsense_list_aliases Firewall aliases
pfsense_list_port_forwards Destination NAT rules
pfsense_list_dhcp_leases DHCP lease table
pfsense_get_arp_table ARP / IP→MAC
pfsense_get_gateway_status Gateway health
pfsense_get_firewall_log Firewall log
pfsense_get_openvpn_status OpenVPN connected clients
pfsense_get_ipsec_status IPsec SAs
pfsense_list_wireguard_tunnels WireGuard (plugin)
pfsense_list_dns_host_overrides Local DNS records
pfsense_get_config_history Config change audit trail

Write operations (require PFSENSE_ALLOW_WRITES=true)

Tool Description
pfsense_add_firewall_rule Add filter rule (descr prefix required)
pfsense_toggle_firewall_rule Enable/disable rule
pfsense_apply_firewall Apply pending changes
pfsense_add_alias / pfsense_update_alias Manage aliases
pfsense_add_port_forward Add destination NAT
pfsense_add_dhcp_static_mapping DHCP reservation
pfsense_add_dns_host_override Local DNS record
pfsense_add_static_route Static route
pfsense_service_action Start/stop/restart services
pfsense_kill_firewall_states Kill states by source
pfsense_reboot Reboot (double confirm)

Query filtering

List tools accept a query parameter with pfSense REST v2 query syntax:

query="interface=wan"                 exact match
query="descr__contains=mcp"           substring
query="ip__startswith=10.77"          prefix
query="interface=wan,protocol=tcp"    multiple conditions

Differences vs opnsense-mcp

Aspect OPNsense pfSense
API Built-in (/api/module/controller/command) Package required (/api/v2/...)
Auth key+secret basic auth X-API-Key header or basic auth
IDs UUIDs Numeric ids (positional)
Apply Savepoint + rollback workflow apply=true param or apply endpoints
Rule description field description descr

Security Notes

  • Never commit API keys; use environment variables or Hermes secrets.
  • Start read-only (PFSENSE_ALLOW_WRITES=false).
  • Create a dedicated pfSense user + API key with minimal privileges.
  • Review the audit log at PFSENSE_AUDIT_LOG_PATH.
  • pfSense numeric ids shift when objects are deleted — always list immediately before mutating by id.

License

MIT

Description
No description provided
Readme 70 KiB
Languages
Python 100%