Change History & Rollback
New in v3.3.0, substantially expanded in v3.10.0. Every AI-made mutation is recorded to one change ledger, and any recorded change can be undone. This is the “safety spine” that makes the write tools trustworthy: an agent (or you) can review recent changes and roll a bad one back.
Three tools, all manage_options, always-on, enabled by default. Plus a human-facing History admin tab.
emcp-tools/list-changes
Section titled “emcp-tools/list-changes”Lists recent changes newest-first, each with a summary, whether it’s reversible, and whether it’s already been rolled back.
Input: domain, rolled_back, reversible, limit, all optional filters. The domain filter matches any recorded domain string — see the full list below. (Its own JSON-schema enum still only lists elementor/filesystem/database, a plugin-side documentation lag; filtering by the newer domains like media or settings works fine in practice, it’s just not reflected in the schema’s advertised options.)
Returns: { changes: [ { id, ts, user_login, domain, action, target, summary, reversible, rolled_back } ], total }. Heavy payloads (before-images) are stripped from the list. Read them with get-change.
emcp-tools/get-change
Section titled “emcp-tools/get-change”Returns one entry’s full detail by id, including its rollback reference (the before-image / backup pointer).
Input: id.
emcp-tools/rollback-change
Section titled “emcp-tools/rollback-change”Undoes one recorded change by id.
Input: id (required). force — boolean, default false.
If the target has changed since the change was recorded, rollback refuses with a conflict error rather than silently overwriting the newer state — pass force: true to roll back anyway. This conflict check only applies to the domains that stamp a fingerprint at record time (Elementor page data, post-field updates, settings, files, and postmeta/global updates); domain types with nothing to compare against (page/post create or delete, redirects, users, ACF fields, media deletes, database writes) always succeed regardless of force.
On success it marks the entry rolled back (no double-rollback) and records a compensating entry. It only ever reverts changes EMCP itself recorded. If the original change’s before-image was capped (see Database, below), the response includes partial: true and a warning.
The History admin tab
Section titled “The History admin tab”EMCP Tools → History surfaces the same ledger for a human: a table of every change (when · who · type · summary · target) with a one-click Roll back button per reversible entry, plus domain filters. No MCP client needed. You can review and undo AI edits straight from wp-admin.
What’s covered
Section titled “What’s covered”As of v3.10.0, the ledger spans ten domains, almost everything a write tool can touch:
- Elementor — page edits (re-saves the prior
_elementor_data) - Content — page/post create, update, and delete (delete restores via re-insert or untrash, matching how it was removed)
- Settings — WordPress option updates
- Redirects — create/update/delete on Redirect Manager entries
- Media — reversible delete (below)
- Gutenberg blocks — block-tree writes
- Global styles — colors and typography changes
- ACF — custom field updates
- Users — create and field updates
- Filesystem — file writes (restores from backup, or deletes a created file)
- Database — row writes (inverses the write from a before-image: re-update, re-insert, or delete)
The ledger is capped by count and size, so the oldest entries age out (a dropped entry can’t be rolled back; the list shows this). Before-images over roughly 4KB are offloaded to a dedicated table rather than stored inline, so the ledger option itself stays light even with large snapshots in play.
Reversible media delete
Section titled “Reversible media delete”delete-media doesn’t just trash the attachment. Unless the delete already goes to WordPress’s own Trash (MEDIA_TRASH on, no force), it first copies the original file plus every registered sub-size into a plugin-managed trash directory (wp-content/uploads/emcp-originals/trash/), then records a full snapshot of the post record and its meta before deleting. Rolling that entry back re-inserts the attachment (reusing its original ID where possible), restores its meta, and copies the files back. force: true skips WordPress’s Trash and always goes through this snapshot-and-record path; confirm: true is required to delete at all.
If MEDIA_TRASH is on and you don’t pass force, the delete goes to WordPress’s own Trash instead — no ledger snapshot is taken in that case, since Trash/untrash is already the recovery path.
Corrupted Elementor data is preserved, not overwritten
Section titled “Corrupted Elementor data is preserved, not overwritten”Separately from the ledger: if a page’s _elementor_data is a non-empty string that doesn’t decode as valid JSON — i.e. it’s already corrupt — the plugin treats it as an empty page rather than erroring, so an edit built on top of it would normally overwrite the only copy of the original data for good. Before that can happen, the raw corrupt string is preserved to a dedicated postmeta key (_elementor_data_emcp_corrupt) so it stays recoverable even though it was never valid enough to roll back through the normal Elementor rollback path.
Relationship to the Content Mirror
Section titled “Relationship to the Content Mirror”Change history is an in-database ledger of recent changes with instant undo. The Content Mirror is the complementary file-based history: durable, diffable, git-trackable JSON exports for external version control. Use the ledger for quick undo; use the mirror for long-term, VCS-managed history.