WordPress

WordPress 7.1 wp_get_abilities(): Filtering Abilities by Category, Namespace and Metadata

By Anton Smolik · Aug 12, 2026 · 5 min read

Developer using wp_get_abilities() function on a WordPress site

WordPress 7.1 makes a small-looking but genuinely useful change to the Abilities API: wp_get_abilities() can now retrieve a specific subset of registered abilities instead of handing you the entire registry every time.

The function itself is not new. wp_get_abilities() arrived with the Abilities API in WordPress 6.9. Until now, though, its job was straightforward: return every registered WP_Ability instance. If a plugin needed only abilities from one category, one namespace, or with a particular metadata value, the filtering had to happen afterward in application code.

WordPress 7.1 changes that by adding an optional $args parameter:

$abilities = wp_get_abilities( $args );

The new query arguments cover three common ways of narrowing the registry:

category
namespace
meta

There are also two caller-level callbacks:

item_include_callback
result_callback

and two WordPress filters that plugins can use to influence the result globally:

wp_get_abilities_item_include
wp_get_abilities_result

This is more than a convenience function for shorter PHP. It gives developers a common way to discover functionality exposed by WordPress core, plugins, themes, automation systems and other integrations built on top of the Abilities API. WordPress itself describes abilities as standardized, machine-readable units of functionality with defined inputs, outputs, execution logic and permission handling.

Before looking at the new syntax, however, there is one distinction worth clearing up.

WordPress abilities are not the same thing as user capabilities

The names are similar enough to cause confusion.

Traditional WordPress capabilities are permissions such as:

edit_posts
manage_options
publish_posts
install_plugins

They answer questions like:

Is this user allowed to perform this action?

You normally work with them through functions such as:

current_user_can( 'manage_options' );

An Ability, in the Abilities API sense, represents a piece of functionality that a component exposes.

An ability might be called:

my-plugin/export-report
my-plugin/get-site-stats
woocommerce/create-order
core/read-settings

Each ability can describe its input, output, category and execution callback. It can also define a permission_callback that decides whether the current request is allowed to execute it.

So this:

wp_get_abilities()

is primarily about discovering registered functionality.

It is not a replacement for:

current_user_can()

and filtering an ability out of a result set is not a security mechanism.

That distinction matters when working with the new WordPress 7.1 filters.


What changed in wp_get_abilities() in WordPress 7.1?

Before WordPress 7.1, the function effectively worked like this:

$abilities = wp_get_abilities();

You received the complete collection of registered abilities.

If you wanted abilities belonging to a particular plugin, you had to retrieve everything and filter the result yourself.

For example:

$abilities = wp_get_abilities();

$plugin_abilities = array_filter(
	$abilities,
	static function ( WP_Ability $ability ) {
		return str_starts_with(
			$ability->get_name(),
			'my-plugin/'
		);
	}
);

That works, but every plugin ends up inventing its own query logic.

WordPress 7.1 moves that basic filtering into the API itself.

The equivalent becomes:

$abilities = wp_get_abilities(
	array(
		'namespace' => 'my-plugin',
	)
);

The new function signature in WordPress 7.1 is:

wp_get_abilities( array $args = array() ): array

Calling it without arguments still returns the registered abilities, preserving the existing usage:

$abilities = wp_get_abilities();

Existing code therefore does not have to be rewritten simply because WordPress 7.1 is installed. The $args parameter is optional.


Filtering abilities by category

Every registered ability belongs to a category.

If your plugin registers several groups of functionality, categories provide a clean way to organize and discover them.

Consider a plugin with abilities such as:

acme/get-report
acme/export-report
acme/get-customer
acme/update-customer

Some may belong to a reporting category while others belong to customer-management.

WordPress 7.1 lets you retrieve only one category:

$abilities = wp_get_abilities(
	array(
		'category' => 'reporting',
	)
);

Only abilities whose category exactly matches reporting will be returned.

A practical example might look like this:

$reporting_abilities = wp_get_abilities(
	array(
		'category' => 'reporting',
	)
);

