How to find what an AI agent did on your WordPress site

The Activity Log tab on a demo site with the filter row in view: User, Connection, Tool, Plugin, From, To and Search fields with Apply and Reset buttons, above a table whose rows show the time, the user, the connection (ChatGPT or Claude with part of the app ID), the tool called, a green success status, the duration in milliseconds and the IP address.

This is a walkthrough of the Activity Log tab in Agent Abilities for MCP, which since 1.9.0 can be filtered by user, connection, tool, plugin, date and text. It covers the admin screen only.

Takeaways:

  • Filter first, then read. A connection plus a date range answers most questions.
  • A denied row says why the call was refused, in the admin log only. The agent never sees the reason.
  • The IP is the address the request came from, which behind a CDN is the CDN. Duration is time inside the tool, and denied calls have none.
  • Export CSV downloads the rows the filters show, not the whole log.

How do I see what an AI agent did on my WordPress site?

Go to the plugin’s Activity Log tab. Each call to one of the plugin’s tools is a row with who made it, which tool, whether it succeeded or was denied, and how long it took. Use the filters to narrow the rows, and Export CSV to keep a copy.

Step 1: start with the summary

The top of the tab summarizes the last 7 days: total calls, success rate, denied, errors and the most-called tools. The screen notes that it counts agent tool calls only, refreshes about once a minute, and shows times in UTC.

A high denied count or an unfamiliar tool at the top of the list is a reason to keep going. A quiet summary is a reason to check that the agent is reaching the site at all, which your agent signs in, then nothing happens covers.

Step 2: filter to the part you care about

Set the filters, then click Apply or press Enter. Typing alone changes nothing, and Reset clears them.

Filter What it matches
User The WordPress user’s login the agent acted as
Connection One connected app, or “App Password or direct” for calls that did not come through an app
Tool A tool name such as aafm/get-posts
Plugin This plugin’s core tools, one integration such as WooCommerce, or other plugins’ tools it bridges
From and To A date range, in UTC
Search Text in the tool name, the note, the user or the app ID

The status tabs (All, Success, Errors, Denied, Started) sit above the filters and combine with them. The Connection list shows the first 50 apps, and the Search box matches an app’s ID for the rest. On a very large log, add a date range, because the text search and user filter have no index.

The log keeps 30 days by default, and the count next to the status tabs says how long yours keeps. Older rows are removed.

Step 3: read a row

Column What it shows
Time (UTC) When the call happened
Principal The WordPress user, with their ID
Connection The app’s name and the start of its ID, or “App Password or direct”
Event The tool called
Detail A short note of what it touched: ids, meta key names, slugs and status values
Status Success, error, denied or started
Duration Time spent inside the tool
IP The address the request came from
Arg keys The names of the arguments sent, never their values

Detail never holds free text. If an agent updates a post, the row names the post, not its body. What the audit log actually stores explains why that line exists.

OWASP’s Logging Cheat Sheet says a useful log records “when, where, who and what” for each event, with the source address as part of who. The Activity Log covers the same ground: the time, the user and connection, the source address, and the tool with its status and note.

Step 4: read the denied rows

Filter the Status tab to Denied. A denied call never ran, so its row has no duration, and its Detail ends with the reason it was refused. The reasons come from a fixed list:

Reason in the log Where to look
key is not on the allowlist The meta key allowlist, when a call tried to read or write a meta key
key is on the deny list The meta key deny list
key is protected or reserved, or key is reserved by WordPress A built-in block on those keys, not a setting
item missing or not editable by this user Whether the item exists and the agent’s user can edit it
ability is not in this role or connection list The per-role and per-connection allowlist in safety controls
rate limit reached The per-minute rate limit on the Settings tab
a required argument was missing The agent’s request, which left out something required
WordPress capability check failed The agent user’s role
the source plugin check refused The permission check of the plugin that owns a bridged tool

The reason shows in the admin log and in the CSV. The message the agent receives is the same generic refusal as before, so a refused agent learns nothing about your rules. Reading a meta key now logs a note the way writing one does, so those attempts leave a trail too.

Step 5: treat the IP and the duration carefully

The IP is the address the request came from. Behind a proxy or CDN, that is the proxy. Cloudflare’s Restoring original visitor IPs page says the origin server normally sees a Cloudflare address, and that the visitor’s appears in the CF-Connecting-IP header. Do not read the IP column as a person’s location.

Duration is the time inside the tool, not what the agent experienced, so it is not how long the agent waited. Use it to spot a tool that is slow on your site.

Step 6: watch it live

Click Live to re-check page 1 every 10 seconds while the browser tab is visible. It follows the filters you applied and stops when you leave page 1, after three failed checks in a row, or after 15 minutes. If you have a text search or a user filter applied, Live needs a date range as well.

Live is useful while you test a new connection or watch an agent you do not yet trust.

Step 7: export what you found

Export CSV downloads the rows your current filters show. The columns are:

created_at, event_type, ability, detail, status, arg_keys, principal_login, principal_user_id, source_ip, client_id, result_count, duration_ms, connection_name

The file carries logins and IP addresses, so store it as you would any record about your users. The two newest columns, duration_ms and connection_name, are at the end, so existing column positions did not move.

What the log does not tell you

The log records calls to this plugin’s tools, including other plugins’ abilities it bridges. It does not record why an agent made a call, and it stores no free-text content. Tools that another plugin publishes straight to the MCP Adapter’s default server are outside it, as the 1.8.0 post explains.

An agent cannot use any of this. The ability that lets an agent read the log is separate from this screen. It did not gain these filters or columns, it has never returned the IP, and since 1.9.0 it leaves out refusal reasons and the rows about notification settings. To have an administrator-level agent read the log for you, switch on Get activity log and use this recipe.

If you would rather be told than look, alerts by email cover the moments worth knowing about. The full list of changes is in the 1.9.0 release post, and the FAQ has the short answers.