Understanding the Universal Audit Log
Every Pro tool call gets recorded — who ran it, what arguments went in, what came back, whether it was reversible, and whether the undo was used. This doc explains what's in each row, how to filter and export the log, how sensitive values get scrubbed before storage, and how retention works.
MCP tools let AI clients run real WordPress operations — publish posts, update products, rewrite widget settings, clone Divi pages. When the person driving the AI later asks "what did the assistant do last Tuesday?", the audit log answers the question specifically instead of you having to reason from memory. It also powers the per-row Undo feature: reversible operations save their reverse-payload alongside the audit row so you can roll them back with one click.
Where to find the Audit Log
In wp-admin, go to Royal MCP Pro → Audit Log. The page is capability-gated to manage_options, so only administrators can view it. Editors and lower-cap roles won't see the menu item.
If you're on a multi-admin site and you want the audit log restricted to specific administrators, gate it further via a custom capability or a plugin like Members / User Role Editor — the underlying page always defers to the current user's cap check.
What gets logged
Every request that hits the Royal MCP endpoint with an MCP tools/call method gets recorded. That includes:
- Free-tier tool calls (e.g.
wp_get_posts,wp_update_post) — yes, the audit log is universal across Free and Pro tools. - Pro-tier tool calls (e.g.
divi_clone_page,wc_bulk_price_update) — these are the ones the log was designed around, and the ones that can also produce an undo token. - Tool calls that were rejected because the license was expired or the caller lacked permission — these are logged with the appropriate status so you can see attempted calls, not just successful ones. See the status values below.
- Tool calls from every MCP client — Claude Desktop, ChatGPT, Cursor, custom SDK calls, direct REST posts. The client's name and version are captured when the request includes them.
Audit capture runs before the license gate. That means when a Pro tool call arrives on an expired license, you see a row with the tool name, the caller, and a gated status — the call is visible in your log even though it didn't execute. This is intentional: it gives you a truthful record of what the AI tried to do, not just what it managed to do.
Anatomy of an audit row
Each row stores 18 pieces of information. The list view surfaces the most useful ones as columns; the rest are visible when you expand a row or in the CSV export. Grouped by purpose:
Identity — who called what and when
id— unique row identifiercreated_at— UTC timestamp of the requesttool_name— e.g.divi_clone_page,wp_update_postuser_id— the WordPress user the request was authenticated asauth_method— how the caller authenticated (OAuth, application password, API key, etc.)session_id— conversation / session identifier when the client sends one; groups related calls togetherclient_meta_json— the MCP client name + version if the client identified itself in the requestroute— the REST route the call came in on
Input — what was requested
args_hash— SHA256 of the request arguments; useful for detecting exact duplicates without inspecting contentargs_json— the full arguments as JSON. Sensitive keys are scrubbed before storage (see Value scrubbing)
Outcome — what happened
response_status— one of the enum values documented in the next sectionresponse_summary— the first 250 characters of the response text, value-scrubbedaffected_object_type— e.g.post,product,widgetwhen the tool acted on one specific WordPress objectaffected_object_id— that object's ID
Reversibility — can this be undone?
undo_token— 64-character hex token if the tool issued one; NULL if the operation isn't reversibleundo_expires_at— when the token stops being redeemableundone_at— when the undo was actually executed (NULL if the operation still hasn't been undone)
For the full mechanics of undo tokens (which tools issue them, TTL rules, how to redeem), see Undo Token Errors.
Housekeeping
user_deleted_at— timestamp populated when the row was tombstoned as part of a GDPR user-erasure request
The response_status values
Every row lands in one of six status buckets. These are the values you'll see in the Status column and the values available in the Status filter dropdown.
| Status | Meaning |
|---|---|
| success | The tool executed and returned a normal response. The response summary shows a truncated snippet of that response. |
| error | The tool ran but returned an error (invalid arguments, WordPress object not found, downstream API failed, etc.). The response summary usually includes the error message. |
| gated | The call was blocked by the license gate — either a Pro tool called on an expired license, or a tool the caller wasn't authorized to run. The row records that the attempt happened; the tool did not execute. |
| undone | The tool executed successfully at the time and issued an undo token; someone later redeemed that token and reversed the operation. The undone_at column shows when the reversal happened. |
| unknown | Rare. The audit recorder couldn't classify the response with certainty (usually indicates a tool returned an unexpected response shape). Worth a support ticket if you see many of these. |
Filtering the audit log
The filter panel above the row list narrows results by any combination of these fields:
| Filter | Notes |
|---|---|
| Tool name | Text field. Matches on exact tool slug (e.g. wp_get_posts). Useful when you're investigating a specific tool's history. |
| User ID | Number field. Filter to calls made under a specific WordPress user. Set to your own user ID to see only what you (or an MCP client acting as you) did. |
| Status | Dropdown with values: any / success / error / gated / undone / unknown. See the table above for what each status means. |
| Undo state | Dropdown with four options:
|
| Date range (From / To) | Date pickers. Row timestamps are stored in UTC, and the date you type is compared as-is against those UTC timestamps — so if you're in a non-UTC timezone, a late-night event in your local time may fall on a different UTC date than you expect. Leave either field blank for open-ended. |
| Search | Free-text search. Matches against session ID, tool name, affected object ID (numeric), the compressed args body, and the response summary. Useful for tracing everything in one MCP session or every call that touched a specific post ID. |
Quick-range presets
Under the filter form, four preset buttons snap the date range to common windows: Last 24h, Last 7 days, Last 30 days, and Clear dates. Use them as the fast path to "what happened today" or "what happened this week" without touching the date pickers.
The per-row Undo action
Rows that produced an undo token show an Undo link in the Actions column — but only while the token is still redeemable. The link shows when all three conditions hold:
- The row has an
undo_tokenstored - The token hasn't been consumed yet (
undone_atis empty) - The token hasn't expired (current time is before
undo_expires_at)
Once you're one condition short, the Undo link disappears from the Actions column. Rows that already got undone show status undone; rows whose window elapsed remain in their original status but no longer offer the action.
Clicking Undo
Clicking the link shows a browser confirmation prompt (Undo this operation? This uses the stored undo token and cannot be re-run.). Confirm, and the plugin redeems the stored undo payload against the same tool that created it. The Audit Log page then shows one of three notices:
- Operation undone. — success. The row's status becomes
undoneand its Undo link goes away. - Undo failed. See details in the audit row. — the undo attempt ran into an error (usually because the target object has been modified further since the reversible payload was recorded). Read the row's response summary for details.
- This row has no active undo token. — the token was consumed or expired between the moment the link was rendered and the moment you clicked. Rare, but possible if two administrators are looking at the same page.
Which tools issue undo tokens, what the per-tool TTLs are, and how to reason about "still safe to undo" windows — those live in Undo Token Errors for now (which covers the concept alongside the troubleshoot). A dedicated concept doc is on the way.
CSV export
The Export CSV (filtered rows) button on the Audit Log page downloads a spreadsheet of the currently-filtered rows. Details:
- Filename:
royal-mcp-pro-audit-YYYYMMDD-HHMMSS.csvusing your server's local time. - Row cap: up to 5,000 rows per export. If your filter matches more than that, narrow it (tighter date range or add a tool-name filter) and export in slices.
- Content: every column in the underlying table —
id,created_at,tool_name,user_id,auth_method,response_status,session_id,route,affected_object_type,affected_object_id,undo_token,undo_expires_at,undone_at,response_summary,client_meta_json,args_hash, andargs_json. - Filters carry over: whatever tool / user / status / date filters are active when you click Export are applied to the row set.
- Formula-injection defense: any cell value that starts with
=,+,-, or@gets a leading single-quote so spreadsheet apps treat it as text and don't try to execute a formula. Safe to open the export in Excel, Numbers, or Google Sheets.
If you want a monthly record of "everything the AI did on this site," set the date range to your report window, apply any tool filters you care about, and export. The scrubbed args + response summary + affected object columns give you enough context to review each call without re-running it.
Value scrubbing (before storage)
Both request arguments and response summaries pass through a scrubber before being written to the audit table. This protects credentials and PII from ending up in a long-lived log even if the tool happened to receive or return them.
Arguments (args_json)
The args scrubber looks for keys whose name matches a sensitive pattern and replaces the value with a redaction marker. Sensitive key patterns include: password, passwd, token, secret, api_key, apikey, access_key, authorization, bearer, session, cookie, signature, private_key, passphrase, mnemonic, seed_phrase, email, phone, ssn, credit_card, cvv, pin, address, and case-insensitive variants of each (dashes and hyphens equivalent to underscores).
Redacted scalar values become [redacted:sha256:<16-char-hash>]. Redacted arrays become [redacted:array:<count>]. You can still see that a field was present (and how big it was) without seeing the contents.
Response summary (response_summary)
Response text is truncated to 250 characters, then passed through four regex patterns that redact anything looking like:
- JWT tokens (three base64url segments separated by dots)
- Auth-preamble + long value (e.g.
bearer abc123...,token xyz...) — matchesbearer,token,api-key,apikey,secret,password,authorization,authfollowed by a 16+ character value - Email addresses
- Long hex strings (24+ hex characters — catches session IDs, API keys, hashes)
The scrubber is designed for common shapes of secrets and PII. If your workflow passes a novel credential format through tool arguments, it may not match the pattern list. Treat the audit log as a working operational record with reasonable defenses — not a certified PII-safe archive. If you're subject to strict compliance requirements, restrict who has manage_options access and shorten the retention window.
Retention & automatic pruning
Rows are pruned automatically after they age out. The default retention is 90 days.
- Filter to change it:
royal_mcp_pro_audit_retention_days— return an integer number of days. Any row whosecreated_atis older than that at the moment the daily prune runs gets deleted. - Never-prune mode: return
0(or any value ≤ 0) from the filter and the daily prune bails without deleting anything. Rows accumulate indefinitely — be sure you actually want that before choosing this option, especially on high-traffic sites. - When the prune runs: once a day via WP-Cron, on the
royal_mcp_pro_audit_pruneevent. If WP-Cron isn't firing on your host (a not-uncommon shared-host issue), old rows won't be deleted until it recovers.
Drop this into a site-specific plugin or a mu-plugins file to keep rows for one year:
add_filter( 'royal_mcp_pro_audit_retention_days', function() {
return 365;
} );The royal_mcp_pro_diagnostics tool
Royal MCP Pro ships one MCP tool designed to be callable even when the license is expired: royal_mcp_pro_diagnostics. It exists so that when something goes wrong — expired license, missing schema, disagreement between what the plugin thinks it has and what's actually registered — you (or your AI) can inspect the state without needing an active license first.
Practically: on an expired license, every other Pro tool returns the "did NOT execute" service message, but this one returns a diagnostic payload. Point Claude at it and ask "run the diagnostics tool" to get a snapshot.
What it returns
The response is a structured payload with these top-level keys:
pro_version,free_version,wp_version,php_version,site_urllicense— nested block withlicensed,sdk_status,sdk_expires_at,sdk_last_check, andsdk_errorpro_tool_countandpro_tool_slugs— every Pro tool currently registeredpro_schema_countandpro_schema_slugs— every Pro tool with a valid schemahandlers_without_schema,schemas_without_handler— drift-detection lists (both should be empty on a healthy install)active_profile— which endpoint profile is currently in effectendpoint_urls— the base MCP endpoint URL + any legacy aliasesbuilders— whether Elementor / Divi / Elementor Pro are active + their versionsseo_plugin_detected,redirection_active,sitevault_active,forgecache_active— sibling-plugin integration signals
Two optional argument flags extend the payload: include_plugins=true adds a list of every active plugin's basename/name/version, and include_audit_tail=true adds the last five Pro tool calls (id, timestamp, tool name, user ID, response status, response summary snippet). Handy for reporting an issue: run diagnostics with both flags on and you have a complete environmental snapshot to attach to a support ticket.
The diagnostics tool is capability-gated to manage_options like the rest of the Pro admin surface. It bypasses the license gate, not the WordPress permissions system.
Common questions
Does the audit log affect performance?
Insertion happens on the same request that made the tool call, so there is a real (small) write cost per call. The write is a single indexed INSERT — well under 10ms on typical setups. Rows are pruned once a day, so the table stays bounded by your retention window.
Can I disable the audit log?
Not from the settings UI — the log is on by design. If you have a specific reason to disable audit capture (extreme write-throughput environment, e.g.), reach out at support@royalplugins.com and describe your workload. The plugin has filter hooks for advanced use cases; we can point you at the right one for your situation.
Do free-tier tool calls really get logged?
Yes. The audit log is a Pro feature, but once you're on Pro every tool call — Free or Pro — is captured. This is what makes it "universal": one place to see everything the AI did, not two separate logs.
Does the log survive plugin deactivation?
Yes. Deactivating the plugin leaves the wp_royal_mcp_pro_audit table in place. Uninstalling the plugin (deleting it entirely) drops the table — if you want to keep the data, export via CSV first.
Do rows include IP addresses?
No. The audit row doesn't record the caller's IP address. It records the WordPress user ID and auth method, which are more useful for accountability inside your team. If IP-level tracking is something you specifically need for compliance, reach out.
What about GDPR user-erasure requests?
The user_deleted_at column is populated when a WordPress user erasure request is processed. Rows for that user are marked but not physically deleted — the user identifier is scrubbed, and the row stays for operational continuity.
Still Stuck? Two-Step Support Path
If the Audit Log isn't showing what you expect — missing rows, wrong statuses, undo actions failing — work through these two steps in order.
Step 1: Confirm the log is capturing new rows
The fastest sanity check:
- Note the top row's timestamp on the Audit Log page.
- From your MCP client, run any simple tool (e.g.
royal_mcp_connection_health). - Reload the Audit Log page. A new row for that tool call should be at the top.
If the new row appears: the log is healthy. Your issue is likely a filter that's excluding what you want to see — clear all filters and try again. If the new row does not appear: the audit capture path may be blocked. Move to Step 2.
Step 2: Email priority support
Email support@royalplugins.com from your purchase email address. Priority email support is included with every Pro license — typical response within 24 hours. Never include your license key in email; we look it up from your purchase address.
- Output of the
royal_mcp_pro_diagnosticstool (withinclude_audit_tail=trueif you can) — this gives us the environmental snapshot we'd otherwise have to ask for - Royal MCP Pro version from WP Admin → Plugins
- What you expected to see on the Audit Log page (e.g., "a row for the divi_clone_page call I ran 10 minutes ago") and what you actually see
- Which filters are active, or "no filters" if you've cleared them
- Screenshot of the Audit Log page in the state that surfaced the issue
- Active caching / security plugins (LiteSpeed Cache, WP Rocket, Wordfence, Solid Security, none)