Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
OPNSENSE_HOSTYesThe base URL of your OPNsense firewall (e.g., https://192.168.1.1).
OPNSENSE_API_KEYYesYour OPNsense API key.
OPNSENSE_API_SECRETYesYour OPNsense API secret.
OPNSENSE_VERIFY_SSLNoSet to true to verify SSL certificates (defaults to false for self-signed certificates).false

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
opnsense_search_aliasesA

Search configured firewall aliases and their types, contents and descriptions.

This returns the alias configuration. For a URL table or GeoIP alias the configured value is a feed URL or a country list, not the addresses currently loaded; use opnsense_get_alias_contents to see what pf actually holds.

Args: params (SearchAliasesInput): Validated input containing: - search (str): Free-text filter (default: "") - alias_type (str): Restrict to one alias type (default: "") - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "aliases": [ {"uuid": str, "name": str, "type": str, "enabled": str, "content": str, "description": str} ] }

Examples: - Use when: "What aliases exist?" -> search="" - Use when: "Show me the GeoIP aliases" -> alias_type="geoip" - Don't use when: You want the addresses currently in the table (use opnsense_get_alias_contents)

opnsense_get_alias_contentsA

Get the addresses currently loaded in an alias's pf table.

This reads the live table, so it reflects resolved hostnames, downloaded URL table feeds and refreshed GeoIP sets. When a rule using an alias is not behaving as expected, comparing this against the alias configuration is usually the fastest way to find that a feed failed to refresh.

Args: params (AliasContentsInput): Validated input containing: - name (str): Alias name - limit (int): Max entries, 1-200 (default: 25) - offset (int): Entries to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "alias": str, "entries": [{"ip": str}] }

Error Handling: - Returns a not-loaded message when the alias exists but has no pf table, which happens when no enabled rule references it

opnsense_find_alias_referencesA

Find where an alias is referenced, so you know what would break if it changed.

Tries the firewall's own reference lookup first. If that endpoint is unavailable on this release, falls back to scanning filter rules for the alias name and says so in the output, since that fallback covers filter rules only and not NAT rules or other aliases that nest this one.

Args: params (AliasReferencesInput): Validated input containing: - name (str): Alias name - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown list of referencing objects, or JSON with this schema: {"alias": str, "source": "api"|"rule_scan", "references": [...]}

Examples: - Use when: "Is this alias still used?" before deleting it - Use when: "What breaks if I empty this list?"

opnsense_update_alias_entriesA

Add or remove entries in an alias table.

Requires OPNSENSE_ALLOW_WRITE=true. Unlike rule edits, these changes take effect immediately: the entry is written into the running pf table without an apply step and without a savepoint to roll back to. That makes this the right tool for blocking an address quickly, and the wrong tool for anything you have not verified, because adding a network to an alias that a block rule references can cut off traffic you depend on.

Entries are applied one at a time; the result reports each outcome separately so a partial failure is visible rather than silent.

Args: params (UpdateAliasEntriesInput): Validated input containing: - name (str): Alias name - action (str): "add" or "delete" - entries (list[str]): 1-100 IPs, networks or hostnames - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Per-entry outcome, or JSON with this schema: { "alias": str, "action": str, "results": [{"entry": str, "status": str, "detail": str}], "succeeded": int, "failed": int }

Error Handling: - Returns a write-disabled explanation when OPNSENSE_ALLOW_WRITE is not set - Reports per-entry failures without aborting the remaining entries

opnsense_get_firewall_logA

Read recent firewall log entries and filter them by verdict, interface, address, port or protocol.

This is the primary tool for "why was this traffic blocked". Two caveats that change how you should read the results:

  • Only rules with logging enabled produce entries. Traffic silently dropped by the default deny rule appears as a block against the default rule label; traffic allowed by a rule with logging off produces nothing at all. Absence of a log entry is therefore not evidence that traffic was blocked.

  • Filtering is applied locally over the most recent entries this call retrieved, so a narrow filter over a busy firewall may return nothing simply because the matching packets are older than the window. Raise 'limit' before concluding there is no traffic.

