What 1.7.6 changes: an OAuth token now works on the MCP route only

If OAuth is turned on for your site, update to 1.7.6 or later (it shipped on 28 September 2026). Before 1.7.6, an OAuth token issued for the MCP endpoint also signed its holder in on other REST routes, admin-ajax.php, admin-post.php, the comment form, the OAuth authorize screen and the discovery URLs. From 1.7.6 it works on the MCP route only.

That is the main change in this release, and we would rather say plainly what the old behaviour was than leave it as one line in the changelog. The rest of 1.7.6 is about the plugin telling you what actually happened: whether a write landed, and what your safety switches really say.

Do you need to act?

  • If OAuth is off, no OAuth token resolves to a user, so there is nothing to fix. Update with your next round of plugin updates.
  • If OAuth is on, update now. The check runs on every request, so tokens issued before the update are covered too, and you do not need to reconnect your agents.
  • If you connect with an Application Password, this change does not apply to it. It is a different credential with a different reach, which OAuth or an Application Password compares.

If you want a clean start anyway, revoke the grant on the Connection tab and approve the connection again. It is optional.

What the old behaviour was

An OAuth token is meant to be a key to one door, the plugin’s MCP route. Before 1.7.6 the plugin decided whether a request was for that door by reading the request’s URL, before WordPress had finished deciding where the request was going. On the places the changelog names, a request carrying a valid token was accepted as the user who had approved the connection.

That mattered because the token takes that user’s capabilities. A route that trusts WordPress’s idea of the current user would treat the token holder as that person. And those routes are outside the MCP endpoint, which is where the allowlist, the high-risk lock and the Activity Log do their work.

We cannot tell you from the plugin whether a token was ever used that way on your site. The Activity Log records MCP traffic, and these were requests to other routes.

What 1.7.6 does instead

The plugin now resolves a token to a user only after WordPress has parsed the request and started REST routing, and only when the route WordPress settled on is the MCP route. The entry points that never do REST routing never match: wp-admin, admin-ajax.php, admin-post.php, wp-comments-post.php, cron and WP-CLI. Neither do requests that WordPress answers while it is still parsing, such as the discovery documents.

Where an OAuth token worked before and after 1.7.6 Before 1.7.6 the plugin decided from the request URL whether a request was for the MCP route, and a token was also accepted on other REST routes, admin-ajax.php, admin-post.php, the comment form, the authorize screen and the discovery URLs. From 1.7.6 the plugin waits until WordPress has routed the request. A token signs its holder in as the approving user only when the route WordPress chose is the MCP route, and everywhere else the token resolves no user. A REQUEST ARRIVES CARRYING AN OAUTH TOKEN The plugin has to decide whether the request is for the MCP route BEFORE 1.7.6 Decided from the URL, before WordPress had routed the request The token was also accepted on: Other REST routes admin-ajax.php and admin-post.php The comment form The authorize screen The discovery URLs FROM 1.7.6 Waits until WordPress has routed the request, then checks the route it chose MCP route only Signed in as the approving user Everywhere else The token resolves no user

There is a second guard on the MCP path itself. If another plugin has registered its own handler on that path, a handler other than the MCP adapter’s gets a 401 before its own callback runs.

This is not airtight against every plugin. The code names its limits, among them: a plugin that defines REST_REQUEST itself on a request WordPress did not serve as REST, and one that builds the REST server inside an early parse_request callback on a non-REST request. WordPress core’s own Application Passwords share the first limit.

The wider picture of what an agent can reach, and what each credential can and cannot bound, is on security and in AI agents on WordPress: risks and controls.

Writes now say what happened

Version 1.7.5 began confirming post, block and template writes against what actually landed. In 1.7.6 the same check covers meta, SEO, media, WooCommerce and The Events Calendar writes. After a write, the plugin reads the database and reports an outcome such as:

Outcome What it means
written The value is now stored
unchanged The value was already there
refused The plugin would not do it
failed read The plugin could not read the database to confirm

Deletes report deleted or absent.

A concrete refusal: the meta key column in a stock WordPress database ignores case, accents and trailing spaces. Asked to update foo, core’s own update would quietly act on a row stored as Foo. The plugin now counts only rows stored under the exact spelling, and refuses the request when another spelling matches.

A successful meta write now returns a status field, and where the plugin has them, a row count and the previous value (when that was plain text or a number). Each outcome also gets a row in the Activity Log with identifiers only: the kind of write, the object, its ID, the key name, the status and the row count.

One thing to expect: a write that went through before can now come back refused, for example when the key’s spelling matches a row stored under a different spelling.

Your switches are read from the database

Read-only mode, the high-risk lock, the ability switches and the allowlist are now read from the database on every MCP request over HTTP. The policy options are fetched together in a single query. If the cached copy and the database row disagree, the stricter answer wins, and if the row cannot be read, the stricter setting holds.

For read-only mode that means it reads as on when either the cache or the row says on. For the enabled-ability list it means an ability counts as enabled only when both agree.

This is the read side of a problem 1.7.3 fixed on the write side: on a site with Redis, Memcached or another persistent object cache, a stale cached value could outvote the setting you had just changed. A stale cache can no longer loosen a control.

Smaller fixes

  • When the Activity Log row for a denied or failed call carries a description, it now begins “Attempted: …”, so a refused write no longer looks like one that happened.
  • WP-CLI printed a pair of “called incorrectly” notices for each enabled bridge tool whose source plugin was inactive. Those tools are now skipped quietly.
  • Under a persistent object cache, the review prompt cleared the whole list of cached “this option doesn’t exist” entries, a list every plugin shares. It now clears only its own.
  • Smaller fixes landed in replace-sitewide, WooCommerce gateway ordering, the Rank Math and AIOSEO head previews, and several tool descriptions.

What did not change

The catalog is still 179 abilities, 85 in core and 94 across the integrations. What an agent can do is still decided by what you switched on, the role and connection allowlists, and the capabilities of the account behind the connection. For the model behind that, see off by default, and for what the log keeps, what the Activity Log actually stores.

Two releases followed this one: 1.7.7 gave the Abilities tab one Save button, and 1.7.8 made bridged tools from other plugins show up in the tool list. The full history is on the changelog.