foreach ( $reporting_abilities as $name => $ability ) {
	printf(
		'<p><strong>%s</strong>: %s</p>',
		esc_html( $ability->get_label() ),
		esc_html( $ability->get_description() )
	);
}

This becomes useful when building an administrative interface that needs to display only abilities relevant to one part of the product.

Instead of maintaining a separate list of supported operations, the interface can discover them directly from the registry.


Filtering abilities by namespace

For plugin developers, namespace will probably be one of the most immediately useful arguments.

Ability names follow a namespace/name format:

my-plugin/generate-report
my-plugin/export-csv
another-plugin/import-data
core/read-settings

To retrieve only abilities registered under my-plugin, use:

$abilities = wp_get_abilities(
	array(
		'namespace' => 'my-plugin',
	)
);

You do not need to include the trailing slash.

WordPress normalizes the value internally, so:

'namespace' => 'my-plugin'

is used to match names beginning with:

my-plugin/

The important point here is that this is a namespace-prefix check, not a loose text search.

That makes it useful for plugin interoperability.

Imagine an extension that integrates with WooCommerce functionality exposed through abilities. Instead of maintaining a hard-coded catalogue of every possible operation, the extension could inspect the abilities registered under that namespace:

$woocommerce_abilities = wp_get_abilities(
	array(
		'namespace' => 'woocommerce',
	)
);

The exact abilities available can then depend on the installed version and extensions running on that particular site.

That is one of the more interesting parts of the Abilities API in general: functionality becomes discoverable rather than something every integration has to know about in advance.


Filtering abilities by metadata

WordPress 7.1 also allows filtering against an ability's metadata.

For example:

$abilities = wp_get_abilities(
	array(
		'meta' => array(
			'show_in_rest' => true,
		),
	)
);

This returns abilities whose metadata contains the requested value.

Metadata queries can also contain nested arrays.

The implementation included for WordPress 7.1 explicitly supports structures such as:

$abilities = wp_get_abilities(
	array(
		'meta' => array(
			'mcp' => array(
				'public' => true,
			),
		),
	)
);

All supplied metadata conditions must match.

In other words, this is not an OR query.

Given:

'meta' => array(
	'show_in_rest' => true,
	'public'       => true,
)

an ability needs to satisfy both conditions to remain in the result.

The comparison is also strict. A boolean:

true

should not be treated as interchangeable with:

1

or:

'true'

when designing metadata you expect other code to query.

Consistent metadata types therefore become more important as plugins start using the registry for discovery.


Combining category, namespace and meta

The new arguments can be combined.

Suppose an application wants abilities that:

  • belong to the content category,

  • come from the core namespace,

  • and are exposed through REST.

The query can be written as:

$abilities = wp_get_abilities(
	array(
		'category'  => 'content',
		'namespace' => 'core',
		'meta'      => array(
			'show_in_rest' => true,
		),
	)
);

These arguments use AND logic.

An ability has to pass every supplied condition.

Conceptually, WordPress is doing something close to:

category matches
AND
namespace matches
AND
metadata matches

This makes wp_get_abilities() much more useful for discovery interfaces because the caller can describe what it wants rather than fetch the registry and reconstruct the query manually.


Using item_include_callback for custom filtering

Not every filtering rule fits into category, namespace or metadata.

WordPress 7.1 therefore adds:

item_include_callback

This callback runs once for each ability that has already passed the declarative filters.

For example:

$abilities = wp_get_abilities(
	array(
		'namespace' => 'my-plugin',

		'item_include_callback' => static function ( WP_Ability $ability ) {
			return str_contains(
				$ability->get_name(),
				'export'
			);
		},
	)
);

This first limits the registry to the my-plugin namespace and then keeps only abilities whose names contain export.

You could also inspect ability properties:

$abilities = wp_get_abilities(
	array(
		'item_include_callback' => static function ( WP_Ability $ability ) {
			return 'Reporting' === $ability->get_label();
		},
	)
);

The callback should return:

true

to keep the ability and:

false

to remove it.

This is especially useful when the query depends on application-specific logic that WordPress could not reasonably express as a standard $args parameter.

Don't turn item_include_callback into an authorization layer

There is a tempting mistake here.

You might write:

'item_include_callback' => static function ( WP_Ability $ability ) {
	return current_user_can( 'manage_options' );
}

That can be completely legitimate if the goal is to decide what an interface should display.

What it should not do is replace the ability's real execution permissions.

The Abilities API has a dedicated permission_callback for authorization. A filtered discovery result and an execution permission check solve different problems. WordPress explicitly includes permission callbacks as part of an ability's security model.

If an operation is sensitive, protect it where the ability executes.

Do not assume that hiding it from one wp_get_abilities() query makes it inaccessible elsewhere.


Transforming the result with result_callback

The other caller-level callback is:

result_callback

Unlike item_include_callback, this does not run against individual abilities during matching.

It receives the complete matched array after the per-item filtering has finished.

That makes it useful for operations such as:

  • sorting,

  • slicing,

  • limiting,

  • pagination-like behavior,

  • or other final transformations.

For example, you could sort abilities alphabetically and return only the first ten:

$abilities = wp_get_abilities(
	array(
		'result_callback' => static function ( array $abilities ) {

			uasort(
				$abilities,
				static function ( WP_Ability $a, WP_Ability $b ) {
					return strcasecmp(
						$a->get_label(),
						$b->get_label()
					);
				}
			);

			return array_slice(
				$abilities,
				0,
				10,
				true
			);
		},
	)
);

Notice the use of:

array_slice( ..., true )

to preserve the ability-name keys.

wp_get_abilities() returns abilities keyed by their registered ability names, which is useful when you want to address an item without rebuilding another lookup structure.


The complete wp_get_abilities() filtering pipeline

One detail I like about the WordPress 7.1 implementation is that the order of operations is explicitly defined.

The pipeline is:

category / namespace / meta
        ↓
item_include_callback
        ↓
wp_get_abilities_item_include
        ↓
result_callback
        ↓
wp_get_abilities_result

That order matters.

The declarative arguments narrow the initial set.

Then your caller-specific callback gets a chance to accept or reject individual abilities.

After that, WordPress fires an ecosystem-level filter for each remaining item.

Once item filtering is finished, the complete result passes through the caller's result_callback.

Finally, the global result filter runs.

WordPress performs the declarative filtering and per-item callback/filter stages inside the same loop over the registry rather than repeatedly walking the collection.

That is worth knowing when deciding where custom logic belongs.

Use:

item_include_callback

when the rule belongs to one particular call.

Use:

wp_get_abilities_item_include

when a plugin needs to affect ability discovery across the site.

Use:

result_callback

when one particular caller needs to sort or slice its result.

Use:

wp_get_abilities_result

when a plugin needs to modify the final result globally.


Using wp_get_abilities_item_include

The new global per-item filter looks like this conceptually:

add_filter(
	'wp_get_abilities_item_include',
	static function ( $include, $ability, $args ) {

		// Apply your rule.

		return $include;
	},
	10,
	3
);

Because this filter runs inside wp_get_abilities(), it can influence calls made by other components.

That makes it powerful.

It also means it should be used carefully.

A plugin that blindly removes abilities here can change what unrelated tools discover.

For example:

add_filter(
	'wp_get_abilities_item_include',
	static function ( $include, WP_Ability $ability, array $args ) {

		if ( str_starts_with( $ability->get_name(), 'internal-tools/' ) ) {
			return false;
		}

		return $include;
	},
	10,
	3
);