Args: params (FirewallLogInput): Validated input containing: - action (str): "pass", "block" or "rdr" (default: "") - interface (str): Device name filter (default: "") - address (str): Substring match on source or destination (default: "") - port (str): Exact source or destination port (default: "") - protocol (str): Protocol name filter (default: "") - limit (int): Max entries to return, 1-200 (default: 25) - offset (int): Entries to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, # matching entries in the retrieved window "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "window_size": int, # raw entries examined "entries": [ {"timestamp": str, "action": str, "interface": str, "dir": str, "protoname": str, "src": str, "srcport": str, "dst": str, "dstport": str, "label": str, "rid": str} ] }

Examples: - Use when: "Why can't the IOT camera reach the internet?" -> address="192.168.30.57", action="block" - Use when: "What is being blocked on WAN right now?" -> interface="pppoe0", action="block" - Don't use when: You want established connections (use opnsense_query_firewall_states)

opnsense_query_firewall_statesA

Query the pf state table to see connections currently tracked by the firewall.

States show what is happening now, while the log shows what happened. A host with many states to one destination is actively communicating; a host with none is not, regardless of what the rules permit. Byte and packet counts per state make this a good way to find which client is consuming a link.

Args: params (QueryStatesInput): Validated input containing: - filter (str): Address or port to match (default: "" = all states) - rule_id (str): Restrict to a specific rule id (default: "") - limit (int): Max states to return, 1-200 (default: 25) - offset (int): States to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "states": [ {"iface": str, "proto": str, "src_addr": str, "src_port": str, "dst_addr": str, "dst_port": str, "state": str, "packets": int, "bytes": int, "age": int, "expires": int, "id": str, "creatorid": str} ] }

Examples: - Use when: "Is this host talking to anything right now?" -> filter="192.168.30.57" - Use when: "What's saturating the WAN?" -> filter="", sort by bytes in the output - Don't use when: You need history (use opnsense_get_firewall_log)

opnsense_get_pf_statisticsA

Get packet filter engine statistics: state table usage, memory limits, counters and timeouts.

Read this when the firewall drops traffic under load rather than by policy. State table exhaustion and hitting a memory limit both show up here as non-zero counters, and neither produces a block entry in the firewall log, which is why load-related drops are so often misdiagnosed as rule problems.

Args: params (PfStatisticsInput): Validated input containing: - section (str): "memory", "timeouts", "interfaces" or "" for all - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown sections, or the raw JSON statistics object. Keys vary by OPNsense release; commonly present are current state count, state limit, searches, inserts, and per-reason drop counters.

opnsense_kill_statesA

Drop tracked connection states matching an address or subnet.

Requires OPNSENSE_ALLOW_WRITE=true. This takes effect immediately and cannot be undone: every matching connection is torn down, and clients see it as a dropped session. It does not change policy, so if a rule still permits the traffic the client will simply reconnect.

Legitimate uses are forcing a host back through a changed rule set, and cutting an active session from a compromised device. It is not a way to block a host; add the address to a blocked alias with opnsense_update_alias_entries for that.

A wildcard filter is rejected. Flushing the entire state table drops every connection through the firewall simultaneously, which is a decision for a human at a console.

Args: params (KillStatesInput): Validated input containing: - filter (str): Address or subnet whose states to drop (required) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Confirmation including how many states were dropped, or JSON: {"filter": str, "dropped": int, "result": str}

Error Handling: - Returns a write-disabled explanation when OPNSENSE_ALLOW_WRITE is not set - Rejects wildcard filters during input validation

opnsense_get_dns_overviewA

Get the state of the Unbound DNS resolver: whether it is running, whether DNS blocklisting is active, and its cache and query counters.

Clients reporting "the internet is down" when routing is fine is almost always DNS. Check here before looking at firewall rules: a stopped resolver or an exhausted cache produces exactly that symptom with no blocks in the firewall log.

