Pete

The Stack

All posts / Docker (17)

Ithaca, NY

Shipped mcp-unifi v0.3.0 today. Forty-one tools for managing self-hosted UniFi gateways from any MCP client. Adds 26 new tools across four tiers: CRUD gaps (firewall update, port profile create/update/delete, port forward CRUD), high-frequency client and port ops (block client, set port state, restart and locate device, static DHCP leases), observability (site health, WAN status, events, alarms, speed tests, top talkers), and four composite tools that collapse multi-step UI workflows into single calls with rollback on partial failure: create_iot_network, create_guest_network, provision_homelab_service, audit_open_ports.

Hardened container: UID 1000, no shell, read-only rootfs, digest-pinned base, hash-pinned wheels. Multi-arch with build provenance and SBOM pushed to GHCR. CI gates on Trivy, ruff, mypy strict, and 224 tests at 90% coverage.

Published to the official MCP Registry as io.github.pete-builds/unifi. Auto-publish workflow wired so future tags self-publish. Also pitched to the new curated GitHub MCP Registry at github.com/mcp via the partnership process. That one reviews manually and runs on a longer cadence.

The other UniFi MCP servers in the wild use older auth flows, no tests, deprecated transport. This is the only one with a hardened container and a registry listing.

Stub mode by default until UCG-Fiber arrives. Same surface, mock data. Build the controller before the hardware shows up.

github.com/pete-builds/mcp-unifi ↗

The 53 Report is live. Full tech stack: SQLite, MCP server, Claude Code agentic workflow for the editorial pipeline, Astro 5, Docker on a Hetzner VPS. Here is how it all connects.

The data layer is the SQLite database from post 045. Every draft pick since 1980, weekly rosters since 2002, per-game snap counts since 2012. About 1.3 million rows. A pick counts as a hit if the player produced 500 or more snaps in any single regular season, the line where they spent at least one year as a real rotational contributor (we started at 100 snaps and tightened the bar after publishing the first three articles).

On top of that sits an MCP server running in Docker on nix1 over Tailscale. Eight tools: team draft hit rate, round hit rate, round trends heatmap, roster composition, pick outcome for a single selection, player career arc, player search, and a database health check. The server runs SSE at port 3711 and gets queried by Claude Code during every editorial run.

The editorial pipeline is where it gets interesting. Four stages: Scout, Beat, Editor, Coach. All running inside Claude Code as custom skill agents.

Scout is read-only. It hits the MCP and returns a structured evidence pack with three to five ranked angles. No prose, no opinions, just numbers and angle proposals, ranked by anomaly vs. league, anomaly within team, regime shift signals, single-pick stories, and counter-narratives.

Beat takes the evidence pack plus the approved angle and writes the article. Every number has to be traceable to Scout's pack or a clean derivation from it. No new numbers, no player names Scout didn't surface. Targets 1,800-2,600 words depending on shape, with narrative and data woven together in every section.

Editor is the stat-fidelity gate. It reads Beat's draft against Scout's pack and returns PASS, REVISE, or BLOCK. A hallucinated stat is an automatic BLOCK. No league rank gets through without the raw value, population denominator, and era window in the same sentence.

Coach orchestrates the whole run. It reads the publishing calendar, picks the next queued team, spawns Scout, presents angles, hands the approved one to Beat, runs Editor, and calls the deploy script only after explicit approval. Never ships without that sign-off.

The product is GM Performance Grading: how well NFL general managers draft and retain talent. Three article shapes: scorecard (tenured GM, four graded columns, final letter grade), narrative (paradox or anomaly, no grade), and methodology (league-wide framing, no team focus). Three published pieces so far, twenty-nine teams queued.

The site is Astro 5, static build, deployed via rsync to Zion (Hetzner VPS, Plesk-managed). DNS through Cloudflare, proxied, Full Strict SSL. Build is clean in under ten seconds.

Long-term target is a paper for SSAC 2027 (abstract due around October 2026) and a staff or contributor role at an NFL team analytics group or a shop like SumerSports or The 33rd Team. The dataset edge is the window: Dubow's AP piece used a 2021-2024 window with binary roster data. SIS used first-round picks only with a four-year endpoint. This stack goes multi-year, snap-weighted, and position-weighted across every round.

