Date: 2026-02-19 (updated 2026-06-14)
WP version evaluated: 7.0 Beta 1 through RC2
Status: Pre-GA assessment retained. No Abilities-specific gating change is planned from this review; Connectors credential REST writes are gated on main, and the PHP execution path remains monitor-only.
Covers: Abilities API, WordPress MCP Adapter, AI Client, Connectors API
The WordPress Abilities API, introduced in WP 6.9, exposes registered "abilities" via REST endpoints and (optionally) WP-CLI. The WordPress MCP Adapter translates these abilities into MCP tools for AI agents (Claude, Cursor, etc.) β it calls the same REST endpoints, so both are covered by the same gating analysis.
This document evaluates the pre-GA WP 7.0 surface, explains why WP Sudo does not currently need to gate Abilities API calls, and documents the strategy for when gating becomes necessary. Treat implementation details here as source-derived research; use release-status.md and ROADMAP.md for current release posture and open follow-up tasks.
Verification sources for ability names and REST routes:
- Abilities API in WordPress 6.9 β Make WordPress Core β 3 core abilities,
permission_callbackpattern - Abilities API REST Endpoints β developer.wordpress.org β REST route structure
- From Abilities to AI Agents: Introducing the WordPress MCP Adapter β developer.wordpress.org β confirms 3 read-only abilities as of 7.0 Beta 1
Ability names were verified against official sources listed above and in
.planning/phases/05-wp-7-0-readiness/05-RESEARCH.md, not inferred from training data.
As of the WP 7.0 Beta/RC review, WordPress core registered three abilities. All three were read-only: they exposed information but did not modify or destroy site state.
| Ability ID | Label | Permission Callback | Destructive? |
|---|---|---|---|
core/get-site-info |
Get Site Information | current_user_can('read') |
No |
core/get-user-info |
Get User Information | current_user_can('read') |
No |
core/get-environment-info |
Get Environment Info | current_user_can('read') |
No |
Abilities are registered inside the wp_abilities_api_init action using
wp_register_ability(). Each registration specifies a permission_callback
(capability check) and an execute_callback (returns data).
The Abilities API registers the following REST routes under the wp-abilities/v1
namespace:
| Method | Route | Description |
|---|---|---|
GET |
/wp-json/wp-abilities/v1/abilities |
List all registered abilities |
GET |
/wp-json/wp-abilities/v1/categories |
List ability categories |
GET |
/wp-json/wp-abilities/v1/{ns}/{name} |
Get a single ability by namespace and name |
GET|POST|DELETE |
/wp-json/wp-abilities/v1/{ns}/{name}/run |
Execute an ability |
The HTTP method for the /run endpoint is determined by the ability type:
- Read-only operations use
GET - Operations requiring input parameters use
POST - Destructive operations use
DELETE
In the evaluated Beta/RC surface, no registered core abilities used DELETE on
/run. All three core abilities used GET.
WP Sudo's gating model intercepts operations that modify or destroy site state: activating plugins, deleting users, changing critical settings, installing themes, and so on. Read-only operations are explicitly outside WP Sudo's scope.
The evaluated core abilities were read-only. They expose information about the site, user, and environment β but they do not change anything. No reauthentication is warranted for information retrieval.
The Abilities API uses permission_callback (a standard WordPress capability check
such as current_user_can('read')) to control access. This is authorization β it
answers "is this user allowed to call this ability at all?"
WP Sudo provides reauthentication β it answers "has this user recently confirmed
their identity, regardless of their role?" These are complementary controls, not
substitutes. The permission_callback check runs inside WordPress before the
execute_callback fires. WP Sudo would intercept at the REST layer (via
rest_request_before_callbacks) before the permission_callback even runs.
For read-only abilities, the permission_callback check is sufficient. WP Sudo
would add no additional security value by intercepting them.
The Gate class (includes/class-gate.php) currently recognizes seven surfaces:
| Surface | Interception point |
|---|---|
admin |
admin_init at priority 1 |
ajax |
admin_init at priority 1 (also fires for admin-ajax.php) |
rest |
rest_request_before_callbacks filter |
cli |
init at priority 0 via function-level hooks |
cron |
init at priority 0 via function-level hooks |
xmlrpc |
init at priority 0 via xmlrpc_enabled filter and function hooks |
wpgraphql |
graphql_process_http_request action (v2.5.0+, conditional on WPGraphQL being active) |
There is no ability surface type. The Abilities API REST routes are served through
the standard WordPress REST API and are therefore already covered by the existing
rest surface interception β no special handling is required.
When a destructive ability appears in WordPress core or a plugin (indicated by a
DELETE method on a /wp-abilities/v1/{ns}/{name}/run route), WP Sudo can gate it
without adding a new surface type.
The existing intercept_rest() method in Gate already intercepts all REST requests
via rest_request_before_callbacks and routes them through match_request('rest').
A new rule in Action_Registry matching the destructive ability's route is all that
is needed:
// Example: hypothetical destructive ability
[
'id' => 'abilities.delete_plugin',
'label' => __( 'Delete plugin via Abilities API', 'wp-sudo' ),
'rest' => [
'route' => '#^/wp-abilities/v1/core/delete-plugin/run$#',
'methods' => [ 'DELETE' ],
],
],The existing matches_rest() method in Gate checks route pattern and HTTP method,
so a regex matching /wp-abilities/v1/.*/run with DELETE would catch all destructive
ability runs in a single rule:
[
'id' => 'abilities.run_destructive',
'label' => __( 'Run destructive ability', 'wp-sudo' ),
'rest' => [
'route' => '#^/wp-abilities/v1/[^/]+/[^/]+/run$#',
'methods' => [ 'DELETE' ],
],
],No new surface type is required for REST-exposed abilities.
The WordPress MCP Adapter translates registered abilities into MCP tools. When an AI
agent calls an MCP tool, the adapter executes the corresponding ability via the same
/wp-abilities/v1/{ns}/{name}/run REST endpoint. From WP Sudo's perspective, an
MCP-originated ability call is indistinguishable from any other REST request β it
flows through rest_request_before_callbacks and is subject to the same Gate
interception.
No special handling is required for MCP Adapter calls. The same REST rules that gate direct ability calls also gate MCP-mediated calls.
For abilities executed via WP-CLI's wp ability run command, the existing CLI
surface gating via function-level hooks in register_function_hooks() applies. A
hook on the appropriate WordPress action that fires before the ability's
execute_callback would be added to the function hook registration block.
Correction (2026-04-13): The original assessment stated that no PHP-level
execution path exists. This was incorrect. WordPress 7.0 includes
WP_Ability::execute() and wp_get_ability(), which allow any PHP code to
execute an ability directly β bypassing the REST API entirely.
Abilities registered with show_in_rest => false can only be executed this way.
They are hidden from REST listings and return rest_ability_not_found on REST
access, but remain callable via:
$ability = wp_get_ability( 'namespace/ability-name' );
$result = $ability->execute( $input );Authorization on the PHP path: WP_Ability::execute() runs input validation
against the ability's input schema, calls check_permissions() (the ability's
permission_callback), executes the callback via do_execute(), and validates
output. This is a capability check (authorization), not reauthentication.
Hooks: Two action hooks fire around execution:
wp_before_execute_abilityβ fires before the ability runswp_after_execute_abilityβ fires after the ability completes
The wp_before_execute_ability hook is the interception point for WP Sudo. If
a destructive ability is registered that plugins call via the PHP path (bypassing
REST), WP Sudo can hook into wp_before_execute_ability to block execution when
no sudo session is active β similar to the function-level hooks used for CLI,
Cron, and XML-RPC surfaces in Gate::register_function_hooks().
The PHP execution path already exists (condition 1 from the original trigger list is met). However, the remaining conditions are not yet met:
Revised trigger conditions:
A non-REST, non-CLI ability execution path is introducedβ exists now (WP_Ability::execute())- A destructive ability is registered that plugins are likely to call via the PHP path (not just REST)
- The
wp_before_execute_abilityhook proves to be a reliable interception point (i.e., it fires consistently, cannot be bypassed, and provides enough context to identify the ability being executed)
Condition 2 is the practical trigger. Until destructive abilities exist, the PHP execution path is not a security concern β the three current core abilities are all read-only.
Implementation plan when conditions 2 and 3 are met:
- Hook
wp_before_execute_abilityinGate::register()(all surfaces, not just non-interactive) to catch PHP-path execution regardless of the calling context - Check the ability ID against a list of gated ability IDs in
Action_Registry - If the ability is gated and no sudo session is active, block with
wp_die()or return aWP_Errordepending on context
This does not require a new surface constant β the existing admin surface
(or a new ability label for audit hooks) is sufficient.
WordPress 7.0 introduces a built-in AI Client β a provider-agnostic PHP API for
sending prompts to external AI models (text generation, image generation, TTS,
etc.). The entry point is wp_ai_client_prompt(), which returns a fluent builder.
WP Sudo does not need to gate AI Client prompt execution. Sending a prompt
to an external AI provider does not modify WordPress state. It is analogous to
wp_remote_post() β an outbound HTTP call, not a destructive site operation.
WordPress core provides the wp_ai_client_prevent_prompt filter for prompt-level
access control, which is the correct layer for that concern.
AI provider credentials (API keys) are managed through the Connectors API, which provides a settings page at Settings > Connectors. AI provider plugins that register with the AI Client's provider registry get automatic Connectors integration.
This is now an active gating target in WP Sudo. An attacker with a stolen admin session could use the Connectors settings page to:
- Exfiltrate data β redirect AI traffic to an attacker-controlled endpoint, capturing prompts that may contain site content, user data, or admin context
- Commit billing fraud β replace a legitimate API key with the attacker's own
- Denial of service β delete provider credentials, breaking AI-dependent features
Connectors credential changes are a settings modification comparable to other
settings that WP Sudo already gates. Current main now ships a built-in REST
rule, connectors.update_credentials, that matches:
POST/PUT/PATCHto/wp/v2/settings- only when request params include connector-style credential setting names
matching
connectors_[a-z0-9_]+_api_key
This is intentionally narrower than gating all REST settings writes. It closes the write-only key replacement path for database-backed connector credentials without interfering with unrelated settings updates.
Current source-grounded understanding: the official dev note exists, and core
routes connector credential writes through the standard REST settings endpoint
rather than a bespoke admin form action. The remaining task is GA parity
verification, not first implementation: confirm the released route, setting-name
pattern, and masking/validation behavior still match the current analysis in
connectors-api-reference.md.
The WordPress MCP Adapter uses per-request authentication:
- STDIO transport: Authenticates via WP-CLI with
--userat server startup - HTTP transport: Uses Application Passwords or custom OAuth per-request
There is no persistent AI agent session concept, no long-lived agent tokens, and no session state maintained across requests. Each MCP tool call is an independent authenticated request that flows through the existing REST or CLI surface.
The pre-GA review did not find a core proposal for persistent agent sessions or long-lived agent tokens. The real-time collaboration work uses short-lived WebSocket tokens for human editors, not AI agents. Re-check this assumption before designing future agent-specific policy.
If a persistent agent session concept is introduced in a future release, it would warrant a new policy tier in WP Sudo (comparable to CLI or Cron policy) β a long-lived token that can perform multiple operations without per-request authentication is a new trust boundary.
No Abilities-specific Gate changes are planned from this assessment alone.
The evaluated core abilities were read-only. The existing REST surface interception in
Gate::intercept_rest() already covers the /wp-abilities/v1/ namespace routes if
a matching rule is ever added to Action_Registry. The PHP execution path exists
but is not a concern until destructive abilities are registered.
Monitoring action items:
- Watch the abilities-api GitHub
repository for new ability registrations, especially any using
DELETEon/runor registered withshow_in_rest => false. - When destructive abilities appear, add a REST rule to
Action_Registrymatching/wp-abilities/v1/.*/runwithDELETEmethod. NoGateclass changes required. This also covers MCP Adapter calls (same REST endpoints). - For abilities with
show_in_rest => falsethat are destructive, hookwp_before_execute_abilityto gate the PHP execution path. - For WP-CLI
wp ability runwith destructive abilities, add a function-level hook inGate::register_function_hooks()targeting the appropriate WordPress action. - Before the next release that changes Connectors coverage, verify that the
released Connectors settings page still writes credential changes through
/wp/v2/settings, that connector setting names still follow the documentedconnectors_*_api_keypattern, and that the built-inconnectors.update_credentialsrule remains accurate. - Monitor Make Core and Trac for any proposal to introduce persistent AI agent sessions or long-lived agent tokens β this would require a new WP Sudo policy tier.
- Monitor the WordPress MCP Adapter for any direct-execution path that bypasses REST.