Args: params (DnsOverviewInput): Validated input containing: - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown summary, or JSON with this schema: { "service": {"status": str}, # "running" / "stopped" "blocklist_enabled": bool, "stats": {...} # cache hits/misses, query counts } Sections whose endpoint is unavailable are omitted.

Examples: - Use when: "Is DNS working?" or "Is the blocklist on?" - Don't use when: You want to see individual lookups (use opnsense_search_dns_queries)

opnsense_search_dns_queriesA

Search recent DNS lookups seen by the Unbound resolver, including which client asked and whether the answer was blocked.

This requires Unbound's reporting to be enabled in Services -> Unbound DNS -> Reporting; without it the resolver answers queries but records nothing and this tool returns an empty result even on a busy network.

Pair this with the firewall log when a client behaves oddly: a device resolving domains it should not, or a blocklist hit the user did not expect, both show up here and in neither the rule set nor the state table.

Args: params (DnsQueriesInput): Validated input containing: - search (str): Filter on domain, client or blocklist (default: "") - blocked_only (bool): Only blocked queries (default: False) - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "queries": [ {"time": str, "client": str, "type": str, "domain": str, "action": str, "blocklist": str, "rcode": str} ] }

Examples: - Use when: "What did this device look up?" -> search="192.168.30.57" - Use when: "Is anything hitting the blocklist?" -> blocked_only=True

opnsense_get_vpn_statusA

Get the status of WireGuard peers and OpenVPN sessions, including last handshake times and transfer counters.

For WireGuard, last handshake is the field that matters: a configured peer with no recent handshake is not connected, no matter what the interface status says. WireGuard is connectionless, so there is no "down" state to observe directly.

Args: params (VpnStatusInput): Validated input containing: - kind (str): "wireguard", "openvpn" or "both" (default: "both") - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown sections per VPN type, or JSON with this schema: { "wireguard": [ {"name": str, "interface": str, "endpoint": str, "latest-handshake": str, "transfer-rx": str, "transfer-tx": str, "allowed-ips": str} ], "openvpn": [ {"common_name": str, "real_address": str, "virtual_address": str, "bytes_received": str, "bytes_sent": str, "connected_since": str} ] } A key is omitted when that VPN type is not configured or its endpoint is absent.

Examples: - Use when: "Is my phone connected over WireGuard?" -> kind="wireguard" - Use when: "Who is on the VPN right now?" -> kind="both"

opnsense_search_firewall_rulesA

Search firewall filter rules and return them in evaluation order with their action, interface, source, destination and description.

Rules are evaluated top to bottom and the last match wins unless a rule sets quick. The 'sequence' column reflects ordering, so when diagnosing "why is this traffic blocked", read the matching rules in sequence order rather than assuming the first match applies.

Scope note: this reads the MVC filter model, which is what Firewall -> Rules uses on current OPNsense releases. On older releases some legacy rules may live outside this model and will not appear.

