GoBuildWP

WordPress Abilities API

The WordPress Abilities API, from registration to an MCP call.

The WordPress Abilities API is the core registry, added in WordPress 6.9, where plugins declare what they can do as named abilities with an input schema, an output schema and a permission check. This guide registers one ability, calls it from PHP and over REST, and diagnoses four registration or exposure failures. The recorded outputs below belong to the historical WordPress 7.1.2 run on 29 September 2026. The registration example now allowlists post and page and checks the selected type's edit capability; that corrected example has not been rerun, and an Author should not inherit page access from edit_posts.

WordPress 6.9+, Historical 7.1.2 baseline, 29 September 2026

TerminalWordPress 7.1.2, 29 September 2026
$ wp eval 'print_r( wp_get_ability( "demo/count-drafts" )
    ->execute( array( "post_type" => "page" ) ) );' --user=1
Array
(
    [post_type] => page
    [drafts] => 9
)

The parts

What a WordPress ability is made of

One call to wp_register_ability() with a name and an array. The handbook at developer.wordpress.org documents each argument; this is what each one does in practice.

The arguments of wp_register_ability and what they do
NameA namespaced string such as demo/count-drafts. It is how every surface, PHP, REST, JavaScript and MCP, refers to the ability.
Label and descriptionWhat a person, or a model choosing a tool, reads. The description is the part that decides whether an agent picks the right ability.
CategoryRegistered first, on its own hook. An ability that names a category nobody registered is rejected.
Input and output schemaJSON Schema. WordPress validates the input before the callback runs, and since 7.1 the REST layer coerces query-string values to the declared types.
Permission callbackRuns before execution with the current user. Return false and the call fails, whichever surface it came from.
Execute callbackThe work itself, returning data that matches the output schema, or a WP_Error.
MetaAnnotations (readonly, destructive, idempotent), show_in_rest for the REST routes, and mcp.public or public for MCP servers built on the Adapter.

Worked example, 29 September 2026

Register one read-only ability, then call it three ways

A must-use plugin on a WordPress 7.1.2 test site with GoBuildWP active. The ability counts drafts of posts or pages, needs the edit capability of the type asked for, and is marked read-only, visible to REST and public to MCP.

wp-content/mu-plugins/count-drafts.phpPHP
<?php
// wp-content/mu-plugins/count-drafts.php
add_action( 'wp_abilities_api_categories_init', function () {
  wp_register_ability_category( 'demo-site', array(
    'label'       => 'Demo site facts',
    'description' => 'Read-only facts about this site.',
  ) );
} );

add_action( 'wp_abilities_api_init', function () {
  wp_register_ability( 'demo/count-drafts', array(
    'label'               => 'Count drafts',
    'description'         => 'Counts draft posts of one post type.',
    'category'            => 'demo-site',
    'input_schema'        => array(
      'type'                 => 'object',
      'properties'           => array( 'post_type' => array( 'type' => 'string', 'enum' => array( 'post', 'page' ), 'default' => 'post' ) ),
      'additionalProperties' => false,
    ),
    'output_schema'       => array(
      'type'       => 'object',
      'properties' => array(
        'post_type' => array( 'type' => 'string' ),
        'drafts'    => array( 'type' => 'integer' ),
      ),
    ),
    'execute_callback'    => function ( $input ) {
      $type = sanitize_key( $input['post_type'] ?? 'post' );
      return array( 'post_type' => $type, 'drafts' => (int) wp_count_posts( $type )->draft );
    },
    'permission_callback' => function ( $input ) {
      $type = sanitize_key( $input['post_type'] ?? 'post' );
      if ( ! in_array( $type, array( 'post', 'page' ), true ) ) {
        return false;
      }
      $object = get_post_type_object( $type );
      return $object && current_user_can( $object->cap->edit_posts );
    },
    'meta'                => array(
      'show_in_rest' => true,
      'annotations'  => array( 'readonly' => true, 'destructive' => false, 'idempotent' => true ),
      'mcp'          => array( 'public' => true ),
    ),
  ) );
} );
REST, signed in as an AuthorRecorded before the permission fix; not rerun
# Read the definition
GET /wp-json/wp-abilities/v1/abilities/demo/count-drafts
→ "meta": { "annotations": { "readonly": true, … },
            "show_in_rest": true, "mcp": { "public": true },
            "public": false }
→ "targetHints": { "allow": ["GET"] }

# Run it: a read-only ability takes GET, input in the query string
GET /wp-json/wp-abilities/v1/abilities/demo/count-drafts/run?input[post_type]=page
→ {"post_type":"page","drafts":9}

# The same call as POST
POST …/demo/count-drafts/run  { "input": { "post_type": "page" } }
→ 405 {"code":"rest_ability_invalid_method",
       "message":"Read-only abilities require GET method."}

From PHP, wp_get_ability() returns the ability and execute() runs the same permission check and schema validation as the REST route. From an MCP client, the ability is one more tool: the Adapter's default server lists it because mcp.public is true, and a product server lists it only if its own rules allow.

Run over HTTP against the local test stack. The Author account and its Application Password existed only for this run and were deleted afterwards. These REST outputs were recorded when the example checked only edit_posts. With the corrected check shown here, an Author asking for page drafts should be refused, because Authors cannot edit pages; that has not been rerun.

Registration and exposure

Four Abilities API failures to diagnose

Some registration failures only emit debug notices; REST failures can return a structured error. Each case below was reproduced on the historical test site or read from core source on the same day.

Symptom, cause and check for four Abilities API failures
FailureWhat you seeWhyHow to catch it
Registered at the wrong momentThe ability simply does not exist. wp_has_ability() returns false and nothing is shown unless WP_DEBUG is on.wp_register_ability() called while a plugin file loads, or on init. Core refuses any call made outside the wp_abilities_api_init action, returns null and raises only a _doing_it_wrong notice.Register inside add_action( 'wp_abilities_api_init', … ). In our run, a file-scope registration of demo/too-early left wp_has_ability( 'demo/too-early' ) false while the hooked ability beside it worked.
Asked for too earlyCode that looks up an ability gets null, or finds that a module's helper functions are not loaded yet.The registry is built on first use, and it is not built at all before init: WP_Abilities_Registry::get_instance() returns null until then, and wp_abilities_api_init fires the first time something asks for the registry.Look abilities up from init onwards, through wp_get_ability() or wp_has_ability(), rather than from code that runs as the plugin loads.
Registered, but invisibleThe ability runs in PHP but the REST routes return 404, or an MCP client never lists it.REST exposure needs meta.show_in_rest. The MCP Adapter keeps every ability private until meta.public or meta.mcp.public is true, and meta.mcp.public false hides it from MCP even when public is true.Read the ability back from /wp-json/wp-abilities/v1/abilities/<name> and look at meta. Ours came back with public: false added and mcp.public: true, so the Adapter would list it.
Right ability, wrong HTTP methodA REST call answers 405 with rest_ability_invalid_method.The run route follows the annotations: read-only abilities use GET, regular writes use POST, and destructive abilities use DELETE. Inspect the definition's targetHints for the allowed method and input shape.The definition's targetHints lists the allowed method. Our POST to a read-only ability returned "Read-only abilities require GET method."

From ability to AI client

How abilities reach an MCP client

The Abilities API does not speak MCP itself. A server translates, and the server decides which abilities a client sees.

  • The MCP Adapter's default server

    Serves /wp-json/mcp/mcp-adapter-default-server and exposes public abilities through three meta-tools: mcp-adapter-discover-abilities, mcp-adapter-get-ability-info and mcp-adapter-execute-ability. Sign-in is WordPress authentication, usually an Application Password.

    MCP Adapter vs GoBuildWP
  • A product server such as GoBuildWP

    GoBuildWP registers its own abilities on wp_abilities_api_init at priority 20 and serves them from /wp-json/mcp/wppilot. Before listing or running one it also applies the safety profile, per-ability switches, confirmation for destructive calls and rate limits, and records supported writes for undo.

    How WordPress MCP works
  • Your own abilities, through either

    An ability you register with mcp.public is picked up by the Adapter as it is. GoBuildWP lists abilities other plugins register too, grouped under their own module on its Abilities screen, where each can be switched off.

    GoBuildWP ability reference WordPress MCP for developers

Checked 29 September 2026

Plugins that register their own abilities

Vendor-native abilities are how most plugins will reach AI clients. These are the ones we could confirm from each vendor's own page.

Plugins that register abilities, what they cover and their status
PluginWhat it registersStatus
WooCommerceProduct and order abilities, 7 in the developer docs, served through the MCP AdapterDeveloper preview behind a feature flag
BricksBuilder abilities for MCP clients, plus an optional skills packageExperimental since Bricks 2.4
Yoast SEO5 abilities: three analysis scores, and reading and updating a post's SEO dataShipped
Ninja Forms29 permission-checked abilities for forms, submissions and settings; the 3 destructive ones stay off until an admin opts inSince 3.14.0
Gravity FormsForms, entries and feeds as abilities behind its own MCP server, signed in with an Application PasswordSince Gravity Forms 3.1
All in One SEOAbilities API support and a built-in MCP server to read, audit and update SEO dataShipped
GoBuildWP232 abilities in Free and 1,148 more in Pro, 1,380 in all, for core WordPress, builders, stores, forms, SEO and cachesShipped
REST namespace
wp-abilities/v1
Register on
wp_abilities_api_init
Categories on
wp_abilities_api_categories_init
Audit hook (7.1)
wp_ability_invoked

FAQ

WordPress Abilities API questions

Building for AI clients? See running a WordPress AI agent.

Which WordPress version has the Abilities API?

WordPress 6.9 and later; the API is in core, so no separate plugin is needed. WordPress 7.1 added input and output validation filters, the wp_ability_invoked action and typed coercion of REST input. GoBuildWP requires WordPress 6.9+ and PHP 8.0+.

Is the Abilities API only for AI?

No. An ability is a typed, permission-checked operation that PHP code, the REST API and the JavaScript client can call as well as an MCP server. AI clients are the most visible consumer because the schemas tell a model exactly what it may send.

How is the Abilities API different from the REST API?

The REST API is a set of resources and routes you design one by one. An ability is a named operation with a schema and a permission check, registered once and discoverable in one list. Core serves abilities that opt into meta.show_in_rest through the shared wp-abilities/v1 routes, so a client does not have to learn each plugin's endpoints.

Do abilities respect user capabilities?

Only through the permission callback you write. WordPress runs it with the current user before every execution, on every surface. An MCP server adds its own layer on top: GoBuildWP, for example, also checks the active safety profile and the per-ability switches before a call runs.

Do I need the MCP Adapter to use abilities from an AI client?

You need some MCP server. The Adapter is the WordPress project's generic one: its default server exposes public abilities through three meta-tools to discover, inspect and execute. Product plugins such as GoBuildWP run their own server on the same abilities with their own policy, logging and undo.

Put your abilities behind a policy.

Install GoBuildWP Free, connect a client in Read Only, and see your own abilities listed next to GoBuildWP's, each one switchable on the site.