This would prevent internal-tools/* abilities from appearing through normal wp_get_abilities() discovery.

Whether that is desirable depends entirely on what your plugin is trying to accomplish.

The important architectural point is scope: a callback passed directly into wp_get_abilities() affects one call; a WordPress filter can affect everybody using that API.


Using wp_get_abilities_result

There is also a global filter for the final array:

add_filter(
	'wp_get_abilities_result',
	static function ( array $abilities, array $args ) {

		// Sort, limit or otherwise modify the final collection.

		return $abilities;
	},
	10,
	2
);

This runs after the caller's result_callback.

That gives WordPress plugins a final opportunity to shape the collection returned by wp_get_abilities().

Again, global modification should have a clear reason.

If you only need custom ordering in your own admin screen, this:

result_callback

is usually a cleaner choice than modifying every abilities query on the site.


A practical example: building an abilities explorer

A useful real-world application is a developer screen that lists abilities registered by a particular plugin.

Imagine a plugin settings page with a namespace selector.

You could retrieve abilities like this:

$namespace = 'my-plugin';

$abilities = wp_get_abilities(
	array(
		'namespace' => $namespace,

		'result_callback' => static function ( array $abilities ) {

			uasort(
				$abilities,
				static fn( WP_Ability $a, WP_Ability $b ) =>
					strcasecmp(
						$a->get_label(),
						$b->get_label()
					)
			);

			return $abilities;
		},
	)
);

And display them:

if ( empty( $abilities ) ) {
	echo '<p>No abilities registered.</p>';
	return;
}

echo '<table class="widefat striped">';
echo '<thead>';
echo '<tr>';
echo '<th>Name</th>';
echo '<th>Label</th>';
echo '<th>Category</th>';
echo '<th>Description</th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';

foreach ( $abilities as $name => $ability ) {

	echo '<tr>';

	printf(
		'<td><code>%s</code></td>',
		esc_html( $name )
	);

	printf(
		'<td>%s</td>',
		esc_html( $ability->get_label() )
	);

	printf(
		'<td><code>%s</code></td>',
		esc_html( $ability->get_category() )
	);

	printf(
		'<td>%s</td>',
		esc_html( $ability->get_description() )
	);

	echo '</tr>';
}

echo '</tbody>';
echo '</table>';

There is nothing especially complicated about this code.

That is precisely the point.

The registry already knows what functionality exists. WordPress 7.1 makes it easier to ask the registry a narrower question instead of copying its contents and repeatedly filtering them yourself.


Why this matters for plugin interoperability

One of the long-term advantages of the Abilities API is that WordPress components do not necessarily need intimate knowledge of each other's PHP implementation.

A plugin can expose:

analytics/get-summary

Another component can discover abilities under:

analytics/

inspect their definitions and decide what functionality is available on that site.

The Abilities API was introduced in WordPress 6.9 specifically as a standardized registry for functionality that can be discovered and used across PHP, REST-based integrations, automation tools and other interfaces. WordPress 7.1 continues that direction by making the registry queryable instead of treating discovery as an all-or-nothing operation.

This becomes increasingly useful as a WordPress installation grows.

A typical production site might have:

WordPress core
WooCommerce
SEO plugin
CRM integration
custom editorial plugin
analytics plugin
automation layer
internal business tools

If several of those components expose abilities, retrieving the whole registry whenever one interface needs three operations becomes unnecessarily clumsy.

Namespace, category and metadata filters give developers a shared vocabulary for narrowing those results.


wp_get_abilities() and REST-exposed functionality

Abilities may also be exposed through the WordPress REST API when configured appropriately.

The Abilities API includes REST endpoints for discovering abilities and executing those that are exposed through REST, while ability registration can use metadata such as:

'meta' => array(
	'show_in_rest' => true,
)

to make that functionality available through the REST layer.

That makes this WordPress 7.1 query useful:

$rest_abilities = wp_get_abilities(
	array(
		'meta' => array(
			'show_in_rest' => true,
		),
	)
);

For developer tooling, this can be far cleaner than retrieving every ability and checking metadata one object at a time.


What about AI and automation?

The Abilities API is also part of WordPress's broader work around machine-readable functionality.

That does not mean wp_get_abilities() is an "AI function."

It isn't.

The API is useful on its own for plugins, admin interfaces and system integrations.

What makes it relevant to automation and AI tooling is discoverability: software can inspect a site's available abilities rather than relying entirely on hard-coded assumptions about what WordPress or a plugin can do. WordPress's own Abilities API documentation explicitly identifies automation systems and AI integrations as use cases for the registry.

Filtering makes that discovery more practical.

A tool may not want every operation exposed by every plugin.

It may want only:

wp_get_abilities(
	array(
		'category' => 'content',
	)
);

or only abilities from a known integration:

wp_get_abilities(
	array(
		'namespace' => 'my-integration',
	)
);

or only abilities carrying a particular metadata flag.

That is a much cleaner foundation for building controlled integrations.


Performance: what the new filtering does and does not improve

It would be easy to oversell this change as a major performance optimization.

That is not really what it is.

The current WordPress 7.1 implementation retrieves the registered abilities from the in-memory registry and then applies the filtering pipeline in PHP. The declarative conditions and per-item inclusion logic are handled in a single pass through that collection.

There is no database query being optimized here.

So this:

wp_get_abilities(
	array(
		'namespace' => 'my-plugin',
	)
);

should not be thought of as the equivalent of adding an SQL WHERE clause to an expensive database query.

The bigger improvement is architectural:

less duplicated filtering code
more predictable discovery
a shared API between plugins
consistent extension points
clearer intent

Those things matter a lot more than saving a tiny PHP loop on most sites.

If your registry somehow contains a very large number of abilities and your callback performs expensive operations for each one, you still need to think about the work that callback is doing.

Do not perform database queries, remote HTTP requests or other expensive operations inside an item_include_callback unless you have a very good reason.


Backward compatibility with WordPress 6.9 and 7.0

This deserves attention if you maintain a public plugin.

The Abilities API itself arrived in WordPress 6.9.

That means this check:

function_exists( 'wp_get_abilities' )

tells you whether the API exists.

It does not tell you whether the new 7.1 $args parameter is available.

If your plugin supports WordPress 6.9 or 7.0, do not blindly call:

wp_get_abilities(
	array(
		'namespace' => 'my-plugin',
	)
);

on those installations.

One straightforward compatibility strategy is:

global $wp_version;

if ( version_compare( $wp_version, '7.1', '>=' ) ) {

	$abilities = wp_get_abilities(
		array(
			'namespace' => 'my-plugin',
		)
	);

} else {

	$abilities = wp_get_abilities();

	$abilities = array_filter(
		$abilities,
		static function ( WP_Ability $ability ) {
			return str_starts_with(
				$ability->get_name(),
				'my-plugin/'
			);
		}
	);
}

If you only support WordPress 7.1 and newer, none of that fallback code is necessary.

For many plugins, increasing the minimum supported WordPress version solely to use this convenience API probably will not make sense immediately.

Keeping a small compatibility layer may be the better trade-off.


Common mistakes to avoid

1. Treating abilities like roles or WordPress capabilities

This is probably the biggest conceptual mistake.

An ability describes functionality.

A capability such as:

manage_options

describes permission.

Abilities can use WordPress capabilities inside their permission callbacks, but the two systems are not interchangeable.


2. Using discovery filtering as security

This:

wp_get_abilities(
	array(
		'item_include_callback' => ...
	)
);

controls what appears in that result.

It does not automatically secure the underlying operation.

Sensitive abilities still need proper permission handling.


3. Forgetting that combined filters use AND logic

This:

array(
	'category'  => 'content',
	'namespace' => 'my-plugin',
)

does not mean:

content category OR my-plugin namespace

It means both conditions must match.


4. Using inconsistent metadata types

If one plugin version registers:

'public' => true

and another registers:

'public' => 'true'

you are creating unnecessary ambiguity for code that filters that metadata.

Keep metadata structures stable and predictable.


5. Using global filters for local problems

If only one screen needs the result sorted, use:

result_callback

rather than:

wp_get_abilities_result

The first affects your query.

The second can affect other code.

That difference becomes important on large sites where several plugins depend on the same API.


6. Assuming filtering means database optimization

Abilities live in a registry.

wp_get_abilities() is not running a SQL query for every call.

Use the new arguments because they make your code cleaner and interoperability better, not because you expect a dramatic database performance gain.


Should existing plugins migrate to the new API immediately?

If you already have code like this:

$abilities = wp_get_abilities();

$abilities = array_filter(
	$abilities,
	$callback
);

and your minimum requirement becomes WordPress 7.1, moving straightforward filtering into wp_get_abilities() is sensible.

For example:

$abilities = wp_get_abilities(
	array(
		'namespace' => 'my-plugin',
	)
);

communicates intent more clearly than a custom anonymous callback whose only purpose is checking the namespace.

The same applies to category and metadata filtering.

I would not rewrite complex working code merely to say that it uses the newest API, though.

If your filter involves domain-specific business logic, item_include_callback may improve organization but not fundamentally change the code.

And if your plugin must continue supporting WordPress 6.9 or 7.0, compatibility code may outweigh the benefit of migrating immediately.

Use the new API where it removes real complexity.


A compact example putting everything together

Here is a more complete query:

$abilities = wp_get_abilities(
	array(
		'category'  => 'content',
		'namespace' => 'my-plugin',

		'meta' => array(
			'show_in_rest' => true,
		),

		'item_include_callback' => static function ( WP_Ability $ability ) {

			// Additional caller-specific filtering.
			return ! str_contains(
				$ability->get_name(),
				'internal'
			);
		},

		'result_callback' => static function ( array $abilities ) {

			uasort(
				$abilities,
				static fn( WP_Ability $a, WP_Ability $b ) =>
					strcasecmp(
						$a->get_label(),
						$b->get_label()
					)
			);

			return $abilities;
		},
	)
);

The request now says quite clearly what the caller expects:

content abilities
from my-plugin
available through REST
excluding internal operations
sorted by label

Before WordPress 7.1, most of that intent would have lived in separate filtering code after retrieving the registry.

That is the practical improvement.


Final thoughts

wp_get_abilities() is not the flashiest WordPress 7.1 change, and that is probably why it is worth paying attention to.

Infrastructure improvements often look boring in isolation.

An optional $args array does not change what WordPress looks like in the browser. It does not add a new editor panel or redesign the admin.

What it does is make the Abilities API easier to use as an actual registry.

Instead of:

get everything
→ loop over everything
→ inspect everything
→ build your own filtering conventions

developers can increasingly write:

wp_get_abilities(
	array(
		'namespace' => 'my-plugin',
	)
);

and describe what they need through a common WordPress API.

For small plugins, that mostly means cleaner code.

For larger WordPress installations, automation systems and plugins that need to discover functionality exposed by other components, the benefit is more significant. A registry becomes much more useful once you can query it predictably.

The most useful additions in WordPress 7.1 are therefore not just category, namespace and meta.

The full improvement is the combination of declarative filters, caller-scoped callbacks and ecosystem-level WordPress hooks:

category
namespace
meta
item_include_callback
result_callback
wp_get_abilities_item_include
wp_get_abilities_result

That gives plugin developers several levels of control without forcing every project to invent its own ability-discovery layer.

If you're building against the Abilities API, wp_get_abilities() is one of the WordPress 7.1 changes worth adding to your toolbox.

Release note: WordPress 7.1 is currently in the Release Candidate stage and is scheduled for final release on August 19, 2026. The examples in this article reflect the WordPress 7.1 implementation available during the RC cycle.


If you need help with a WordPress or WooCommerce project, I work on custom development, performance, technical SEO and automation — particularly on sites where the standard plugin-and-page-builder approach has started to become the bottleneck.


Anton Smolik

Written by

Anton Smolik

WordPress & AI-automation specialist with 10+ years of deep platform expertise — building fast, findable sites through performance, technical SEO, custom plugins and AI workflows. Based in Barcelona.

Working on something like this? I take on WordPress, WooCommerce, performance and AI-automation projects as a freelancer.

Get a free site audit