Agent Abilities for MCP is a plugin about WordPress MCP security, so when we find a gap in our own code, the honest thing is to fix it and say so out loud. Version 1.4.3 closes the most serious one we have found since launch. Any user who could upload a file, even a plain author with no special role, could read every file in the media library, not just their own. That is fixed now. Below is what the bug actually was, how the fix works, and everything else that shipped alongside it.
What media reads were supposed to do, and what they actually did
The plugin’s whole pitch is that an agent only ever acts as the WordPress user it connected as, scoped to what that user can do. Media reads broke that promise. get-media, get-media-item, and count-media checked whether the calling user could upload files at all, and once that check passed, handed back the entire library: every image, every document, every attachment on the site, uploaded by anyone. An author connected as themselves, with no elevated role, could list and read files another author or an editor had uploaded and never intended to share. The permission check was doing its job. It just was not scoping the result to match.
How the fix scopes results to the caller
The three abilities now filter by the calling user’s own uploads, the same way the WordPress admin’s own media library does for a role that cannot see other people’s content. If your capability stops at your own posts, your media reads stop at your own uploads too. The one exception is edit_others_posts, the same capability WordPress already uses to decide whether a role can touch content it did not create. A user who holds it, typically an editor or administrator, still sees the full library, because that is exactly the boundary WordPress itself draws elsewhere. We did not invent a new permission model here. We made media reads follow the one WordPress already has. You can see the ability’s scope on its own page, get-media, and the wider approach to access control on our security page.
Why the media count had to change too
count-media reports how many items are in the library, and a count that disagreed with the list sitting next to it would have been its own kind of confusing, technically accurate about the whole site but wrong about what the caller could actually see. So the count now applies the same scoping as the list: an author gets a count of their own uploads, an editor gets the site total. The number and the list it describes now always agree.
WooCommerce product delete now checks the specific product
Deleting a WooCommerce product only checked the blanket manage_woocommerce capability, not whether that particular product was one the caller should be allowed to delete. It now checks the product being deleted, the same way the rest of the plugin’s write abilities check the specific item rather than a capability that covers the whole store. You can see the full set of WooCommerce abilities and how they are gated on the WooCommerce integration page.
Cleaner errors for duplicate SKUs and coupon codes
Trying to save a product with a SKU that already exists, or a coupon with a code already in use, used to come back as an uncaught error with nothing useful in it. Both now return a plain message naming what the value collided with, so an agent, and the person reading its output, knows exactly what went wrong instead of getting a stack trace shaped like a failure.
Two OAuth conformance fixes
Two smaller OAuth bugs are fixed in this release as well. The pointer WordPress sends back on a 401 to tell a client where to start the OAuth discovery flow was being compared to the request path case-sensitively, so a request that differed only in casing got no pointer at all and the client had nowhere to go. And the authorization response was missing the issuer field that RFC 9207 requires, the field a client uses to confirm which server actually answered it. Both are now correct, including on error redirects.
Bridged results are wrapped as an object
The Abilities Bridge lets abilities registered by other plugins flow through the same governed pipeline as the built-in catalog. Some of those bridged abilities were answering with a bare JSON list where the MCP protocol expects an object, and strict clients were rejecting that outright rather than guessing at the shape. Bridged results are now always wrapped as an object before they reach the wire, so a third-party ability’s answer is something every conformant client can actually read.
Least privilege only means something if it holds
None of this changes what the plugin promises, it makes the promise true. Off by default, least privilege, capability-gated, fully audited, all of that only matters if a user connected with limited access actually gets limited access back. This release closes a case where they did not, and the rest of the fixes tighten a few smaller gaps around it. It follows the same week’s 1.4.1 standards fixes and 1.4.2 status and permission fixes. If you run the plugin, 1.4.3 is worth updating to.