Mcp Pfsense

io.github.felhen-aiPublished by GitHub account felhen-aiv0.3.1Updated Nov 4, 2026

Manage pfSense firewalls through AI assistants — rules, DHCP, DNS, and more

Overview

AI-generated overview

Local MCP server that lets an assistant inspect and manage a pfSense firewall: rules, DHCP reservations, DNS overrides, services and logs.

What you can do
  • Read system and network state: version, CPU, memory, uptime, interfaces, gateway health, ARP table, DHCP leases, services and recent firewall logs
  • List firewall rules and aliases, and stage new or deleted rules
  • List, add and delete DHCP static mappings (IP reservations)
  • List, add and delete Unbound DNS host overrides
  • Check staged changes per subsystem and apply them, or restart a service
When to use it
Useful when you administer a pfSense firewall with the pfrest REST API and want to query its state or make rule, DHCP and DNS changes conversationally instead of through the WebGUI.
Requirements
Runs locally as a Python package (Python 3.11+) via uvx or pip. Needs pfSense 2.7.x/2.8.x with the pfrest REST API v2 installed, an API user account, and the PFSENSE_HOST and PFSENSE_PASSWORD environment variables (optionally PFSENSE_USERNAME, PFSENSE_PORT, PFSENSE_SCHEME, PFSENSE_VERIFY_SSL). Network access to the firewall is required.
Before you install
Requires the firewall admin API password in PFSENSE_PASSWORD, and SSL verification is off by default (PFSENSE_VERIFY_SSL=false). Writes change firewall, DHCP and DNS configuration; deletes, service restarts and apply_changes need confirm=true, and apply_changes also activates changes a human staged in the WebGUI but has not reviewed. Granting the API user broad api-v2 or page-all privileges widens what the assistant can do.

Installation

In SourceWeft

  1. Open Mcp Pfsense in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

mcp-pfsense

[PyPI] [Python] [License: MIT]

MCP server for managing pfSense firewalls through AI assistants like Claude, ChatGPT, and Copilot.

Requires: pfrest package installed on your pfSense instance (provides the REST API).

Features

20 tools across 7 categories:

CategoryToolsDescription
Systemget_system_status, get_interfacesVersion, CPU, memory, uptime, temperature, network interfaces
Firewalllist_firewall_rules, add_firewall_rule, delete_firewall_rule, list_firewall_aliasesRule management with interface filtering, alias listing
DHCPlist_dhcp_leases, list_dhcp_static_mappings, add_dhcp_static_mapping, delete_dhcp_static_mappingActive leases, IP reservations
DNSlist_dns_host_overrides, add_dns_host_override, delete_dns_host_overrideUnbound DNS Resolver host overrides
Pending changesget_pending_changes, apply_changesSee what is staged per subsystem (firewall, dhcp, dns) and apply it
Monitoringget_gateway_status, get_arp_table, list_services, get_firewall_logsGateway health, connected devices, service status, recent raw firewall log entries
Servicesrestart_serviceRestart any pfSense service

Safety

  • Two-step confirmation for destructive operations (delete rules, delete mappings, restart services, apply changes): the tool returns a warning on first call and only executes when called again with confirm=true.
  • Writes are staged, not live. Like the pfSense WebGUI, add_* and delete_* store the change in the config but do not activate it. The tool response says so (applied: false, plus a pending note). Activate with apply_changes(subsystem, confirm=true) — which reloads that subsystem, including anything a human left staged in the WebGUI — or pass apply=true on the write itself when you explicitly want a one-shot change. Nothing the assistant does reaches the packet filter without one of those two explicit steps.
  • delete_dhcp_static_mapping takes the mapping's interface (its parent_id in list_dhcp_static_mappings) and mapping_id; a mapping is addressed by both.

Installation

bash
# Using uvx (recommended)uvx mcp-pfsense
# Using pippip install mcp-pfsense

Prerequisites

  1. pfSense with pfrest package installed
  2. A user account with API access (typically admin)

Configuration

