Generated banner for Wp Abilities API
Generated logo for Wp Abilities API

Wp Abilities API

WordPressOfficialPublished by WordPress

  • Skill
  • Tool integration
  • Software development
  • GPL-2.0-or-later
  • No issues found

From the author

Use when working with the WordPress Abilities API (wp_register_ability, wp_register_ability_category, /wp-json/wp-abilities/v1/*, @wordpress/abilities) including defining abilities, categories, meta, REST exposure, and permissions checks for clients.
— the description by WordPress

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to working with the WordPress Abilities API. It covers the registration of abilities and categories in PHP, exposing them via the REST API, and consuming them in JavaScript using @wordpress/abilities. The documentation emphasizes a layered architecture—distinguishing between domain capabilities and their projections—to ensure stable, transport-neutral plugin contracts that remain maintainable as consumer requirements evolve.

What it does

  • Registers abilities and categories using the WordPress Abilities API.
  • Exposes domain capabilities through the REST API for client consumption.
  • Provides patterns for delegating ability execution to existing REST controllers.
  • Offers guidance on grouping heuristics to avoid atomic-ability bloat.
  • Defines standard error vocabulary for consistent agent-based error handling.
  • Facilitates client-side ability consumption via @wordpress/abilities.

When to use it

  • When adding new capabilities that should be discoverable by agents or external clients.
  • When refactoring existing REST endpoints into a more stable, action-oriented ability contract.
  • When building features that require consistent permission checks across different transport layers.
  • When debugging issues where abilities are not appearing in REST responses or client-side registries.

Advantages

  • Promotes a stable, transport-neutral domain layer that survives UI or consumer changes.
  • Reduces boilerplate by providing patterns for delegating to existing controller logic.
  • Ensures consistent error handling and input validation across the plugin ecosystem.
  • Improves discoverability of plugin actions for AI agents and automated workflows.

Usage examples

“Register a new ability for listing user orders in my plugin.”
The agent will guide you through defining the ability ID, category, and meta, ensuring it is registered via the correct hooks and exposed in the REST API.
“My ability is not showing up in the REST API; how do I debug this?”
The agent will help you verify the registration hook, check for the show_in_rest meta flag, and confirm the namespace and route configuration.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/wordpress/wp-abilities-api/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip wp-abilities-api.zip -d .agents/skills

Documentation by WordPress

WP Abilities API

When to use

Use this skill when the task involves:

  • registering abilities or ability categories in PHP,

  • exposing abilities to clients via REST (wp-abilities/v1),

  • consuming abilities in JS (notably @wordpress/abilities),

  • diagnosing “ability doesn’t show up” / “client can’t see ability” / “REST returns empty”.

Inputs required

  • Repo root (run wp-project-triage first if you haven’t).

  • Target WordPress version(s) and whether this is WP core or a plugin/theme.

  • Where the change should live (plugin vs theme vs mu-plugin).

Procedure

Before deciding what to register, read references/domain-vs-projection.md — abilities live at the domain capability layer; MCP / Command Palette / REST exposure is a projection. Registration shape and exposure shape are different decisions, and conflating them forces re-registration every time a consumer's constraints change.

1) Confirm availability and version constraints
  • If this is WP core work, check signals.isWpCoreCheckout and versions.wordpress.core.

  • If the project targets WP < 6.9, you may need the Abilities API plugin/package rather than relying on core.

2) Find existing Abilities usage

Search for these in the repo:

  • wp_register_ability(

  • wp_register_ability_category(

  • wp_abilities_api_init

  • wp_abilities_api_categories_init

  • wp-abilities/v1

  • @wordpress/abilities

If none exist, decide whether you’re introducing Abilities API fresh (new registrations + client consumption) or only consuming.

3) Register categories (optional)

If you need a logical grouping, register an ability category early (see references/php-registration.md).

4) Register abilities (PHP)

For grouping decisions (how many abilities to register, and where to put filters vs. new ability names), read references/grouping-heuristic.md first — it keeps you from shipping one atomic ability per REST operation.

To avoid drift between the ability and the existing UI / REST code path, see references/shared-core-service.md — abilities, REST handlers, CLI commands, and UI controllers should be thin adapters over a shared service. The reference also covers the metric trap (REST handlers that emit usage telemetry) and the AGENTS.md rule for keeping registrations in sync when underlying code paths change.

For shared helper patterns when multiple execute callbacks delegate to existing REST controllers, see references/plugin-family-patterns.md (identify the shared-API-client vs zero-arg-controllers shape) and references/delegate-helper-pattern.md (one helper shape that works, and when not to use it).

For standardized WP_Error codes that let agents reason about retry vs. escalation, see references/error-code-vocabulary.md.

Implement the ability in PHP registration with:

  • stable id (namespaced),

  • label/description,

  • category,

  • meta:

    • add readonly: true when the ability is informational,

    • set show_in_rest: true for abilities you want visible to clients.

Use the documented init hooks for Abilities API registration so they load at the right time (see references/php-registration.md).

5) Confirm REST exposure
  • Verify the REST endpoints exist and return expected results (see references/rest-api.md).

  • If the client still can’t see the ability, confirm meta.show_in_rest is enabled and you’re querying the right endpoint.

6) Consume from JS (if needed)
  • Prefer @wordpress/abilities APIs for client-side access and checks.

  • Ensure build tooling includes the dependency and the project’s build pipeline bundles it.

Verification

  • wp-project-triage indicates signals.usesAbilitiesApi: true after your change (if applicable).

  • REST check (in a WP environment): endpoints under wp-abilities/v1 return your ability and category when expected.

  • If the repo has tests, add/update coverage near:

    • PHP: ability registration and meta exposure

    • JS: ability consumption and UI gating

Failure modes / debugging

  • Ability never appears:

    • registration code not running (wrong hook / file not loaded),

    • missing meta.show_in_rest,

    • incorrect category/ID mismatch.

  • REST shows ability but JS doesn’t:

    • wrong REST base/namespace,

    • JS dependency not bundled,

    • caching (object/page caches) masking changes.

  • Execute callback returns unexpected errors or silently ignores input:

    • input_schema defaults aren't being applied, pagination key drift between the ability and the backing, or empty()-based ID validation — see references/input-schema-gotchas.md.

Escalation

  • If you’re uncertain about version support, confirm target WP core versions and whether Abilities API is expected from core or as a plugin.

  • For canonical details, consult:

    • references/rest-api.md

    • references/php-registration.md

Files

references/grouping-heuristic.md

Grouping heuristic — domain-layer granularity

How to decide WHAT to register when a plugin already has a REST (or internal service) surface. The hard part of adopting the Abilities API is not the registration syntax — it's picking the right domain-layer granularity so abilities map to user-meaningful actions instead of HTTP plumbing.

Scope note. This reference governs domain-layer decisions: how many abilities to register, where to put filters vs. where to introduce a new ability name. It does NOT govern projection-layer choices (flat-with-full-schemas vs single-tool facade vs nested-discovery vs semantic grouping in the consumer view). Those are separate decisions made after the domain layer is settled — see domain-vs-projection.md for the layering. A domain layer chosen well is reusable across multiple projections; conflating the two means re-registering every time a consumer's constraints change.

Three observed approaches

ApproachShapeExampleVerdict (domain-layer)
Action-bundleOne ability bundles many sub-operations behind an action string.my_plugin_account with action: "get" | "update" | "delete".Avoid. Hides the ability surface from the agent's tool-list and defeats the Abilities API's introspection model — agents can't see what a bundle can do until they invoke it.
REST-atomizationOne ability per HTTP method per resource (typically 5 per resource: list, get, create, update, delete).orders-list, orders-get, orders-create, orders-update, orders-delete.Avoid as the registration shape. Couples ability names to HTTP plumbing rather than user intent — and forces re-registration if the projection layer ever needs to compress the surface.
Semantic-intentOne ability per real-world question or state transition. Filter parameters in input_schema collapse N variants into 1.One feedback/get-responses ability with status, is_unread, search, and date-range filters in input_schema — replaces what would be 8+ atomized variants.Recommended for the domain layer.

Why semantic-intent wins at the domain layer

  1. Users think in questions, not HTTP verbs. "Which form responses are unread?" maps cleanly to one ability with an is_unread filter. It does NOT map to 8 abilities (get-unread-responses, get-spam-responses, get-trashed-responses, ...). Ability names are use-case contracts — see ./domain-vs-projection.md.

  2. The Abilities API's input_schema is designed for rich inputs. Enum constraints, date-time formats, and required-field validation do the variant-splitting job that atomization would delegate to the ability name.

  3. Writes stay narrow anyway. A write ability should already be one state transition; atomization and semantic-intent converge for writes.

  4. Tool-list token cost is a downstream consequence, not the reason. Semantic-intent registrations also serialize cheaper in flat MCP projections — but token cost is a projection-layer concern. If registrations are cheap by accident because the use-case framing happened to compress them, that's a happy side-effect; if they're expensive, the fix is at the projection layer (single-tool facade, nested-discovery), not by re-grouping the domain.

Rules that make it work

1. Group reads by the question a user would type

Draft the question in plain English. That question is the ability. The filter parameters go in input_schema.

  • WRONG: one ability per status value.

  • RIGHT: one get-<resource> ability with status: { type: "string", enum: [...] }.

2. Keep writes narrow — one state transition per ability

A write ability should do exactly one thing the agent can reason about in isolation and explain to a user. Different state transitions → different abilities (different consequences, different annotations, different permission implications).

  • WRONG: update-resource that branches internally on an action enum.

  • RIGHT: submit-evidence and close-resource as separate abilities.

3. Prefer 1 ability with filter params over N abilities with no params

Ask: "if the backing added a new filter value, would that create a new ability?" If not, the filter belongs in input_schema, not in the ability name.

4. Zero-arg overview abilities are high-leverage

When enumerating the backing surface, specifically look for zero-argument aggregate or "overview" endpoints — "what's my balance?", "what's my next payout?", "what's my form response count?". These answer the highest-frequency user questions with zero input and zero room for agent error. Flag them even if they weren't in the original plan.

5. Don't ship abilities you can't explain in one sentence

Every ability's label + description should fit in an agent's tool-selection prompt. If you can't describe the ability in one sentence without "and", that's usually a sign it's two abilities.

Worked example A — feedback/responses: 3 abilities for the whole responses surface

Consider a generic feedback or form-response plugin. Its admin screens expose: a list with 8+ filters, a detail view, bulk status changes (spam / trash / publish), read/unread toggles, and a count-by-status dashboard summary.

REST-atomization would ship ~6 abilities (list, get, delete, update, bulk-update, count). Semantic-intent registers three:

  • feedback/get-responses — list/search, with status, is_unread, search, before, after, parent in input_schema.

  • feedback/update-response — one write that covers status changes AND read-state toggles on a single response (semantically "modify a response").

  • feedback/get-status-counts — the dashboard summary ability. Zero-arg-friendly (only optional filters).

Why three works: a user asking "show me spam responses from last week" uses one ability. An agent updating one response to spam uses one ability. The dashboard-style "how many unread?" uses one ability. The entire product surface fits in three tool-list entries.

Worked example B — generic Tickets plugin: one ability with a status filter, not eight

A hypothetical myplugin-tickets plugin manages a support-ticket queue. Its REST endpoint GET /myplugin/v1/tickets accepts status, priority, assigned_to, tag, date_before, date_after, search. Status values include new, triaged, in_progress, waiting_customer, waiting_internal, resolved, closed, reopened — eight in total.

  • Atomization would ship ~8 abilities (get-tickets-new, get-tickets-resolved, get-tickets-closed, ...).

  • Semantic-intent ships one — myplugin-tickets/get-tickets with status: { type: "string", enum: ["new", "triaged", "in_progress", "waiting_customer", "waiting_internal", "resolved", "closed", "reopened"] }.

The user question "which tickets are waiting on the customer?" becomes one ability invocation with status: "waiting_customer". The agent doesn't scan a list of 8 near-identical tool names; it scans one, and the enum documents what values are valid.

The same plugin also registers a zero-arg myplugin-tickets/get-queue-summary ability (open count + average response time + oldest unresolved) because "how is the queue today?" is the highest-frequency support-team question and the backing endpoint takes no arguments. That one ability has outsized value for one line of registration code — rule 4 in action.

Escape hatch — when per-operation granularity is right (for reads)

Two cases where one-ability-per-operation IS appropriate on the read side, despite the recommendation against REST-atomization for reads:

  1. Genuinely different permission models. If list-<resource> and search-<resource> require different capabilities or different confirmation flows, splitting is honest.

  2. Different destructive / idempotent annotations. An ability that both reads and writes cannot honestly declare readonly: true; split the read-only part into its own ability.

For writes, this escape hatch isn't needed: rule 2 ("one state transition per ability") already establishes per-operation granularity as the default. Splitting submit-evidence and close-resource into separate abilities isn't an exception — it's rule 2 in action.

  • domain-vs-projection.md — granularity governs domain-layer decisions; that reference covers the projection layer where token-efficiency tradeoffs and consumer-shape choices live.

  • shared-core-service.md — implementation mechanism for keeping abilities, REST handlers, CLI commands, and UI in lockstep on the domain layer.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

The author says: Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
WordPress
Publisher
WordPress (Official)
Source
WordPress/agent-skills · skills/wp-abilities-api · commit 7374e6f
Licence
GNU General Public License v2.0 or later (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by WordPress or WordPress.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download wp-abilities-api.zip