WP Manifestindependent plugin directory
manifest / ai / ai-godmode

AI Godmode

WordPress plugin that gives an AI agent server-administrator access, plus a Tool Drawer that cuts MCP token use. Everything ships switched off.

by John Pozadzides · github.com/johnpoz/ai-godmode · website

★ 0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/johnpoz/ai-godmode/archive/refs/heads/main.zip

A self-contained WordPress plugin that registers server-administrator actions as native WordPress Abilities (core since 6.9), so an AI agent can discover and run them over MCP through a bridge. Every ability ships switched off. The name is the safety argument.

Personal project by John Pozadzides. Not affiliated with Texas Metal Works. Free, GPLv2 or later.

Status

v0.8.3. 65 abilities across thirteen families (25 read, 40 write), plus a SiteGround cache purge on SiteGround hosting, ten more when Code Snippets is active and two more when Easy MCP AI is active, every one default-off, capability-checked, and audit-gated for mutations. The off-site audit log is built, deployed and proven live, and is now isolated per site: one bucket, one ingest key and one viewer key each, so connecting a new site cannot disturb a site already recording. The admin interface is tabbed, with a Help tab and a two-part alarm.

Family Abilities
options read-option, write-option, list-options, delete-option, transient-get, transient-set, transient-delete
database db-list-tables, db-describe-table, db-query-read, db-query-write, db-export
filesystem fs-list, fs-read, fs-search, fs-write, fs-patch, fs-mkdir, fs-copy, fs-move, fs-delete, fs-chmod, fs-zip, fs-unzip
plugins plugin-list, plugin-install, plugin-activate, plugin-deactivate, plugin-update, plugin-delete
themes theme-list, theme-install, theme-switch, theme-update, theme-delete
users user-list, user-get, user-create, user-update, user-delete, role-list, role-create, role-delete, role-add-cap, role-remove-cap, app-password-list, app-password-create, app-password-delete
cron cron-list, cron-run, cron-schedule, cron-unschedule
diagnostics get-environment, site-health, php-info, constants, hooks-inspect, error-log-tail
run-php run-php
core get-reference
site http-fetch, cache-purge, permalinks-flush
drawer find, call, wp-find, wp-call (the last two present only when Easy MCP AI is active)
siteground siteground-purge-cache (present only when Speed Optimizer is active)
code-snippets snippets-list, snippets-get, snippets-create, snippets-update, snippets-replace, snippets-activate, snippets-deactivate, snippets-trash, snippets-run, snippets-revert (present only when Code Snippets is active and compatible)

25 read, 41 change something. In the interface they are labelled READ and WRITE; "mutation" was removed as a user-facing word because it meant nothing to anyone who had not read the source.

A WRITE badge turns into WARNING only while the off-site log has stopped recording, which is exactly when that ability is being refused. The badge is a live readout of what will happen if the ability is called, not a permanent label on the category. A label that is always on gets ignored.

The Tool Drawer

Every tool definition an MCP client holds is sent back to the model with every request. On onlyreason.org that was 243 tools and 70,314 tokens per request (2026-10-07, counted with the Anthropic token-count API), of which this plugin's 70 abilities were 11,403. So since 0.7.0 the connector lists a small core set and the rest sit in a drawer: still registered, switched and audited exactly as before, reachable through godmode/find (search, returns the input schema) and godmode/call (run by name through every gate the ability has), and for Easy MCP AI's own tools through godmode/wp-find and godmode/wp-call, which run them through Easy MCP's complete call pipeline. The find descriptions carry the names of everything currently in the drawer, grouped by family, so the model knows what it can ask for.

No MCP server plugin is patched. They all answer through the WordPress REST layer, and includes/class-surface.php shortens any JSON-RPC tools/list reply through core's rest_post_dispatch filter, whichever server sent it (Easy MCP AI, the WordPress MCP Adapter, or the next one). Their call paths never consult the listing. The always-loaded set is operator-configurable on the Tool Drawer tab, one expandable section per family or plugin with a master checkbox; the switch ships on because it removes exposure rather than granting power. Other plugins' ability tools stay listed unless "Put other plugins' abilities in the drawer too" is on; a tick on an individual tool wins either way. What the operator gives up is the client-side per-tool prompt for drawer tools, since to the client the drawer is one tool; the gating moves entirely server-side.

How The Switches Work

An ability runs only when the master switch is armed AND its own switch is on. Both default off. The master switch sits above the tab bar under the heading "Arm / Disarm Master Switch", deliberately the loudest element on the screen, because the predictable failure is somebody switching on every ability and never realising one switch above them all is still off. Exposure flags (meta.public, meta.show_in_rest) follow the effective state, so a switched-off ability is neither listed nor executable. Exposure is never the security boundary: every call runs a manage_options capability check regardless.

A newly registered ability is always absent from the enabled map, which reads as false. New power is never granted quietly by an update.

Guardrails carried by the framework: filesystem access is confined to the WordPress root and refuses path escapes, protected directories, and writes to wp-config.php, the root .htaccess, .env files, .git trees, symlinks and the plugin's own files; database reads are SELECT-only and row-capped by SQL; secrets (salts, keys, passwords, user_pass, secret-looking keys inside option values) are scrubbed from files, query rows and constants, and refused from options, including every godmode_ option of the plugin's own; the plugin refuses to deactivate or delete itself, to flip its switches through SQL, to strip manage_options from the administrator role, or to delete the last administrator or the current user. run-php is total control and is labelled as such: it is bound by none of the above, because it is code rather than an ability with rules.

Audit

