Wp abilities api
Skill Lonsdale201/wp-agent-skills/wordpress/wp-abilities-api
Register WordPress Abilities: machine-readable plugin operations with JSON Schema contracts, required permission callbacks, optional REST exposure, client-side abilities, and AI/MCP-friendly discovery. Covers categories, wp_register_ability, WP_Ability::execute, REST run endpoints, @wordpress/abilities, @wordpress/core-abilities, meta.show_in_rest, annotations, and Ability vs REST route vs custom hook decisions. Use when exposing plugin functionality to agents, admin JS, external tools, WP AI Client workflows, or reviewing AI integration code.From its SKILL.md
npx -y skills add Lonsdale201/wp-agent-skills --skill wp-abilities-apiAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 21 stars21 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
17.2 KB, ~3.6k tokens by cl100k_base, as published. Nobody here has run it
WordPress Abilities API
A standardized registry for plugin / theme / core functionality, designed primarily so AI agents and external tools can discover what a WordPress site can do and invoke those capabilities through a uniform contract. Each Ability has a stable identifier, JSON-Schema-typed inputs and outputs, and a required permission callback. WP_Ability::execute() validates input, checks permissions, executes, and validates output.
Pre-Abilities, the same functionality was scattered across do_action, apply_filters, custom REST routes, public PHP functions, and ad-hoc plugin APIs (each with its own conventions). The Abilities API consolidates that into one machine-readable surface.
This skill is grounded in WordPress 7.0 core behavior. Server-side Abilities shipped in WordPress 6.9; WordPress 7.0 adds the client-side @wordpress/abilities package, @wordpress/core-abilities bridge, WP AI Client integration, and REST schema cleanup for client validation. If a handbook page and installed core disagree, verify against the target site's wp-includes/abilities-api, REST controller classes, and wp-includes/js/dist/script-modules/abilities.
When to use this skill
Trigger when ANY of the following is true:
- Scaffolding plugin functionality that AI agents (Claude, ChatGPT, custom MCP clients) should be able to discover and invoke.
- Designing an admin tool whose logic should be reachable from BOTH PHP code and the block editor JS without writing two parallel implementations.
- Reviewing a plugin where you see
wp_register_ability,wp_get_ability,WP_Ability, thewp_abilities_api_initaction, or the@wordpress/abilitiesJS package. - Registering client-side admin operations with
@wordpress/abilities, or loading server abilities into JS with@wordpress/core-abilities. - Passing abilities to
wp_ai_client_prompt()->using_abilities()or handlingWP_AI_Client_Ability_Function_Resolver. - Deciding whether to expose a feature as a custom REST endpoint vs an Ability.
- Migrating an existing custom REST endpoint to the Abilities API.
Availability and installation
Three installation paths, depending on WP version:
- WordPress 7.0+ - server-side PHP API plus client-side
@wordpress/abilitiesand@wordpress/core-abilitiesscript modules ship in core. - WordPress 6.9.x - server-side PHP API, registry, REST exposure, and core integration ship in core. Feature-detect the JS packages before relying on them.
- WordPress < 6.9 - Composer package for plugins that bundle their own dependencies:
Package: https://packagist.org/packages/wordpress/abilities-api.composer require wordpress/abilities-api - WordPress < 6.9 - feature plugin for site-wide install: download the latest release ZIP from https://github.com/WordPress/abilities-api or install via WP admin.
For JavaScript, prefer script modules and explicit dependencies. Do not assume a window.wp.abilities global exists.
Registering a category (prerequisite)
Every Ability MUST belong to a category. Register categories on the dedicated init action (Abilities API docs):
add_action( 'wp_abilities_api_categories_init', 'myplugin_register_ability_categories' );
function myplugin_register_ability_categories(): void {
wp_register_ability_category(
'myplugin-site-information',
array(
'label' => __( 'Site Information', 'myplugin' ),
'description' => __( 'Abilities that report on site state.', 'myplugin' ),
)
);
}
If two plugins register the same category slug, the second registration fails with _doing_it_wrong() and returns null; the first category remains registered. Pick a slug specific enough to avoid collisions (myplugin-tools, not tools).
Registering an ability — minimal example
add_action( 'wp_abilities_api_init', 'myplugin_register_abilities' );
function myplugin_register_abilities(): void {
wp_register_ability(
'myplugin/site-info',
array(
'label' => __( 'Site Info', 'myplugin' ),
'description' => __( 'Returns information about this WordPress site.', 'myplugin' ),
'category' => 'myplugin-site-information',
'input_schema' => array(),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'site_name' => array(
'type' => 'string',
'description' => __( 'The name of the site.', 'myplugin' ),
),
'php_version' => array(
'type' => 'string',
'description' => __( 'PHP version running on the server.', 'myplugin' ),
),
),
),
'execute_callback' => 'myplugin_get_site_info',
'permission_callback' => static fn () => current_user_can( 'manage_options' ),
'meta' => array(
'show_in_rest' => true,
),
)
);
}
function myplugin_get_site_info(): array {
return array(
'site_name' => get_bloginfo( 'name' ),
'php_version' => PHP_VERSION,
);
}
Registration arguments — what's required
WordPress 6.9+ and 7.0 core validate these arguments in WP_Ability::prepare_properties() and WP_Abilities_Registry::register():
| Field | Required | Type | Notes |
|---|---|---|---|
label | yes | string | Translated, human-readable. |
description | yes | string | Translated. AI agents read this to decide whether to invoke. |
category | yes | string | Slug must match a registered category. |
input_schema | no | array | Required in practice when the ability accepts input. If omitted/empty and input is provided, execution returns ability_missing_input_schema. |
output_schema | no in core, yes in docs/review | array | Core allows omission and then skips output validation. Provide it anyway for contracts, REST discovery, and agents. |
execute_callback | yes | callable | The PHP function that runs. |
permission_callback | yes | callable | Returns true, false, or WP_Error. Enforced by WP_Ability::execute() and by REST run permission checks. |
meta | optional | array | Additional metadata. 'show_in_rest' => true exposes the ability via the REST endpoints. |
The description is the most important field for AI consumers — it's what an agent reads to decide whether the Ability is the right tool. Write it the way you'd describe the function to a smart colleague reviewing the API.
Callback arity depends on input_schema: if the schema is empty, WordPress calls execute_callback and permission_callback with no arguments. If the schema is non-empty, WordPress passes the normalized input as the first argument.
Naming convention
Ability identifier: namespace/ability-name (slash-separated, kebab-case on both sides).
- The
namespaceis your plugin slug or a sub-system inside it (myplugin,myplugin-billing). - The
ability-namedescribes what it does (get-site-info,cancel-subscription,summarize-post).
Pick names a non-developer could read aloud — myplugin/cancel-subscription is better than myplugin/cancel-sub-v2. Treat the identifier as a public API contract: once an Ability is shipped and an AI agent or external client depends on it, renaming is a breaking change.
Server-side PHP registration in WP 7.0 accepts exactly two slash-separated segments (myplugin/do-thing). The client-side @wordpress/abilities registry accepts 2-4 segments, but use the two-segment form for abilities that need to round-trip through PHP, REST, or the WP AI Client.
Permission callback
Required. Return true to allow execution, false to deny, or WP_Error for a structured REST permission failure. WP_Ability::execute() enforces this for direct PHP execution; if the callback returns WP_Error there, core logs it via _doing_it_wrong() and returns a generic ability_invalid_permissions error so the message is not leaked. The REST run endpoint also checks permissions before execution:
'permission_callback' => static function (): bool {
return current_user_can( 'manage_options' );
},
For object-level abilities (operate on a specific post / user / order), pass the object ID through the meta-capability check:
'permission_callback' => static function ( array $input ): bool {
return current_user_can( 'edit_post', (int) ( $input['post_id'] ?? 0 ) );
},
Keep permission callbacks cheap and side-effect-free. In the REST run flow, WordPress validates input and calls check_permissions() in the REST permission_callback, then WP_Ability::execute() validates and checks permissions again before running the ability. Do not put logging, billing, remote API calls, or state changes in permission_callback.
Executing an ability from PHP
$ability = wp_get_ability( 'myplugin/site-info' );
if ( $ability instanceof WP_Ability ) {
$result = $ability->execute(); // This ability has no input_schema.
}
For an ability with a non-empty input_schema, pass the matching normalized
input array to execute( $input ).
Helpers documented in the announcement:
wp_get_ability( string $id ): ?WP_Abilitywp_get_abilities(): array<string, WP_Ability>wp_has_ability( string $id ): bool
For inspection / debugging during development, use wp shell:
$ wp shell
wp> wp_has_ability( 'myplugin/site-info' );
=> bool(true)
wp> wp_get_ability( 'myplugin/site-info' );
=> object(WP_Ability) ...
REST and JavaScript exposure
Set meta.show_in_rest = true for server abilities that external tools may
discover/run. Core exposes list/get/run routes, enforces the ability permission
callback, selects GET/POST/DELETE from annotations, and strips internal schema
callbacks from REST responses. Public constraints belong in JSON Schema.
Use @wordpress/core-abilities to bridge server abilities into JavaScript and
await its exported ready promise before immediate execution. Use
@wordpress/abilities alone for client-only categories/abilities. Client-side
permission callbacks never replace PHP authorization. Read reference.md for
the verified route shapes, module bootstrap, and complete client examples.
WP AI Client integration
WordPress 7.0 can expose selected server-side abilities as AI model function declarations:
$result = wp_ai_client_prompt( 'Summarize current site diagnostics.' )
->using_abilities( 'core/get-site-info', 'core/get-environment-info' )
->generate_text_result();
For AI-driven ability execution, instantiate WP_AI_Client_Ability_Function_Resolver with an explicit allowlist. Never give a model a blanket list of all abilities; the ability's own permission_callback still runs, but the resolver allowlist is the first boundary.
MCP adapter — bridging to AI agents
The WordPress team maintains a separate MCP adapter that bridges the Abilities registry to the Model Context Protocol — the protocol Claude, ChatGPT, and other AI agent runtimes use to discover and invoke external tools. With the adapter installed, every registered Ability becomes an MCP tool the AI can call, gated by the same permission_callback.
Architectural details of the MCP adapter were not included in the Nov 2025 announcement (deferred to a follow-up post). For now, the practical takeaway: registering a clean, well-described Ability also makes it AI-agent-ready without any extra code on your side, IF the site administrator installs the MCP adapter.
When to use an Ability vs a custom REST route vs a custom hook
| Situation | Use |
|---|---|
| Operation that AI agents / external tools should discover and invoke | Ability |
| Typed operation called from your own JS and potentially reusable by tools | Ability |
| Resource CRUD/public headless API with stable HTTP semantics | Custom REST route/controller |
| Internal extension point (other plugins / themes can wire callbacks) | Custom action / filter hook (see wp-plugin-hooks) |
| Pure UI rendering with no logic to expose externally | Block / shortcode, NOT an Ability |
| Webhook receiver from a third-party service | Custom REST route — webhooks usually need a fixed URL contract that doesn't fit the Abilities namespace shape |
Choose by contract, not novelty: Ability for a discoverable typed operation, REST for an HTTP resource/integration contract, and an action/filter for an in-process extension point.
Critical rules
- Register on
wp_abilities_api_initfor abilities,wp_abilities_api_categories_initfor categories. Don't register oninitdirectly. - Categories are prerequisite. Register categories before abilities.
- Identifier
namespace/ability-nameis a public contract — treat it like a versioned API. - Always provide a
permission_callback; core requires it. Use object-aware capability checks for object-scoped abilities. - Provide schemas even when core does not force them. If the ability accepts input,
input_schemais required in practice; without it, provided input is rejected.output_schemais essential for documentation and agents. - Set
meta.show_in_rest = trueif external tools should call the ability. Without it, only PHP-side->execute()works. - Use
@wordpress/core-abilitiesfor server abilities in JS. Use@wordpress/abilitiesalone only for client-only abilities. - Do not expose internal REST schema callbacks to clients. WP 7.0 strips
sanitize_callback,validate_callback, andarg_optionsfrom REST ability schemas. - Allowlist abilities for WP AI Client tool use. Do not pass all registered abilities to a prompt.
- Description is the AI's tool selector. Write it for a reader who doesn't know your plugin.
- For pre-6.9 sites, use the Composer package or feature plugin. Don't reimplement the registry yourself.
Common mistakes
Audit wrong lifecycle hooks, missing categories/schemas/permissions,
unconditional permission on privileged writes, empty descriptions, generic
namespaces, client categories registered after their abilities, and blanket AI
tool allowlists. Corrected examples are in reference.md.
Cross-references
- Run
wp-rest-apiwhen comparing against customregister_rest_routepatterns. Many existing custom REST endpoints in plugin codebases would be cleaner as Abilities. - Run
wp-ai-clientwhen abilities are being exposed as AI model tools or chained through prompt workflows. - Run
wp-plugin-hooksfor the case where the right primitive is actually anapply_filtersextension point inside the plugin, not a publicly invocable Ability. - Run
wp-security-auditon theexecute_callback— it's a request-handling endpoint reached through REST and MCP. Sanitize / validate inputs, escape outputs, treat the input as untrusted regardless of the schema.
What this skill does NOT cover
- The MCP adapter's internal architecture and configuration (deferred to a separate post in the official series).
- Advanced
@wordpress/abilitiesstore internals beyond registration, querying, execution, and unregistering. - Multisite-network considerations for Abilities (not addressed in the Nov 2025 announcement; verify before relying on per-site vs network registration semantics).
- WP-CLI command coverage beyond
wp shellinspection (no dedicated CLI commands documented yet). - Building a full hook extension layer around abilities. Core provides
wp_abilities_api_categories_init,wp_abilities_api_init,wp_before_execute_ability,wp_after_execute_ability,wp_register_ability_category_args, andwp_register_ability_args.
References
- Abilities API documentation — primary authoritative source.
- Introducing the Abilities API (Nov 2025) — announcement post with concrete examples.
- Client-Side Abilities API in WordPress 7.0.
wordpress/abilities-apiComposer package.- WordPress/abilities-api on GitHub — active feature-plugin repository (the older archived repo is NOT canonical).
- Make WordPress Core AI team blog — ongoing development discussion.
What ships with it: 1 file
3.7 KB alongside SKILL.md
- reference.md3.7 KB