Let Codex run unattended. Keep WordPress writes supervised.
WP MCP Server holds every WordPress write for approval, even when nobody's watching the Codex session that proposed it.
Self-hosted core, works unattended. WP MCP Cloud never holds your SSH keys or site credentials.
A supervised session can catch a bad call. An unattended one can't.
Codex is built to run tasks with minimal hand-holding, sometimes unattended, sometimes on several sites at once. Nothing about that changes if one of those tasks quietly changes a live option.
Three steps, and the exact walkthrough lives in the docs.
docker compose up -d) or pip install -e .. It comes up on 127.0.0.1:8642, bound to loopback — nothing outside your machine can reach it.site_register at a site over SSH+WP-CLI, or install the companion plugin for sites where you only have plugin access.mcp-remote bridge in ~/.codex/config.toml with a scoped bearer token.[mcp_servers.wpguard]
command = "npx"
args = [
"-y", "mcp-remote", "http://127.0.0.1:8642/mcp",
"--header", "Authorization: Bearer <TOKEN>",
]
Unattended or not, only one path reaches production.
Reads run with nobody watching
Codex can pull core version, active plugins/theme, options, and post meta on its own schedule. No packet needed, whether or not anyone's watching the run.
Every write waits for a human
Option updates, post-meta edits, and content search/replace preview first with apply=False. An unattended run can propose a change — it still can't ship one alone.
Locked behind SSH and admin scope
Raw PHP execution stays a deliberately hard-to-reach, admin-only, SSH-only tier. It's never a tool an autonomous run picks up by default.
Codex can run the whole task alone. It still can't approve its own write.
Kick off Codex on a maintenance task and walk away — that's the point of running it unattended. A separate approval still has to land before any write reaches the live site.
packet_open(summary="Update tagline for spring promo",
risk="low", verb="wp_mutate_option", …)
→ status: proposed
packet_approve(packet_id="a1b2c3", approver="connor")
→ status: approved
// the proposer and the approver can be different people
Even a stale write gets refused, automatically.
If someone else changes the live value after Codex previewed a change, the write fails at execution. It never silently overwrites what changed underneath it. Pass the dry-run's etag back as expected_etag, and a stale write fails closed automatically.
The same digest binding refuses execution if a packet's arguments were tampered with after approval. An approval that no longer matches what's about to run simply doesn't count.
Before you connect Codex, you'll probably ask
-
"Nobody's watching when Codex runs a maintenance task overnight."
That's exactly when the gate matters most. It lives on the WP MCP server, separate from Codex's own session. An unattended run still can't write without an approved packet.
-
"Codex often runs several tasks across several sites at once."
Every one of those tasks hits the same gate. Each write needs its own approved packet, scoped to that exact site and change.
-
"I don't want a vendor holding my clients' SSH keys either."
wpguard-mcp runs on your own infrastructure. WP MCP Cloud never touches customer sites or holds SSH keys or credentials.
Also using Claude or Cursor?
Same guard, same packets, same evidence trail — whichever MCP client your team standardizes on.