Sentinel

An AI agent that runs my homelab — I describe a change in plain English, it plans the tool calls and executes them.

I got tired of SSHing through three different boxes to do one small thing — spin up a container, open a port for it, check whether the firewall was actually dropping the scans hitting it. Sentinel is an LLM agent that does all of that from a sentence: I describe what I want, Claude figures out which tools to call, shows me the plan, and only runs it once I confirm.

It's a real agentic system, not a chat wrapper — a single typed tool layer driving my Proxmox host, my OPNsense firewall, and the Docker box, exposed through the Model Context Protocol and the Claude Agent SDK.

What it controls

How it works

Minimal moving parts, but it's a proper agent loop. Here's the whole path from a sentence to a change on the hardware:

  plain English
       │
       ▼
  Claude Opus 4.8  ── agentic tool-use loop, adaptive thinking
       │   picks + sequences tools
       ▼
  tool layer ──▶ safety gate (dry-run / confirm) ──▶ audit log (SQLite)
       │
       ▼
  providers ──▶ Proxmox · OPNsense · Docker      (API / REST / SSH)

The agent loop

At the center is a Claude Agent SDK loop running Claude Opus 4.8 with adaptive thinking. It's standard tool use / function calling: the model reads the request, decides which tools to call and in what order, reads each result, and either calls more or answers. The model plans; the harness executes. I stay in the loop for anything that changes state.

One tool layer, three surfaces

Every capability is a single typed function in one module. That module is wrapped two ways: a FastMCP server over stdio (the MCP server that Claude Desktop and the Claude Code plugin connect to) and an in-process MCP server registered with the Claude Agent SDK (what the deploy/chat agent and the CLI drive). Same functions behind all of it, so a deploy behaves identically whether it comes from Claude Desktop, Claude Code, or the terminal.

What happens on every call

A tool that changes something never just acts. It runs a fixed pipeline:

  1. builds a PlannedChange — a structured description of what it would do, flagged destructive or not;
  2. passes it through the safety gate — returns the plan untouched unless confirm=true, and refuses outright if it's destructive and unconfirmed;
  3. calls the provider to do the real I/O;
  4. writes the outcome — planned or applied — to an append-only SQLite audit log;
  5. returns a typed ActionResult.

The gate and the audit live in the tool layer, not in any one surface — so the same guarantees hold whether the call came from the agent, the MCP server, or the CLI.

Talking to the hardware

Providers are thin and side-effect-honest — a method that mutates, mutates; nothing hides logic.

The one-sentence deploy and the dashboard run as a FastAPI service (the agent) alongside a small Next.js front-end that reads the audit log and calls the agent to plan a deploy.

Example

One sentence becomes a real command. It plans first, then asks.

$ sentinel deploy "run postgres:16, 2GB ram, expose 5432, volume pgdata"

plan (nothing applied yet):
  docker run -d --name pg --restart unless-stopped --memory 2g \
    -p 5432:5432 -v pgdata:/var/lib/postgresql/data postgres:16

Apply this plan? [y/N]

Safe by default

Every change is a dry run unless I pass a confirm. Anything destructive — stopping a VM, deleting a firewall rule, removing a container — refuses to run without it, and every action gets logged.

I added that after I once stopped sshd mid-change on the firewall and locked myself out. Recoverable, but annoying. Now firewall changes only go through supported paths and back up the config first, so that particular mistake can't happen again.

Getting started

uv sync
cp config.example.yaml config.yaml   # your hosts
cp .env.example .env                  # API key + ssh key

uv run sentinel status                # read-only sanity check
uv run sentinel deploy "..."          # plans, then asks
uv run sentinel mcp                   # expose tools to Claude Desktop / Code

Full setup, including the Claude Desktop and Claude Code wiring, is in the README.

Built with

Python · Claude Opus 4.8 · Claude Agent SDK · Model Context Protocol (FastMCP) · tool-use / function calling · FastAPI · Typer · Pydantic · paramiko · proxmoxer · httpx · SQLite · a small Next.js dashboard · Docker.