Args: params (SearchRulesInput): Validated input containing: - search (str): Free-text filter (default: "") - enabled_only (bool): Skip disabled rules (default: False) - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "rules": [ { "uuid": str, # pass to opnsense_get_firewall_rule "sequence": str, "enabled": str, "action": str, # "pass", "block", "reject" "interface": str, "direction": str, "ipprotocol": str, "protocol": str, "source_net": str, "source_port": str, "destination_net": str, "destination_port": str, "description": str } ] }

Examples: - Use when: "Which rules mention the IOT VLAN?" -> search="IOT" - Use when: "What blocks port 445?" -> search="445" - Don't use when: You want NAT rules (those live under firewall/source_nat and firewall/d_nat, reachable via opnsense_api_request)

opnsense_get_firewall_ruleA

Get every configured field of a single firewall rule by UUID.

The search tool returns a summary; this returns the full record including scheduling, state policy, logging, gateway assignment, categories and reply-to, which is what you need before changing or reproducing a rule.

Args: params (GetRuleInput): Validated input containing: - uuid (str): Rule UUID - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown field list, or JSON containing the full rule object. Enum fields are flattened from OPNsense's {"value": ..., "selected": 1} form to the selected value.

Error Handling: - Returns a not-found message when the UUID does not exist

opnsense_get_rule_statisticsA

Get per-rule pf counters: how many times each rule was evaluated, and how many packets, bytes and states it accounted for.

This turns "which rules actually matter" into a data question. Counters reset every time the ruleset is reloaded, so a zero count means "not matched since the last apply", not "never matched". Confirm the ruleset has been stable for a while before concluding a rule is dead.

Args: params (RuleStatisticsInput): Validated input containing: - search (str): Filter on rule label or interface (default: "") - unused_only (bool): Only rules with zero evaluations (default: False) - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "rule_stats": [ {"label": str, "evaluations": int, "packets": int, "bytes": int, "states": int, "interface": str} ] }

Examples: - Use when: "Which rules are never hit?" -> unused_only=True - Use when: "How much traffic does the guest block rule see?" -> search="guest"

opnsense_toggle_firewall_ruleA

Enable or disable a single firewall rule.

Requires OPNSENSE_ALLOW_WRITE=true. The change is staged, not live: nothing takes effect until opnsense_apply_firewall_changes runs. Take a savepoint before toggling anything that could affect your own access path, so an unreachable firewall reverts itself after 60 seconds instead of needing console access.

Args: params (ToggleRuleInput): Validated input containing: - uuid (str): Rule UUID - enabled (bool): True to enable, False to disable - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Confirmation text noting that an apply is still required, or JSON: {"result": str, "uuid": str, "enabled": bool, "applied": false}

Error Handling: - Returns a write-disabled explanation when OPNSENSE_ALLOW_WRITE is not set - Returns a not-found message when the UUID does not exist

opnsense_apply_firewall_changesA

Activate staged firewall changes, with automatic rollback protection.

Requires OPNSENSE_ALLOW_WRITE=true. This wraps OPNsense's savepoint mechanism, which exists because a bad rule can lock you out of the firewall that hosts the API you would need to fix it.

The sequence, in order:

  1. mode='savepoint' -> returns a revision timestamp

  2. make changes -> e.g. opnsense_toggle_firewall_rule

  3. mode='apply' -> with revision=<from step 1>; the firewall will revert to that revision by itself in 60 seconds

  4. verify connectivity -> confirm the firewall is still reachable

  5. mode='cancel_rollback' -> with the same revision; makes the change permanent

Skipping step 5 is safe: the firewall reverts. Skipping step 1 is not: an apply with no savepoint cannot be undone remotely.

mode='revert' rolls back to a revision immediately without waiting.

Args: params (ApplyChangesInput): Validated input containing: - mode (str): 'savepoint', 'apply', 'cancel_rollback' or 'revert' - revision (str): Revision timestamp, required for all modes except savepoint - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Text describing what happened and what to do next, or JSON: {"mode": str, "revision": str, "result": str, "next_step": str}

Error Handling: - Returns a write-disabled explanation when OPNSENSE_ALLOW_WRITE is not set - Returns a validation message when revision is missing for a mode that needs it

opnsense_list_interfacesA

List the firewall's interfaces with their config name, device name, addresses and link status.

This is the mapping tool for the rest of the server. Firewall rules reference the config name (lan, wan, opt1...), while diagnostics, ARP entries and counters report the device name (igb0, vlan0.30, pppoe0). Call this first whenever a question spans both worlds, for example "which rules apply to the IOT VLAN".

Args: params (ListInterfacesInput): Validated input containing: - search (str): Substring filter (default: "") - include_disabled (bool): Include down interfaces (default: True) - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "interfaces": [ { "identifier": str, # config name, e.g. "opt2" "device": str, # device name, e.g. "vlan0.30" "description": str, # label shown in the GUI, e.g. "IOT" "status": str, # "up" / "down" / "no carrier" "ipv4_address": str, "ipv6_address": str, "macaddr": str, "enabled": bool } ] }

Examples: - Use when: "What VLANs are configured?" -> search="vlan" - Use when: "Which device is the GUEST network?" -> search="GUEST" - Don't use when: You want throughput counters (use opnsense_get_interface_statistics)

opnsense_get_interface_statisticsA

Get packet, byte, error and collision counters per interface.

These are cumulative counters since boot, not rates. To measure throughput, call twice and take the difference over the elapsed time. Rising error or collision counts on a physical NIC usually mean a duplex mismatch or a bad cable.

Args: params (InterfaceStatisticsInput): Validated input containing: - device (str): Device name to filter to, e.g. "igb0" (default: "" = all) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON mapping device name to a counter object: {"": {"packets in": int, "packets out": int, "bytes in": int, "bytes out": int, "errors in": int, "errors out": int, "collisions": int, ...}}

Examples: - Use when: "Is the WAN dropping packets?" -> device="pppoe0" - Don't use when: You want live per-connection traffic (use opnsense_query_firewall_states)

opnsense_search_neighborsA

Search the ARP (IPv4) and NDP (IPv6) neighbour tables to find which device holds an address, or which addresses a MAC has.

This is the fastest way to identify an unknown host on a segment. Entries only exist for devices that have communicated recently, so an absent entry does not prove a device is offline. For a fuller picture of a specific segment, pair this with opnsense_list_dhcp_leases.

Args: params (SearchNeighboursInput): Validated input containing: - search (str): Substring on IP, MAC, hostname or interface (default: "") - family (str): "ipv4", "ipv6" or "both" (default: "ipv4") - resolve_hostnames (bool): Resolve names, slower (default: False) - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "neighbors": [ {"ip": str, "mac": str, "intf": str, "hostname": str, "manufacturer": str, "expires": int, "family": "ipv4"|"ipv6"} ] }

Examples: - Use when: "What is 192.168.30.57?" -> search="192.168.30.57" - Use when: "Which IPs does this MAC hold?" -> search="a4:83:e7:11:22:33", family="both" - Don't use when: You want the full DHCP allocation (use opnsense_list_dhcp_leases)

opnsense_get_routesA

Get the firewall's active routing table, including the default route and any policy or VPN routes.

Useful when traffic reaches the firewall but leaves the wrong way, or when checking that a VPN tunnel installed the routes it should have.

Args: params (GetRoutesInput): Validated input containing: - search (str): Filter on destination, gateway or interface (default: "") - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "routes": [ {"destination": str, "gateway": str, "netif": str, "flags": str, "proto": str, "expire": str} ] }

Examples: - Use when: "What's the default route?" -> search="default" - Use when: "Did the WireGuard tunnel add its routes?" -> search="wg"

opnsense_list_dhcp_leasesA

List DHCP leases handed out by the firewall, across the Kea and legacy ISC backends.

Leases map MAC addresses to IPs and hostnames, which is usually the best way to put a name to a device seen in firewall logs. A device with a static IP will not appear here; check opnsense_search_neighbors instead.

Args: params (DhcpLeasesInput): Validated input containing: - search (str): Filter on IP, MAC or hostname (default: "") - backend (str): "auto", "kea" or "isc" (default: "auto") - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "backend": "kea"|"isc", "leases": [ {"address": str, "hwaddr": str, "hostname": str, "if_descr": str, "state": str, "expire": str} ] }

