<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: builderall</title>
    <description>The latest articles on Forem by builderall (@builderall_uniq).</description>
    <link>https://forem.com/builderall_uniq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3812062%2Fe0c94667-b3e5-4324-96ca-5a103055b104.png</url>
      <title>Forem: builderall</title>
      <link>https://forem.com/builderall_uniq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/builderall_uniq"/>
    <language>en</language>
    <item>
      <title>I gave Claude access to my OPNsense firewall — here's what happened</title>
      <dc:creator>builderall</dc:creator>
      <pubDate>Sat, 07 Mar 2026 20:33:06 +0000</pubDate>
      <link>https://forem.com/builderall_uniq/i-gave-claude-access-to-my-opnsense-firewall-heres-what-happened-nif</link>
      <guid>https://forem.com/builderall_uniq/i-gave-claude-access-to-my-opnsense-firewall-heres-what-happened-nif</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This software is provided as-is, without warranty of any kind. Upgrading firewall firmware carries inherent risk including temporary or permanent loss of network connectivity. Always maintain a tested backup of your configuration, ensure you have out-of-band access (console, IPMI) before performing major upgrades, and test in a non-production environment first. The authors are not responsible for any damage, data loss, or network outages resulting from the use of these tools. Use at your own risk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; OPNsense major upgrades can break in ways the web UI can't recover from. I built a stateful SSH upgrade script that handles reboots and pkg ABI mismatches automatically, plus a Claude MCP server so you can manage your firewall conversationally. During a live upgrade this week, we found and fixed two bugs in real time. Code on GitHub.&lt;/p&gt;




&lt;h2&gt;
  
  
  The upgrade that broke everything
&lt;/h2&gt;

&lt;p&gt;If you run OPNsense at home or in a small business, you've probably been there. A major version upgrade starts cleanly in the web UI, the progress bar moves, the firewall reboots... and then nothing works. The packages are half-installed. &lt;code&gt;pkg&lt;/code&gt; refuses to run. The web UI shows the old version. You're staring at a FreeBSD shell wondering what happened.&lt;/p&gt;

&lt;p&gt;This is not a bug in OPNsense. It's a known limitation of how the web UI handles major upgrades. The FreeBSD base and kernel get upgraded first, triggering a reboot. After that reboot, the new base ships a different &lt;code&gt;pkg&lt;/code&gt; ABI — and the old &lt;code&gt;pkg&lt;/code&gt; binary can no longer install or upgrade anything. The web UI has no recovery path for this. You're on your own.&lt;/p&gt;

&lt;p&gt;I hit this twice. The second time, I decided to fix it properly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: The SSH Upgrade Script
&lt;/h2&gt;

&lt;p&gt;The first tool I built is a Python script that runs directly on OPNsense via SSH as root. It treats a major upgrade as a &lt;strong&gt;stateful, multi-stage pipeline&lt;/strong&gt; rather than a single operation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pre-checks&lt;/td&gt;
&lt;td&gt;Disk space (2GB minimum), pkg database health, lock file cleanup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanup&lt;/td&gt;
&lt;td&gt;Remove unused packages, clean cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup&lt;/td&gt;
&lt;td&gt;Save &lt;code&gt;config.xml&lt;/code&gt; and installed package list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base/Kernel&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;opnsense-update -bk&lt;/code&gt; — reboots if kernel changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fix pkg&lt;/td&gt;
&lt;td&gt;Reinstall &lt;code&gt;pkg&lt;/code&gt; using &lt;code&gt;pkg-static&lt;/code&gt; after base upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packages&lt;/td&gt;
&lt;td&gt;Switch repo, refresh catalog, upgrade all packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verification&lt;/td&gt;
&lt;td&gt;Check pkg database, verify critical services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Dry-run by default.&lt;/strong&gt; Every run shows exactly what will happen without making any changes. You add &lt;code&gt;-x&lt;/code&gt; to execute for real.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Preview what a major upgrade would do&lt;/span&gt;
./opnsense-upgrade.py &lt;span class="nt"&gt;-t&lt;/span&gt; 26.1

&lt;span class="c"&gt;# Execute it&lt;/span&gt;
./opnsense-upgrade.py &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 26.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stateful — survives reboots.&lt;/strong&gt; When the script triggers a reboot, it saves its state to &lt;code&gt;/var/db/opnsense-upgrade.state&lt;/code&gt; and installs an auto-resume script in &lt;code&gt;/etc/rc.local.d/&lt;/code&gt;. After the firewall comes back up, it picks up exactly where it left off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixes pkg automatically.&lt;/strong&gt; After a base/kernel upgrade, the script detects the ABI mismatch and reinstalls &lt;code&gt;pkg&lt;/code&gt; using &lt;code&gt;pkg-static&lt;/code&gt; before attempting any package upgrades. This is the step the web UI misses entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safety guards.&lt;/strong&gt; Major upgrades are blocked when minor updates are pending. The script also validates disk space, checks pkg database health, and blocks duplicate runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing status:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Major upgrade (25.7 → 26.1) — tested successfully in production, including base/kernel reboot and pkg ABI fix&lt;/li&gt;
&lt;li&gt;Minor updates via SSH script (26.1.1 → 26.1.2) — tested successfully in production&lt;/li&gt;
&lt;li&gt;Minor update via MCP server (26.1.2 → 26.1.3) — tested today, including live bug fixes&lt;/li&gt;
&lt;li&gt;Next major upgrade — script is ready; OPNsense API currently reports 26.7 as the next major version but it has not been released yet&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 2: The Claude MCP Server
&lt;/h2&gt;

&lt;p&gt;The SSH script solved the reliability problem. But I wanted to go further — managing the firewall conversationally without logging into a shell at all.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;MCP&lt;/strong&gt; (Model Context Protocol) comes in. MCP is Anthropic's open protocol for giving AI assistants structured access to external tools and data sources. You define tools with typed inputs and outputs, register the server with Claude Code, and Claude can call those tools in response to natural language requests.&lt;/p&gt;

&lt;p&gt;I built an MCP server in Python (&lt;code&gt;httpx&lt;/code&gt; + Anthropic's &lt;code&gt;mcp&lt;/code&gt; SDK) that connects Claude directly to OPNsense's REST API. Once registered, you can manage your firewall from a chat window:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Check my firewall for updates"&lt;br&gt;
"Run a pre-upgrade health check"&lt;br&gt;
"Show me the 26.7 changelog"&lt;br&gt;
"Apply the latest update"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The tools
&lt;/h3&gt;

&lt;p&gt;The server exposes 10 tools across two categories:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read tools&lt;/strong&gt; (no confirmation needed):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current version, FreeBSD base, next major version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;check_updates&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minor/major update availability, reboot status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pre_upgrade_check&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full health assessment with go/no-go verdict&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;upgrade_status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Monitor an in-progress upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_changelog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Changelog for any version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_packages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All installed packages with versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;system_info&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uptime, load average, top processes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Write tools&lt;/strong&gt; (require explicit confirmation):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;run_update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trigger a minor update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;run_upgrade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trigger a major upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reboot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot the firewall&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Security model
&lt;/h3&gt;

&lt;p&gt;This is worth dwelling on. Giving an AI access to your firewall sounds alarming. The MCP approach is actually safer than SSH in several ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scoped API keys&lt;/strong&gt; — the Claude MCP user can only call firmware and diagnostics endpoints. It cannot touch firewall rules, VPN config, DHCP, or anything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revocable instantly&lt;/strong&gt; — delete the API key in OPNsense and Claude loses access immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit trail built in&lt;/strong&gt; — OPNsense logs every API call under System &amp;gt; Log Files &amp;gt; Audit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirmation required&lt;/strong&gt; — write tools require you to explicitly approve before Claude proceeds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only mode&lt;/strong&gt; — set &lt;code&gt;OPNSENSE_READ_ONLY=true&lt;/code&gt; to disable all write tools entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklmmyke2t2yno6dbbrf4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklmmyke2t2yno6dbbrf4.png" alt=" " width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;System &amp;gt; Access &amp;gt; Users — the claude-mcp user alongside root&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit User — assign all four privileges in the Privileges field&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vs1ilcvf1la9wb4hza0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vs1ilcvf1la9wb4hza0.png" alt=" " width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: A live upgrade — and two bugs found mid-session
&lt;/h2&gt;

&lt;p&gt;I recently used the MCP server to upgrade my OPNsense from 26.1.2 to 26.1.3. Here's what happened — including bugs found and fixed during the session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 1: Stale firmware cache blocks the update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Check my firewall for updates

Claude: Minor update available: 26.1.3
        Reboot status: not required
        Want me to run the update?

You: Yes

Claude: [calls run_update]
        "System is already up to date. No update needed."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;check_updates&lt;/code&gt; just told us 26.1.3 was available. Why is &lt;code&gt;run_update&lt;/code&gt; saying we're up to date?&lt;/p&gt;

&lt;p&gt;The OPNsense firmware daemon caches its status. &lt;code&gt;check_updates&lt;/code&gt; got a fresh response showing 26.1.3. &lt;code&gt;run_update&lt;/code&gt; made a second API call seconds later and got a stale "up to date" response.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;check_updates&lt;/code&gt; had a fallback: if &lt;code&gt;product_latest != product_version&lt;/code&gt; (comparing version strings directly), report an update as available regardless of the status field. &lt;code&gt;run_update&lt;/code&gt; only checked the status field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; apply the same two-condition logic to &lt;code&gt;run_update&lt;/code&gt;. Committed and pushed mid-session.&lt;/p&gt;

&lt;h3&gt;
  
  
  The update runs — and the firewall reboots
&lt;/h3&gt;

&lt;p&gt;After the fix, the update triggered successfully. 82 packages, 280MB of downloads. Then — connection lost.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;check_updates&lt;/code&gt; had said "Reboot status: not required." Yet the firewall rebooted mid-update.&lt;/p&gt;

&lt;p&gt;This is a subtle distinction: "reboot not required" reflects the &lt;em&gt;current state&lt;/em&gt; — no reboot pending from a previous operation. It does not predict whether the upcoming update will reboot. The 26.1.3 update included kernel and base packages, so OPNsense rebooted automatically.&lt;/p&gt;

&lt;p&gt;After ~3 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Check version

Claude: OPNsense version: 26.1.3 ✓
        Base package: 26.1.3
        Kernel package: 26.1.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7r95kqw9j0revkbtjg0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7r95kqw9j0revkbtjg0.png" alt=" " width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug 2: Post-reboot false "reboot needed" flag
&lt;/h3&gt;

&lt;p&gt;Right after the reboot, &lt;code&gt;check_updates&lt;/code&gt; reported "reboot needed" again.&lt;/p&gt;

&lt;p&gt;The firmware daemon runs early in boot and sees the &lt;code&gt;needs_reboot&lt;/code&gt; flag before it clears. The timing check then reports it as genuine: "daemon ran after boot, so flag is genuine." But the reboot already happened as part of the update that just completed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; extend the staleness check — if no packages are pending AND the installed version equals the latest available, the flag is always treated as a leftover artifact regardless of daemon timing. Two lines of Python, committed immediately.&lt;/p&gt;

&lt;p&gt;Three bugs found and fixed in a single upgrade session. Real-world testing works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: Pre-upgrade health check
&lt;/h2&gt;

&lt;p&gt;Before any upgrade, &lt;code&gt;pre_upgrade_check&lt;/code&gt; gives you a structured go/no-go assessment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pre-Upgrade Assessment
========================================

Current version: 26.1.3
Minor update:    up to date
Major upgrade:   26.7 -- planned, not yet released on mirror
Reboot:          not required
In-progress:     none
Obsolete py37:   none found

----------------------------------------
VERDICT: System is up to date. Major upgrade to 26.7 is planned but not yet released.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This covers everything that could cause an upgrade to fail: pending minor updates (must be applied before a major upgrade), stale reboot flags, in-progress upgrades, and obsolete Python 3.7 packages that break during FreeBSD base upgrades.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: Split routing recovery tools
&lt;/h2&gt;

&lt;p&gt;The third component is for when a major upgrade goes wrong and takes your network with it.&lt;/p&gt;

&lt;p&gt;If OPNsense loses LAN connectivity mid-upgrade, you're stuck: you need the internet to download packages, but you need the firewall to reach the internet. The PowerShell scripts in &lt;code&gt;ps1/&lt;/code&gt; solve this for Windows users by setting up split routing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wired interface&lt;/strong&gt; → OPNsense directly (SSH access to resume the upgrade)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WiFi interface&lt;/strong&gt; → Internet (download packages, read docs)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\ps1\Enable-SplitRouting-WithModule.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then SSH into OPNsense and resume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./opnsense-upgrade.py &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Everything is open source: &lt;strong&gt;&lt;a href="https://github.com/builderall/opnsense-upgrade" rel="noopener noreferrer"&gt;https://github.com/builderall/opnsense-upgrade&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;opnsense-upgrade/
├── python/    # SSH upgrade script (runs on OPNsense)
├── mcp/       # Claude MCP server (runs on your workstation)
└── ps1/       # Windows split routing recovery tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MCP server setup (10 minutes):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a restricted API user in OPNsense with these privileges:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;System: Firmware&lt;/code&gt; — check for updates, trigger upgrades, reboot&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Diagnostics: System Activity&lt;/code&gt; — uptime (used to validate reboot status)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Diagnostics: Backup &amp;amp; Restore&lt;/code&gt; — configuration backup access&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Diagnostics: System Health&lt;/code&gt; — system health metrics&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Generate an API key and store it in &lt;code&gt;mcp/.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install dependencies: &lt;code&gt;pip install -r mcp/requirements.txt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Register with Claude Code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--scope&lt;/span&gt; user opnsense &lt;span class="nt"&gt;--&lt;/span&gt; bash &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"cd ~/projects/opnsense-upgrade/mcp &amp;amp;&amp;amp; exec .venv/bin/python -m src.opnsense_mcp.server"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Restart Claude Code and ask: &lt;em&gt;"What version is my OPNsense?"&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Full instructions in &lt;a href="https://github.com/builderall/opnsense-upgrade/blob/master/mcp/SETUP.md" rel="noopener noreferrer"&gt;mcp/SETUP.md&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The remaining untested scenario is a full major upgrade (26.1 → 26.7) — which requires OPNsense to actually release 26.7. When that happens, this will be the first real test of the auto-resume logic in the Python script.&lt;/p&gt;

&lt;p&gt;The broader pattern here — wrapping device management APIs in MCP servers and letting Claude handle the conversational layer — applies well beyond OPNsense. Any device with a REST API and a need for careful, audited operations is a candidate.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;MIT licensed. Contributions welcome at &lt;a href="https://github.com/builderall/opnsense-upgrade" rel="noopener noreferrer"&gt;https://github.com/builderall/opnsense-upgrade&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opnsense</category>
      <category>pfsense</category>
      <category>homelab</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