Set environment variables:

VariableRequiredDefaultDescription
PFSENSE_HOSTYes—pfSense hostname or IP
PFSENSE_PASSWORDYes—API user password
PFSENSE_USERNAMENoadminAPI username
PFSENSE_PORTNo443API port
PFSENSE_SCHEMENohttpshttp or https
PFSENSE_VERIFY_SSLNofalseVerify SSL certificate

Claude Desktop

Add to claude_desktop_config.json:

json
{  "mcpServers": {    "pfsense": {      "command": "uvx",      "args": ["mcp-pfsense"],      "env": {        "PFSENSE_HOST": "10.10.10.1",        "PFSENSE_PASSWORD": "your-password"      }    }  }}

Claude Code

bash
claude mcp add pfsense -- uvx mcp-pfsense

Then set environment variables in your shell or .env file.

Usage Examples

Once connected, ask your AI assistant:

  • "What's the pfSense system status?"
  • "Show me all firewall rules on the LAN interface"
  • "List active DHCP leases"
  • "Add a DNS entry for nas.home.lan pointing to 10.10.10.50"
  • "What devices are connected to the network?" (ARP table)
  • "Show gateway health and latency"
  • "Show the 50 most recent firewall log entries" (read-only get_firewall_logs)
  • "Create a firewall rule to allow TCP port 8080 on LAN"
  • "Reserve IP 10.10.10.60 for MAC aa:bb:cc:dd:ee:20"

API Compatibility

  • pfSense: 2.7.x and 2.8.x
  • pfrest: REST API v2 — any v2.x release, except list_dhcp_static_mappings, which needs v2.7.0 or later (it uses the /services/dhcp_server/static_mappings collection endpoint added in that release).
  • Python: 3.11+

The endpoint, parameters and encoding each tool uses are pinned by tests/test_client_endpoints.py and tests/test_wire_format.py, derived from the pfrest v2 endpoint definitions. Versions before 0.2.0 called several endpoints that do not exist in pfrest v2 (see Troubleshooting).

Note: pfrest runs on nginx (port 80 by default), separate from the pfSense WebGUI (lighttpd on port 443). If your pfrest is configured on a non-standard port, set PFSENSE_PORT and PFSENSE_SCHEME accordingly.

Troubleshooting

Only get_system_status and get_arp_table work; everything else returns 400/404

mcp-pfsense 0.1.1 and earlier called singular endpoints for listing (/interface, /firewall/rule, /firewall/alias) and legacy paths that pfrest v2 does not serve (/status/dhcp_leases, /services/dhcpd/static_mapping, /services/unbound/host_override, /status/gateway, /status/service for GET). Upgrade to 0.2.0 or later.

403 on list_services or other reads

pfrest checks the privileges of the API user per endpoint. Grant the user the api-v2-* privileges for the endpoints you need (or page-all for full access) under System → User Manager.

ModuleNotFoundError: No module named 'mcp.server.fastmcp'

The MCP Python SDK 2.0 removed the module that mcp-pfsense 0.1.1 and earlier import, so fresh installs (uvx mcp-pfsense, pip install) failed on startup. Upgrade to 0.2.0 or later, which pins mcp<2. If you must stay on an older mcp-pfsense: uvx --with "mcp<2" mcp-pfsense.

A rule / mapping / override was created but is not in effect

That is the default: writes are staged (see Safety). Check with get_pending_changes(subsystem) and activate with apply_changes(subsystem, confirm=true), or in the WebGUI. If a write returns 200 but nothing is stored at all, the pfrest read_only setting is on (System → REST API → Settings).

Development

bash
git clone https://github.com/felhen-ai/mcp-pfsense.gitcd mcp-pfsensepython -m venv .venvsource .venv/bin/activatepip install -e ".[dev]"
# Run testspytest
# Lint and type checkruff check .mypy src/

License

MIT

Repository and registry compatibility