Next up: interactive analysis with logins and custom date range filters. After that, a longer story-driven piece on the BNM blog, less technical, more about how this came together.

the53report.com ↗

Built a Spotify MCP server this weekend. Most of the ones out there run as a local subprocess on your laptop. This one runs once in a Docker container and any MCP client on the LAN or Tailscale hits it over SSE. Nine tools, one OAuth, no per-machine setup.

github.com/pete-builds/spotify-mcp-sse ↗

Also tonight: spun up a new production server on Hetzner. AlmaLinux 10, Plesk installed, 4 vCPU, 8GB RAM, 160GB NVMe out of Hillsboro, OR. Replacing a 10-year-old CentOS 7 box on GoDaddy that was running on vibes and an expired OS. Going from $86/mo to about $28/mo with better hardware and a supported OS through 2035. Named it Zion.

Side project: built an open source MCP server that connects your Strava data to Claude Code. Ask questions like 'how far did I run this week?' and get formatted stats back. Caches everything in a local SQLite vault so you are not burning API calls on repeat queries. Handles token refresh, bulk sync, and runs as a Docker container. A friend is already forking it and helping improve it. That is what open source is about.

github.com/pete-builds/strava-mcp-vault ↗

Ran a full backup audit today. Found gaps: missing Docker volumes, no retention policy on one backup set, a dead service in the health check. Fixed all three, then wrote a disaster recovery runbook. Six phases, every command copy-pasteable. If the server dies, we rebuild from the NAS in under four hours. Next step: off-site backups to a remote server so a single point of failure does not take out everything.

Started building MCP servers to connect our infrastructure directly to Claude Code. Model Context Protocol turns any API into a tool that the assistant can call natively. Five servers running now: media management, fitness tracking, server monitoring, diagramming, and analytics. Each one is a Python FastMCP container on the same Docker host. SSE transport so they are accessible from any machine on the network.

Shipped Model Arena, a tool for comparing language model outputs side by side. Send the same prompt to multiple models, see the results in real time. Built with Python and FastAPI, streams responses via SSE. Useful for picking the right model for a given task instead of guessing.

Built Phantom Paste, a zero-knowledge pastebin. Written in Go, stores everything in SQLite, encrypts client-side. The server never sees the plaintext. Pastes expire automatically. First app we built from scratch and shipped to production on our own infrastructure.

Deployed n8n as our automation engine. Visual workflow builder, webhook triggers, hundreds of integrations. Connect APIs, move data between systems, schedule tasks. No more writing one-off scripts for every integration. Build it once, let it run.

Added LiteLLM as an API gateway in front of all our language models. One endpoint, multiple providers. Route requests to local Ollama models or external APIs through the same interface. PostgreSQL backend tracks usage and costs. Swap models without changing a single line of application code.

Started running large language models locally with Ollama. No API costs, no data leaving the network. Pull a model, run it, done. Paired it with Open WebUI for a clean chat interface. The same tools the big platforms charge for, running on our own hardware.

Deployed Uptime Kuma to monitor every service. If a container goes down, we know within 60 seconds. Dashboard shows uptime history, response times, and SSL cert expiration. No excuse for a service being down and nobody noticing.

Connected everything with Tailscale. The server, the NAS, the workstation, the phone. Encrypted mesh network, no port forwarding, no exposed services. Access any tool from anywhere like you are sitting on the same LAN. Changed the way we work.

Put Caddy in front of everything as the reverse proxy. Automatic TLS certificates, clean subdomain routing, zero manual cert management. Every internal service gets its own address. One config file controls all the traffic.

Every service gets its own Docker Compose stack. Isolated networks, named volumes, environment files. No package conflicts, no dependency drift. Need to tear something down? One command. Need to rebuild? Same command. 29 containers running and the server barely notices.

Set up a dedicated Ubuntu server as the backbone for everything we build. One box, clean install, SSH access, nothing else. Every service runs in its own container. The server does one job: host Docker and stay out of the way.