Lesson 1 established that every MCP connection resolves to a real WordPress user. This lesson makes the practical consequence of that fact explicit: an AI agent’s access is never defined by “being an agent,” it’s defined entirely by whatever role and capabilities that underlying user account already has. There is no separate agent permission system to configure. You are configuring WordPress’s existing one.
Lesson 1, and a basic familiarity with WordPress’s built-in roles (Administrator, Editor, Author, Contributor, Subscriber). No new tools needed yet.
Step 1: The connection is the user, permanently
When Claude, Cursor, or any other MCP client sends a request using an Application
Password, WordPress resolves that request to the exact WP_User who generated the
password, no more, no less. If that user is an Administrator, the agent has
Administrator-level capabilities everywhere a permission_callback checks them. If
that user is a Contributor, the agent is bounded by Contributor-level capabilities. This
sounds obvious stated plainly, but it’s the single most common misconfiguration in real
deployments: someone generates an Application Password from their own Administrator
account “just to get it working,” and every AI client using that credential inherits
full site control indefinitely.
This is the most important sentence in this course. Every Application Password an AI client uses should belong to a dedicated, purpose-built account with the minimum capabilities that agent actually needs, never your own admin login. The next lesson builds exactly that account.
Step 2: A precise refresher on roles and capabilities
WordPress’s permission model has two layers, and both matter here:
An ability’s permission_callback is almost always, directly or indirectly, a call to
current_user_can():
// File: my-plugin.php
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
There is nothing MCP-specific about this check. It’s the same function, checking the same capability, that WordPress core itself uses to decide whether to show an “Edit” button in wp-admin. An AI agent invoking this ability is subject to exactly the same rule as a human clicking that button, because it’s authenticating as the same kind of account.
Step 3: Object-level checks resolve against the same user too
Some capability checks aren’t just role-based, they’re checked against a specific object, most commonly via WordPress’s meta-capability mapping:
current_user_can( 'edit_post', $post_id );
This resolves differently depending on who owns $post_id. A Contributor can edit their
own draft but not someone else’s. An agent authenticated as a Contributor-role account
inherits exactly that same restriction, it can act on posts that account owns, and gets
correctly denied on posts it doesn’t, with zero extra code from you. This is a real,
useful property: WordPress’s existing ownership rules apply to agent traffic
automatically, as long as your permission_callback actually uses them rather than a
blanket true.
It’s tempting, especially while debugging a new ability, to write
'permission_callback' => '__return_true' and move on. Ship that, and every one of
WordPress’s built-in ownership and capability checks stops applying to that ability
entirely, regardless of which account is connected. Always route through
current_user_can(), even during development.
Step 4: Why the built-in roles are usually the wrong fit
The five default roles were designed for humans managing content through wp-admin, not
for scoping a programmatic AI connection. Editor, for instance, bundles edit_others_posts,
delete_posts, manage_categories, and more, likely far more than a single agent
workflow (say, “summarize new comments”) actually needs. Reaching for the nearest
built-in role as a shortcut tends to over-grant. The next lesson covers building a
dedicated role or a small set of custom capabilities scoped to exactly what a given
agent connection is meant to do.
Test it: confirm which capabilities a connected account actually has
Before trusting any existing connection, check exactly what its underlying user can do:
wp-env run cli wp eval '
$user = get_user_by( "login", "ai-agent-readonly" );
print_r( $user->allcaps );
'
Review that list against what the agent’s workflow actually requires. Anything present that the workflow doesn’t need is unused, unnecessary risk.
Recap
An MCP connection has no identity beyond the WordPress user it authenticated as. That
user’s role and capabilities are the entire access boundary, checked through the same
current_user_can() function WordPress core uses everywhere else, including
object-level ownership checks. Never issue an agent credential from an Administrator
account, and don’t assume a built-in role is well-scoped just because it’s convenient.
The next lesson builds a dedicated, minimally-scoped role instead.
Resources & further reading
- Roles and Capabilities, WordPress developer reference, developer.wordpress.org
- current_user_can() function reference, developer.wordpress.org
- MCP Adapter repository, GitHub