I had a client, a marketing agency running a bespoke WordPress platform for the companies it works with. Good shop, capable team, but the plugin stack was a mess. Every feature request meant getting three plugins to cooperate, a CRM, an email marketing tool and some custom analytics, and each one had its own idea of a public API. Some exposed global functions, some had class methods nobody had documented, and one expected you to write queries against its tables directly. My team spent hours reading source code just to make two plugins talk to each other. The hours racked up, and every plugin update felt like defusing a bomb.
\n\nFor years the standard answer, ours included, was to build adapter layers. A wrapper for Yoast, another for WooCommerce, another for Gravity Forms, each one translating that plugin’s interface into something our themes could call the same way every time. Every wrapper was its own small project. It held together until a plugin update changed a function signature and our code broke. After that we were maintaining a pile of shims and patching them release by release, which is a brittle answer to a real question: how do you reliably integrate functionality from plugins that were never built to work together?
\n\nThe WordPress Abilities API: a standard way to expose plugin features
\n\nThat is the problem the WordPress Abilities API is meant to solve. It is a new functional primitive arriving in WordPress 6.9, aimed at plugin interoperability. Instead of leaving other developers to reverse engineer your plugin, you publish what it can do. An ability is registered in one standard format, which makes it discoverable by other code and self-documenting for whoever reads it: here is what I can do, here is what I need, here is what I give you back.
\n\nThe idea came out of the Core AI team, but it is not limited to AI. It is about functional extensibility. A plugin author declares the specific things the plugin can do, such as creating a product, fetching user data or processing a payment, and each one carries explicit input and output definitions, so the registration doubles as documentation. Once an ability exists, it can be called from the REST API, from the Command Palette, or from something like the MCP Adapter later on, without a separate hand-written integration for each context.
\n\nIn practice that means fewer sites broken by an update, quicker development, and behaviour you can predict. You stop rummaging through global $woocommerce or guessing which obscure hook to use, because the Abilities API gives you an official contract to work against.
What registering an ability looks like
\n\nHere is a simplified version of registering and then executing an ability. It is conceptual, and the bbioon prefixes are there for illustration:
<?php\n/**\n * Register a custom ability.\n */\nfunction bbioon_register_custom_product_ability() {\n bbioon_ability_register( \'bbioon_create_product\', array(\n \'label\' => __( \'Create a New Product\', \'bbioon-domain\' ),\n \'description\' => __( \'Creates a new product with specified details.\', \'bbioon-domain\' ),\n \'args\' => array(\n \'product_name\' => array(\n \'type\' => \'string\',\n \'description\' => __( \'The name of the product.\', \'bbioon-domain\' ),\n \'required\' => true,\n ),\n \'price\' => array(\n \'type\' => \'float\',\n \'description\' => __( \'The price of the product.\', \'bbioon-domain\' ),\n \'required\' => true,\n ),\n \'status\' => array(\n \'type\' => \'string\',\n \'description\' => __( \'The product status (e.g., publish, draft).\', \'bbioon-domain\' ),\n \'default\' => \'draft\',\n ),\n ),\n \'returns\' => array(\n \'type\' => \'integer\',\n \'description\' => __( \'The ID of the newly created product.\', \'bbioon-domain\' ),\n ),\n \'callback\' => \'bbioon_callback_create_product\',\n \'authorization\'=> \'manage_products\', // WordPress capability.\n ) );\n}\nadd_action( \'bbioon_abilities_init\', \'bbioon_register_custom_product_ability\' );\n\n/**\n * Callback function for the \'create_product\' ability.\n */\nfunction bbioon_callback_create_product( $args ) {\n $product_id = wp_insert_post( array(\n \'post_title\' => sanitize_text_field( $args[\'product_name\'] ),\n \'post_type\' => \'product\', // Assuming WooCommerce \'product\' post type.\n \'post_status\' => sanitize_text_field( $args[\'status\'] ?? \'draft\' ),\n ) );\n\n if ( $product_id && ! is_wp_error( $product_id ) ) {\n update_post_meta( $product_id, \'\_price\', (float) $args[\'price\'] );\n update_post_meta( $product_id, \'\_regular_price\', (float) $args[\'price\'] );\n // Add more product metadata as needed.\n return $product_id;\n }\n\n return 0; // Or throw an error.\n}\n\n// Example of executing the ability programmatically:\n// if ( bbioon_ability_exists( \'bbioon_create_product\' ) ) {\n// $new_product_id = bbioon_ability_execute( \'bbioon_create_product\', array(\n// \'product_name\' => \'My New Awesome Gadget\',\n// \'price\' => 99.99,\n// \'status\' => \'publish\',\n// ) );\n//\n// if ( $new_product_id ) {\n// // Product created successfully.\n// }\n// }\n?>\n\nThe registration is the documentation. It states what bbioon_create_product takes and what it gives back, so calling bbioon_ability_execute with those arguments produces a result you can predict without reading the plugin’s source. Authorization is declared in the same place, tied to a WordPress capability, which keeps the permission check with the ability rather than with every caller.
What the Abilities API changes day to day
\n\nThe Abilities API moves plugin integration from an undocumented mess to a structured interface you can discover and read. That should make features inside WordPress more reliable and easier to use in the right context, and it takes away the job of maintaining a wrapper for every plugin you touch.
\n\nIf you are still writing a custom shim for every plugin interaction, this is worth looking at before 6.9 ships rather than after. WordPress 6.9 Release Candidate 2 is out, so you can register an ability or two against it and watch how the calls behave.
\n\nThis stuff gets complicated fast. If you are tired of debugging someone else’s mess and just want your site to work, drop my team a line. We have probably seen it before.
\n