Examples: - Use when: "Who has 192.168.30.57?" -> search="192.168.30.57" - Use when: "List everything on the IOT VLAN" -> search="192.168.30." - Don't use when: The host is statically addressed (use opnsense_search_neighbors)

opnsense_api_requestA

Call any OPNsense API endpoint directly. Use this only for endpoints the other tools do not cover.

The dedicated tools shape their output, handle pagination and explain their failures; this returns raw JSON. Reach for it when you need something outside their scope, for example NAT rules (firewall/source_nat, firewall/d_nat), traffic shaping, IDS/Suricata, captive portal, certificates, or plugin endpoints such as CrowdSec.

Endpoint naming: the published reference lists commands in snake_case but URLs use camelCase, so get_interface_names becomes getInterfaceNames and query_states becomes queryStates.

GET is always permitted. POST requires both OPNSENSE_ALLOW_WRITE=true and confirm=true. A small set of endpoints is refused outright regardless of settings: reboot, halt, factory reset, configuration revert, snapshot activation and user account changes. Those need a human who can reach the console if it goes wrong.

Args: params (ApiRequestInput): Validated input containing: - module (str): e.g. "firewall" - controller (str): e.g. "source_nat" - command (str): camelCase command, e.g. "searchRule" - method (str): "GET" or "POST" (default: "GET") - segments (list[str]): Extra path segments (default: []) - params (dict): Query parameters (default: {}) - body (dict): JSON body for POST (default: {}) - confirm (bool): Required true for POST (default: False) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: The endpoint's JSON response, pretty-printed. Responses longer than 20000 characters are truncated with a note; narrow the query rather than relying on the truncated tail.