The canonical repository is https://github.com/felhen-ai/mcp-pfsense. Existing GitHub clone and issue URLs redirect to this repository. Do not recreate a repository at the old address, which would remove those redirects.

The canonical MCP Registry identifier is io.github.felhen-ai/mcp-pfsense. The legacy identifier io.github.antonio-mello-ai/mcp-pfsense remains supported with a separate server.legacy.json manifest. Both identifiers install the same mcp-pfsense package from PyPI, and the package name and CLI command remain unchanged. Keep both ownership markers in this README so future releases can be registered under either identifier.

Source: README.md at commit 91891e0

Tools

20

get_system_status

Get pfSense system status including version, CPU, memory, uptime, and temperature.

Takes no parameters.

get_interfaces

List all network interfaces with status and configuration.

Takes no parameters.

list_firewall_rules

List firewall rules, optionally filtered by interface.

Parametersinterface

add_firewall_rule

Add a firewall rule. Type is 'pass', 'block', or 'reject'. The rule is staged (not active) until apply_changes('firewall') is called or apply=true is passed here.

Parameterstyperequiredapplydescrsourcedstportprotocolinterfacerequiredipprotocoldestination

delete_firewall_rule

Delete a firewall rule by its ID (the `id` from list_firewall_rules). Requires confirm=true. Staged until apply_changes('firewall') is called or apply=true is passed.

Parametersapplyconfirmrule_idrequired

list_firewall_aliases

List firewall aliases (IP groups, port groups, URL lists).

Takes no parameters.

list_dhcp_leases

List active DHCP leases showing IP, MAC, hostname, and lease times.

Takes no parameters.

list_dhcp_static_mappings

List DHCP static mappings (IP reservations), optionally filtered by interface. Each mapping carries `parent_id` (its DHCP server / interface) — pass that as `interface` to delete_dhcp_static_mapping.

Parametersinterface

add_dhcp_static_mapping

Create a DHCP static mapping (IP reservation) for a MAC address. Staged until apply_changes('dhcp') is called or apply=true is passed.

Parametersmacrequiredapplydescripaddrrequiredhostnameinterfacerequired

delete_dhcp_static_mapping

Delete a DHCP static mapping. Requires confirm=true. `interface` is the mapping's DHCP server — the `parent_id` value returned by list_dhcp_static_mappings; `mapping_id` is its `id` there. Staged until apply_changes('dhcp') is called or apply=true is passed.

Parametersapplyconfirminterfacerequiredmapping_idrequired

list_dns_host_overrides

List DNS Resolver host overrides (local DNS entries).

Takes no parameters.

add_dns_host_override

Create a DNS host override entry in Unbound DNS Resolver. Staged until apply_changes('dns') is called or apply=true is passed.

Parametersiprequiredhostrequiredapplydescrdomainrequired

delete_dns_host_override

Delete a DNS host override by ID. Requires confirm=true. Staged until apply_changes('dns') is called or apply=true is passed.

Parametersapplyconfirmoverride_idrequired

get_pending_changes

Check whether a subsystem ('firewall', 'dhcp' or 'dns') has staged, unapplied changes.

Parameterssubsystemrequired

apply_changes

Apply ALL staged changes of a subsystem ('firewall', 'dhcp' or 'dns'). Requires confirm=true. This reloads the subsystem, activating every pending change — including any a human staged in the pfSense WebGUI and has not reviewed yet.

Parametersconfirmsubsystemrequired

get_gateway_status

Get gateway status including latency, packet loss, and online state.

Takes no parameters.

get_arp_table

Get ARP table showing connected devices (IP, MAC, interface).

Takes no parameters.

list_services

List all services and their running status.

Takes no parameters.

restart_service

Restart a service by name. Requires confirm=true.

Parametersnamerequiredconfirm

get_firewall_logs

Read-only view of recent firewall log entries. Each entry contains its ID and raw log text from pfrest. `limit` caps the number of entries (default 50). This tool only reads logs; it never writes to the firewall.

Parameterslimit

Version history

1
  1. v0.3.1LatestNov 4, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts