tph-deploy – Agency Operations as One Command
A deployment and domain-management platform for a one-person web shop: sixteen commands over a dozen live sites, a backup taken before every deploy by default, and an MCP server exposing the same core so an AI assistant can drive it without a second implementation.
Project Details
Tags
Challenge
Small-business web work lands you on shared cPanel hosting. Not because it is good, but because it is what a client on a $40-a-month plan is paying for, and telling them to move to a platform with real deploy tooling is telling them to pay four times as much for something they will never see.
So the routine was FTP. Drag a folder, hope, click around the live site to see whether it worked. Add a domain and the job spans four control panels — registrar, DNS, cPanel, then a note somewhere about what you just did. Do that across a dozen sites and the failure mode is not dramatic; it is a slow accumulation of sites nobody can quite remember the setup of, and the growing reluctance to touch any of them.
The honest version of the problem: I was the deployment process, and I do not have uptime.
Solution
A Node CLI that owns the whole lifecycle, backed by a JSON registry of every site and a small PHP agent on the host for the things FTP cannot reach.
- Deploy is a pipeline, not an upload. Build if the site needs building, take a backup, upload, write the
.htaccess, verify the result over HTTP, push the commit. Six steps that used to be six separate acts of memory. - Provisioning spans the whole chain.
provisionwalks a new domain from registrar to DNS to host to the registry in one command;deprovisionwalks it back out in reverse. The two exist as a pair specifically so that tearing down cannot leave orphans that bill forever. - SSL is a managed pipeline. Certificate acquisition and install run through the CLI, with a
ssl checkthat reports expiry across every site at once — because the failure mode of certificates is silent until the day it is extremely loud. - WordPress sites get a real management path. A PHP agent on the host exposes plugin, theme, and update state, so the dozen WordPress installs can be audited without logging into a dozen dashboards.
- An MCP server over the same core. The commands were already discrete, named operations with typed arguments — which is precisely what a tool surface is. Exposing them over MCP meant an AI client could deploy, check status, or roll back conversationally, without a second implementation to keep in sync.
Decisions Worth Defending
- Backup is on by default and opt-out by name.
--skip-backupexists, and it is the flag you have to type. Every deploy tool I have watched people lose a site to was one where safety was a thing you remembered rather than a thing you disabled. - Rollback reads from either a backup or a git commit. The two failure modes are different — a bad upload versus a bad change — and a rollback that only handles one of them will be useless on exactly the day it matters.
- Dry run before anything destructive.
--dry-runprints the upload set without touching the host. Cheap to build, and it is what makes deploying to a client's live site on a Friday a decision rather than a gamble. - The registry is a file in the repo. Site configuration is version-controlled JSON, not state living in a database or in my head. It diffs, it reviews, and it comes back with the repo.
- MCP is an interface, not a rewrite. The tools call the same functions the CLI calls. The moment an AI surface gets its own copy of the deploy logic, one of them starts drifting, and it will be the one you use less.
The design insight that transferred: a good CLI is already a good tool surface for a language model. Discrete verbs, explicit arguments, one job each, honest failures. The same properties that make a command memorable to a person make it callable by a model — which is the same lesson the finance-mcp-server build arrived at from the opposite direction.
Outcome
It runs the agency. Every site TPH maintains is deployed, monitored, and recovered through it.
- Static (HTML and 11ty) and WordPress deploys to shared cPanel hosting over FTPS, with pre-deploy backup and HTTP verification
- End-to-end domain provisioning and deprovisioning across registrar, Cloudflare, cPanel, and the registry
- Let's Encrypt certificate provisioning, renewal, and fleet-wide expiry checking
- A stdio MCP server exposing deploy, backup, rollback, status, WordPress, SSL, DNS, and health tools
- A PHP dashboard on the host for status at a glance without a terminal
What surprised me was how much of the value came from status and ssl check rather than from deploying. Deployment was the problem I set out to solve; knowing the current state of twelve sites without visiting twelve URLs turned out to be the thing I use every week. Tooling that tells you what is true is worth more than tooling that does things faster.