Examples: - Use when: "List NAT port forwards" -> module="firewall", controller="source_nat", command="searchRule" - Use when: "What log filter fields exist?" -> module="diagnostics", controller="firewall", command="logFilters" - Don't use when: A dedicated tool exists; it will give better-structured output

Error Handling: - Refuses forbidden endpoints with an explanation - Returns a write-disabled explanation for POST when writes are off - Returns a confirm-required message for POST without confirm=true

opnsense_get_system_statusA

Get a health overview of the OPNsense firewall: version, uptime, load, memory, optional temperature sensors and optional disk usage.

Start here when asked how the firewall is doing, what version it runs, or whether it is under load. This does NOT report firmware updates - use opnsense_get_firmware_status for that.

Args: params (SystemStatusInput): Validated input containing: - include_temperature (bool): Include temperature sensors (default: True) - include_disk (bool): Include filesystem usage (default: True) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown summary, or JSON with this schema: { "system": {...}, # product name, versions, uptime, CPU model "resources": {...}, # load average, memory and swap usage "temperature": [...], # optional, per-sensor readings "disk": {...}, # optional, per-filesystem usage "status": {...} # pending notices and their severity } Sections whose endpoint is unavailable on this release are omitted.

Examples: - Use when: "Is the firewall healthy?" or "What OPNsense version am I on?" - Don't use when: You want pending updates (use opnsense_get_firmware_status) - Don't use when: You want per-interface throughput (use opnsense_get_interface_statistics)

opnsense_get_firmware_statusA

Check for pending OPNsense updates and report the installed version and repository state.

Note that OPNsense caches its update check. If the result looks stale, the firmware/check endpoint (reachable via opnsense_api_request) forces a refresh. This tool never installs anything.

Args: params (FirmwareStatusInput): Validated input containing: - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown summary, or JSON with this schema: { "status": str, # "ok", "error", "none" "status_msg": str, # human-readable state, when present "product_version": str, # currently installed version "product_latest": str, # latest available version "upgrade_needs_reboot": str, # "1" when a reboot would be required "download_size": str, "updates": list # per-package update records }

Examples: - Use when: "Are there updates pending?" or "What's the latest OPNsense version?" - Don't use when: You want uptime or load (use opnsense_get_system_status)

opnsense_list_servicesA

List services registered with OPNsense and whether each one is running.

Covers core services (unbound, openvpn, ntpd) and plugin services (crowdsec, wireguard, os-* plugins). Use this to get the exact service id before calling opnsense_control_service.

Args: params (ListServicesInput): Validated input containing: - search (str): Substring filter on name/description (default: "") - running_only (bool): Only running services (default: False) - limit (int): Max records, 1-200 (default: 25) - offset (int): Records to skip (default: 0) - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Markdown table, or JSON with this schema: { "total": int, "count": int, "offset": int, "has_more": bool, "next_offset": int|null, "services": [ {"name": str, "description": str, "running": int, "locked": int} ] }

Examples: - Use when: "Is unbound running?" -> search="unbound" - Use when: "What services are stopped?" -> running_only=False, then read the column - Don't use when: You want to restart something (use opnsense_control_service)

opnsense_control_serviceA

Start, stop or restart a service on the firewall.

Requires OPNSENSE_ALLOW_WRITE=true. Stopping a service is disruptive: stopping unbound breaks DNS for every client behind the firewall, and stopping a VPN service drops its tunnels. Confirm the intent before calling with action='stop'.

Args: params (ControlServiceInput): Validated input containing: - name (str): Service id, e.g. "unbound" - action (str): "start", "stop" or "restart" - service_id (str): Instance id for multi-instance services (default: "") - response_format (ResponseFormat): 'markdown' or 'json'

Returns: str: Confirmation text, or JSON: {"result": str, "service": str, "action": str}

Error Handling: - Returns a write-disabled explanation when OPNSENSE_ALLOW_WRITE is not set - Returns a 403 privilege message when the API key cannot control services

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription
opnsense_interfacesMapping of interface config names (lan, wan, opt1) to device names (igb0, vlan0.30) with addresses and link status.
opnsense_aliasesAll configured firewall aliases with their types and contents.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aesaganda/opnsense-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server