Every execution writes an intent record before the callback runs (wp_before_execute_ability) and a completion record afterwards. Records carry a per-site sequence number and a SHA-256 hash chain. Refusals are recorded as failures rather than as silence.

Mutations refuse to run when a configured sink cannot record the intent, unless the caller passes acknowledge_unlogged: true; the override is itself logged as a gap marker. Reads go to the local ring buffer only; mutations also go to every configured remote sink.

The off-site log

A local log is worthless against the thing it exists to record: this plugin can delete anything on the server, run-php included, so anything it writes it can also erase. The record has to leave the machine before each action runs.

worker/audit-sink.js is an append-only Cloudflare Worker in front of an R2 bucket, deployed onto the operator's own account. Two layers keep records there:

  • The Worker is the only door. Routes are health, ping, append, query, view. There is no delete route in the source and no call to any R2 delete or overwrite method, so no key reaches one. An ingest key reaches only append and lives on the site; a viewer key reaches only query and never touches the site.
  • R2 enforces it underneath. Every append is a conditional write (onlyIf: { etagDoesNotMatch: "*" }) on a key derived from the sequence number, so a sequence number cannot be reused. A bucket lock blocks deletion below the Worker entirely.

Provisioning takes one pasted Cloudflare token, builds all of the above, mints both keys, then discards the token permanently. It is never written to the database: a token that can create Workers, sitting in an options row run-php can read, would turn a compromise of the site into a compromise of the whole Cloudflare account.

The honest limit: this is tamper evidence, not tamper proofing. The account owner can remove the lock and delete. What is eliminated is the AI doing it. See worker/README.md.

The alarm

When the off-site log stops recording, two things happen. A banner appears on every admin screen, dismissible for twelve hours and returning immediately if the failure changes. And a red mark appears on the Settings and AI Godmode menu items, which cannot be dismissed and stays for as long as the problem does.

The status check calls ping, which validates the ingest key. It deliberately does not use health, which takes no key and always answers: an earlier version did, and consequently reported the log as Live while every mutation was in fact being refused.

Verification

Everything here runs outside WordPress.

php tools/check-schemas.php > /tmp/schemas.json    # definitions, size budget
python3 tools/check-schemas.py < /tmp/schemas.json # JSON Schema Draft 2020-12
php tools/check-viewer-chain.php                   # hash chain and tamper cases
php tools/check-autoload.php                       # class to filename resolution
php tools/render-admin.php                         # every admin tab, every state
php tools/check-code-snippets.php                  # Code Snippets family and guard
php tools/check-hardening.php                      # 0.6.2/0.6.3 security rules, executed
php tools/check-surface.php                        # tool drawer: listing filter, index, four abilities
node worker/test/check-php-json-encoding.mjs       # JS encoder vs real PHP

Inside WordPress, as an administrator at init or later (the Abilities API refuses to initialize earlier). tools/verify-live-v0.2.0.php exposes godmode_v2_verify(), which exercises every family by real execution including negative and guardrail tests, restoring state afterwards:

require 'tools/verify-live-v0.2.0.php';
echo wp_json_encode( godmode_v2_verify() );

Against a live endpoint:

AUDIT_BASE=https://<worker>.workers.dev \
AUDIT_INGEST=... AUDIT_VIEWER=... ./worker/test/live-matrix.sh

Current results: 78 definitions within budget, 156 schemas valid, 13 of 13 chain checks, 102 classes resolvable, 60 of 60 tool drawer checks, every admin tab rendering in all three states, 8 of 8 encoder cases byte-exact, 33 of 33 live Worker checks.

Layout

ai-godmode.php                      plugin header, constants, autoloader, boot
uninstall.php                        option cleanup
includes/class-plugin.php            bootstrap, heartbeat schedule
includes/class-settings.php          switch and configuration storage
includes/class-registrar.php         wp_register_ability wrapper with all gates
includes/class-audit.php             audit manager, hash chain, sink fan-out
includes/interface-audit-sink.php
includes/class-ring-buffer-sink.php  local record
includes/class-cloudflare-sink.php   off-site record
includes/class-cloudflare-provisioner.php  one-token setup and key rotation
includes/class-audit-viewer.php      authoritative chain verification
includes/class-health.php            is the off-site log actually recording
includes/class-notices.php           the banner and the admin menu mark
includes/class-redactor.php          denylist and payload redaction
includes/class-admin.php             tabbed settings screen
includes/class-admin-ui.php          design system, tabs, badges, help tips
includes/class-sink-admin.php        Off-site Log and Maintenance tabs
includes/class-help-admin.php        Help tab, fifteen sections
includes/class-mcp-bridge.php        optional adapter registration, default off
includes/class-surface.php           the tool drawer: listing filter and drawer index
includes/abilities/                  one class per ability
worker/audit-sink.js                 the append-only endpoint, shipped in the zip
worker/test/                         encoder differential and live matrix
tools/                               schema, chain, autoload and render harnesses
docs/kb/                             copies of the knowledge base pages

Project rules

  • Verify by execution, never inspection alone. Report pass or fail per step.
  • Everything ships default-off. The create-disabled invariant never changes.
  • John deploys, never Claude.
  • No em or en dashes anywhere, including in shipped files and commit messages.
  • Definitions stay under about 2 KB; prose belongs in godmode/get-reference and the Help tab, not in schemas.
  • Push to Gitea in the same session the code is written; GitHub is mirrored from it.

Knowledge Base

Design, decisions and run results live in the personal KB (kb.johnp.me), shelf "Web Development Projects", book "AI Godmode". Copies are in docs/kb/. Start with cowork-handoff-start-here.md, then off-site-audit-log-build-and-verification.md and v0.3.x-status-light-and-interface-rebuild.md.