AI tools like Claude, Cursor, and ChatGPT can now interact directly with your WordPress site, but only if your site knows how to describe what it can do.
That’s exactly what the WordPress Abilities API solves.
It lets developers expose WordPress functionality as reusable, self-describing abilities that can be used by PHP, JavaScript, the REST API, and AI tools through the Model Context Protocol (MCP).
Here’s what this guide walks you through:
- Understand what the Abilities API is, in simple words
- Create your first custom ability, step by step: Get Posts by Post Type
- Test that ability two different ways
- Connect it to Claude Desktop using the official MCP Adapter
- Build two more real abilities: one that adds missing descriptions to tags and categories, and one that gives you a full site health report.
Every piece of code in this guide is real and tested. Let’s start from zero.
What Is the WordPress Abilities API?
The WordPress Abilities API is a feature introduced in WordPress 6.9 that gives plugins, themes, and WordPress core a standard way to describe what they can do.
Before understanding the API, let’s first understand what an ability is.
Think of an ability as a single action that your WordPress site can perform. For example, getting a list of posts, publishing a page, generating a maintenance report, or updating a WooCommerce product. These actions already exist in WordPress, but until now, there wasn’t a standard way to describe them so that other plugins, applications, or AI tools could easily discover and use them.
The Abilities API solves this problem by letting developers register each action as an ability. Along with the code that performs the action, you also describe what the ability does, what information it needs, what it returns, and who is allowed to execute it.
Every ability includes a few basic pieces of information:
| Part | What it is | In our example |
|---|---|---|
| Name | A unique identifier for the ability | wpvibes/get-posts |
| Description | Explains what the ability does | Returns a list of posts filtered by post type, status, and limit |
| Input | The information the ability needs | post_type, status, and limit |
| Output | The information the ability returns | A list of posts with ID, title, status, date, and link |
| Permission | Who is allowed to run the ability | Users who can edit posts |
For example, suppose you create an ability called wpvibes/get-posts. Instead of another plugin or AI tool needing to understand your plugin’s internal code, it simply looks at the ability’s definition to learn what inputs it accepts, what output it returns, and whether the current user has permission to run it.
The actual PHP code that performs the work still lives inside your plugin. The Abilities API simply provides a standard way to describe that functionality so it can be discovered and used consistently across WordPress.
Now that you understand what an ability is and what information it contains, let’s build one from scratch.
Prerequisites
Before writing any code, make sure you have:
- WordPress 6.9 or later (the Abilities API has been in core since 6.9; 7.0+ is recommended)
- A local development site (LocalWP, WordPress Studio, or similar)
- WP-CLI installed and working (needed for testing and for the Claude connection later)
- Claude Desktop installed (the free plan is sufficient for this guide)
- A code editor and basic PHP knowledge
Notes: Do everything in this guide on a local or staging site first, never directly on a live production site. The MCP Adapter is still a young project (v0.5.0 at the time of writing) and is evolving quickly. Always check the official documentation if something looks different from what’s shown here.
With those ready, there’s one more thing to set up before writing any actual ability code: a place for that code to live.
Create a Plugin for Your Code
All the code in this guide lives inside one small custom plugin. Here’s how to set it up:
- Create a new folder and file at
wp-content/plugins/wpvibes-abilities/wpvibes-abilities.php. - Paste in this basic plugin header:
<?php
/**
* Plugin Name: WPVibes Abilities Demo
* Description: Custom abilities for the Abilities API tutorial.
* Version: 1.0.0
* Author: WPVibes
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
- Activate the plugin from wp-admin → Plugins.
- It does nothing yet, we’ll add abilities to it one at a time.

Important: Never put ability registration code directly in your theme’s functions.php on a real project. Abilities are functionality, not design, they belong in a plugin, so they keep working even if the theme changes.
Step 1: Create Your First Ability, “Get Posts by Post Type”
Now that you understand what an ability is, it’s time to create one. In this step, we’ll build a simple wpvibes/get-posts ability that returns a list of posts based on the post type, status, and limit you specify.
Step 1.1: Register an ability category
Every ability belongs to a category, this helps tools group and display abilities. Add this to wpvibes-abilities.php:
/**
* Step 1.1: Register a category for our abilities.
*/
add_action( 'wp_abilities_api_categories_init', 'wpvibes_register_ability_category' );
function wpvibes_register_ability_category() {
wp_register_ability_category(
'wpvibes-tools',
array(
'label' => __( 'WPVibes Tools', 'wpvibes-abilities' ),
'description' => __( 'Demo abilities from the WPVibes tutorial.', 'wpvibes-abilities' ),
)
);
}
Notes: Categories are registered on their own hook (wp_abilities_api_categories_init), while abilities are registered on a different one (wp_abilities_api_init). Mixing these two up is one of the most common first-time mistakes. If your ability doesn’t appear anywhere, check the hooks first.
Step 1.2: Register the ability
With the category in place, you can now register your first ability. Register the wpvibes/get-posts ability by adding the following code below your category registration.
/**
* Step 1.2: Register the "Get Posts" ability.
*/
add_action( 'wp_abilities_api_init', 'wpvibes_register_get_posts_ability' );
function wpvibes_register_get_posts_ability() {
wp_register_ability(
'wpvibes/get-posts',
array(
'label' => __( 'Get Posts', 'wpvibes-abilities' ),
'description' => __( 'Returns a list of posts filtered by post type, status, and limit.', 'wpvibes-abilities' ),
'category' => 'wpvibes-tools',
// What data this ability ACCEPTS.
'input_schema' => array(
'type' => 'object',
'properties' => array(
'post_type' => array(
'type' => 'string',
'description' => 'The post type to fetch. Example: post, page, product.',
'default' => 'post',
),
'status' => array(
'type' => 'string',
'description' => 'Post status to filter by.',
'enum' => array( 'publish', 'draft', 'pending', 'future' ),
'default' => 'publish',
),
'limit' => array(
'type' => 'integer',
'description' => 'How many posts to return (1-20).',
'default' => 5,
'minimum' => 1,
'maximum' => 20,
),
),
),
// What data this ability RETURNS.
'output_schema' => array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'id' => array( 'type' => 'integer' ),
'title' => array( 'type' => 'string' ),
'status' => array( 'type' => 'string' ),
'date' => array( 'type' => 'string' ),
'link' => array( 'type' => 'string' ),
),
),
),
// The actual logic.
'execute_callback' => 'wpvibes_get_posts_callback',
// Who can run it.
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
// Make it visible to AI agents through MCP (used later in this guide).
'meta' => array(
'mcp' => array(
'public' => true,
),
),
)
);
}
Understanding the Registration
Although the registration looks long, most of it is simply describing the ability.
The important parts are:
'wpvibes/get-posts': the unique name. Always use your own namespace (your plugin or company name) so it never conflicts with other plugins.input_schema: this is JSON Schema format. It tells any consumer, human, tool, or AI, “you can send mepost_type,status, andlimit, and here are the rules for each.” Notice theenumfor status, the AI now knows exactly which values are valid, without guessing.output_schema: describes what comes back. This lets AI tools understand your response without any extra explanation.permission_callback: the security gate. Here we require theedit_postscapability, so only users who can edit posts may run this ability.meta.mcp.public => true: this single line is what will later make the ability visible to Claude through the MCP Adapter.
Important: Never use 'permission_callback' => '__return_true' in production code. Every ability must have a real permission check. When AI agents can discover and call your abilities, the permission callback is your main line of defense.
Step 1.3: Write the execute callback
Registering an ability only describes what it does.
The actual work happens inside the execute callback.
Whenever someone runs wpvibes/get-posts, WordPress calls this function automatically. It receives the validated input from the schema, performs the requested task, and returns the result.
Add the following function below your ability registration:
/**
* Step 1.3: The logic that runs when the ability is executed.
*
* @param array $input Validated input from the schema.
* @return array|WP_Error
*/
function wpvibes_get_posts_callback( $input ) {
// Sanitize inputs (yes, even though the schema already validates them).
$post_type = isset( $input['post_type'] ) ? sanitize_key( $input['post_type'] ) : 'post';
$status = isset( $input['status'] ) ? sanitize_key( $input['status'] ) : 'publish';
$limit = isset( $input['limit'] ) ? absint( $input['limit'] ) : 5;
// Check the post type actually exists on this site.
if ( ! post_type_exists( $post_type ) ) {
return new WP_Error(
'invalid_post_type',
sprintf( 'The post type "%s" does not exist on this site.', $post_type )
);
}
$posts = get_posts(
array(
'post_type' => $post_type,
'post_status' => $status,
'posts_per_page' => min( $limit, 20 ),
)
);
// Build the response to match our output schema.
$results = array();
foreach ( $posts as $post ) {
$results[] = array(
'id' => $post->ID,
'title' => get_the_title( $post ),
'status' => $post->post_status,
'date' => get_the_date( 'Y-m-d', $post ),
'link' => get_permalink( $post ),
);
}
return $results;
}
What This Callback Does
- Sanitize inputs anyway. The schema validates types, but defense in depth is a WordPress best practice.
- Validate important conditions. Before querying posts, we check that the requested post type actually exists.
- Perform the requested task. We call
get_posts()using the values supplied by the user. - Return structured data. Instead of returning raw WordPress objects, we build a clean response that matches the
output_schemawe defined earlier.
If something goes wrong, return an WP_Error object instead of an empty response. This gives applications and AI tools a clear explanation of what failed.
Congratulations! You’ve successfully created and registered your first WordPress ability. Before we connect it to Claude, it’s a good idea to verify that everything works correctly. We’ll test the ability in two ways:
- Using PHP with WP-CLI to confirm the ability executes correctly.
- Using the official WordPress AI plugin to test it through a visual interface.
Once both tests are successful, we’ll connect the ability to Claude using the MCP Adapter.
Step 2: Test the Ability with the AI Plugin
WP-CLI is fast and convenient for developers, but sometimes it’s easier to test an ability visually.
The official AI plugin in WordPress includes an Abilities Explorer that lets you discover registered abilities, provide input values, and view the response, all from within the WordPress admin area.
- Go to Plugins → Add New.
- Search for AI, then install and activate the plugin.
- Open Settings → AI.
- Turn on Enable AI at the top of the page.
- Open the Admin Experiments tab and enable the Abilities Explorer feature.

Test Your Ability
- Go to Tools → Abilities Explorer.
- Find your
wpvibes/get-postsability and click Test.

3. Click Invoke Ability.
4. The Result section displays the data returned by your ability.
If everything is configured correctly, you should see the same output you received when testing with WP-CLI.

Step 3: Test the Ability with PHP (WP-CLI)
Before connecting any AI, always test the ability directly. If something breaks later during MCP setup, this test tells you whether the problem is in your ability or in the connection.
The fastest way is WP-CLI. Run this from your site’s folder:
wp eval '
$ability = wp_get_ability( "wpvibes/get-posts" );
if ( ! $ability ) { echo "Ability not found!"; exit; }
$result = $ability->execute( array( "post_type" => "post", "limit" => 3 ) );
if ( is_wp_error( $result ) ) {
echo "Error: " . $result->get_error_message();
} else {
print_r( $result );
}
' --user=admin
Here, ‘ –user=[your-user-name].
You should see an array of your latest 3 posts with their IDs, titles, and links.

You can also execute the same ability anywhere in your own plugin or theme code:
add_action( 'admin_init', function () {
$ability = wp_get_ability( 'wpvibes/get-posts' );
if ( $ability ) {
$result = $ability->execute(
array(
'post_type' => 'page',
'limit' => 5,
)
);
echo '<pre>';
print_r( $result );
echo '</pre>';
exit;
}
} );
Tips: Always check the result with is_wp_error( $result ). A WP_Error object is truthy in PHP, so a simple if ( $result ) check will pass even when the ability failed. This is one of the most common bugs when working with abilities.
This “PHP first” test also shows the first real use case: any plugin can now call any other plugin’s abilities through one standard interface, with permissions checked automatically. No hard-coded function names, no tight coupling.
Step 4: Connect Your Ability to MCP(Claude) with the MCP Adapter
So far, you’ve created and tested your ability inside WordPress. Now it’s time to make it available to an AI assistant.
To do that, we’ll use the official WordPress MCP Adapter. Before setting it up, let’s quickly understand how everything fits together.
What Is the MCP Adapter?
The Model Context Protocol (MCP) is an open standard that lets AI applications communicate with external systems.
The WordPress MCP Adapter is an official plugin that acts as a bridge between your WordPress site and AI clients.
Instead of talking directly to your plugin, Claude communicates with the MCP Adapter. The adapter discovers all abilities you’ve registered with the Abilities API and exposes them as tools that Claude can use.
The overall flow looks like this:
Claude Desktop
↓ Sends the request
MCP Adapter
↓ Connects Claude to WordPress
Abilities API
↓ Finds and validates the ability
wpvibes/get-posts
↓ Runs the callback and returns posts
Step 4.1: Install the MCP Adapter
The adapter isn’t in the plugin directory yet. Install it from GitHub:
- Go to the MCP Adapter releases page
- Download the latest
mcp-adapter.zip - In wp-admin: Plugins → Add New → Upload Plugin → upload the zip → Activate
Verify it’s working with WP-CLI:
wp mcp-adapter list
You should see at least one server listed, the default one: mcp-adapter-default-server.
Notes: Only abilities with meta.mcp.public => true are exposed through the MCP Adapter. This prevents private abilities from becoming available accidentally.
Step 4.2: Connect Claude Desktop to your site
Open Claude Desktop’s configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json

Add the following configuration:
{
"mcpServers": {
"my-wordpress-site": {
"command": "npx",
"args": ["-y", "@automattic/mcp-wordpress-remote@latest"],
"env": {
"WP_API_URL": "https://your-site.com/wp-json/mcp/mcp-adapter-default-server",
"WP_API_USERNAME": "your-username",
"WP_API_PASSWORD": "xxxx xxxx xxxx xxxx"
}
}
}
}
Replace the following values with your own:
WP_API_URL: Replaceyour-site.comwith your own site’s actual address.WP_API_USERNAME: A WordPress user with permission to execute the ability.WP_API_PASSWORD: An Application Password for that user.
New to Application Passwords? Jump to the walkthrough at the end of this guide.
Save the file and fully restart Claude Desktop (quit and reopen, not just close the window).
Important: Claude runs abilities as whichever user’s credentials you provided above. Your permission callbacks are checked against that exact user. This is also why permission callbacks matter so much, they’re enforced on every single call, even from AI.
Step 4.3: Run Your Ability From Claude
Open a new chat in Claude Desktop. If everything is configured correctly, your WordPress server appears in the available tools list.

Now ask Claude:
“Show me my five latest draft posts.”
Claude will:
- Discover the available abilities.
- Select
wpvibes/get-posts. - Fill in the required input values.
- Ask for your approval.
- Execute the ability.
- Display the returned posts.
Try a few more:
- “How many pages does my site have?”
- “List my latest WooCommerce products” (if you have a
productpost type) - “Do I have any posts scheduled for the future?”
Notice something important: you never told Claude how to use your ability. The input schema did all the explaining. This is why we spent time writing good descriptions and schemas in Step 1, they aren’t just documentation, they’re instructions that AI reads and follows.
NOTE (security, read this twice):
- Never paste API credentials directly into an AI chat prompt. Credentials belong in the configuration file, where the MCP client handles them, the AI model itself never needs to see them.
- Use HTTPS only for remote connections.
- Create a dedicated user with only the capabilities your abilities need, don’t use your main admin account.
- Start with read-only abilities (like our Get Posts example). Add write or delete abilities only after you’re comfortable with how everything works.
- Application Passwords require “pretty” permalinks. If the MCP URL returns a 404, check Settings → Permalinks isn’t set to “Plain.”
Two More Real-World Abilities
Now that you know the full method, here are two more abilities to add to the same plugin. The steps are the same as Example 1, so we’ll only explain what’s different in each one.
Example 2: Add Missing Descriptions to Tags and Categories
Most WordPress sites accumulate tags and categories over time, but descriptions often get skipped when they’re created quickly. This matters more than it looks, category and tag archive pages often display that description publicly, and a missing one is a small but real SEO and content-quality gap. This example is also safe to try, since editing a description can always be changed again, nothing gets permanently deleted.
This example uses two abilities together: one that finds the problem, and one that fixes it.
Ability 2a: Find tags/categories with no description
What this code does
This ability only looks, it never changes anything. It checks whichever taxonomy you tell it to (tags or categories), goes through every single term in it, and picks out only the ones where description is empty. It hands back a simple list, just the ID, name, and taxonomy for each one. Since nothing gets written or deleted here, Claude will run this one immediately without hesitation, exactly like the read-only get-posts example from earlier in this guide.
add_action( 'wp_abilities_api_init', 'wpvibes_register_list_terms_without_description_ability' );
function wpvibes_register_list_terms_without_description_ability() {
wp_register_ability(
'wpvibes/list-terms-without-description',
array(
'label' => __( 'List Tags/Categories Without a Description', 'wpvibes-abilities' ),
'description' => __( 'Returns a list of tags or categories that currently have no description set.', 'wpvibes-abilities' ),
'category' => 'wpvibes-tools',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'taxonomy' => array(
'type' => 'string',
'description' => 'Which taxonomy to check.',
'enum' => array( 'category', 'post_tag' ),
'default' => 'category',
),
),
),
'output_schema' => array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'id' => array( 'type' => 'integer' ),
'name' => array( 'type' => 'string' ),
'taxonomy' => array( 'type' => 'string' ),
),
),
),
'execute_callback' => 'wpvibes_list_terms_without_description_callback',
'permission_callback' => function () {
return current_user_can( 'manage_categories' );
},
'meta' => array(
'mcp' => array( 'public' => true ),
),
)
);
}
function wpvibes_list_terms_without_description_callback( $input ) {
$taxonomy = isset( $input['taxonomy'] ) ? sanitize_key( $input['taxonomy'] ) : 'category';
$terms = get_terms(
array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
)
);
if ( is_wp_error( $terms ) ) {
return new WP_Error( 'invalid_taxonomy', 'That taxonomy does not exist.' );
}
$results = array();
foreach ( $terms as $term ) {
// Only include terms with an empty or missing description.
if ( empty( $term->description ) ) {
$results[] = array(
'id' => $term->term_id,
'name' => $term->name,
'taxonomy' => $term->taxonomy,
);
}
}
return $results;
}
Ability 2b: Save a new description for one tag or category
This ability handles one term at a time, and it needs three pieces of information to run: which term (term_id), which taxonomy it belongs to, and the new description text to save. It first double-checks that the term actually exists, so it doesn’t fail halfway through with a confusing error. Then it hands the new description off to WordPress’s own built-in function, wp_update_term(), which saves just that one field. The term’s name, its slug, everything else about it stays exactly as it was. Because this only changes a piece of text, and text can always be changed again.
add_action( 'wp_abilities_api_init', 'wpvibes_register_update_term_description_ability' );
function wpvibes_register_update_term_description_ability() {
wp_register_ability(
'wpvibes/update-term-description',
array(
'label' => __( 'Update Tag/Category Description', 'wpvibes-abilities' ),
'description' => __( 'Sets or updates the description for a specific tag or category.', 'wpvibes-abilities' ),
'category' => 'wpvibes-tools',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'term_id' => array(
'type' => 'integer',
'description' => 'The ID of the tag or category to update.',
),
'taxonomy' => array(
'type' => 'string',
'description' => 'Which taxonomy this term belongs to.',
'enum' => array( 'category', 'post_tag' ),
),
'description' => array(
'type' => 'string',
'description' => 'The new description text to set.',
),
),
'required' => array( 'term_id', 'taxonomy', 'description' ),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'term_id' => array( 'type' => 'integer' ),
'new_description' => array( 'type' => 'string' ),
),
),
'execute_callback' => 'wpvibes_update_term_description_callback',
'permission_callback' => function () {
return current_user_can( 'manage_categories' );
},
'meta' => array(
'mcp' => array( 'public' => true ),
),
)
);
}
function wpvibes_update_term_description_callback( $input ) {
$term_id = absint( $input['term_id'] );
$taxonomy = sanitize_key( $input['taxonomy'] );
$description = sanitize_textarea_field( $input['description'] );
$term = get_term( $term_id, $taxonomy );
if ( ! $term || is_wp_error( $term ) ) {
return new WP_Error( 'term_not_found', 'That tag or category does not exist.' );
}
$result = wp_update_term(
$term_id,
$taxonomy,
array( 'description' => $description )
);
if ( is_wp_error( $result ) ) {
return $result;
}
return array(
'term_id' => $term_id,
'new_description' => $description,
);
}
How you’d actually use it
- Ask Claude: “Which of my categories and tags don’t have a description?” Claude runs Ability 2a and shows you the list.
- Ask Claude: “Write good descriptions for those and add them.” Claude writes a description for each one itself, then runs Ability 2b once per category/tag to save it.
Tips: This is a safe one to try for real. If a description comes out wrong, just ask Claude to write it again with different wording. Nothing here is permanent.
Example 2: Site Maintenance Report (read-only, admin-only)
Imagine asking Claude every Monday morning: “give me my site’s status report,” and getting back pending updates, moderation queue, and health flags in one answer, instead of clicking through five admin screens.
add_action( 'wp_abilities_api_init', 'wpvibes_register_maintenance_report_ability' );
function wpvibes_register_maintenance_report_ability() {
wp_register_ability(
'wpvibes/maintenance-report',
array(
'label' => __( 'Site Maintenance Report', 'wpvibes-abilities' ),
'description' => __( 'Returns a status report: versions, pending updates, comment queues, and basic health flags.', 'wpvibes-abilities' ),
'category' => 'wpvibes-tools',
// No input needed — an empty object schema.
'input_schema' => array(
'type' => 'object',
'properties' => new stdClass(),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'environment' => array( 'type' => 'object' ),
'updates' => array( 'type' => 'object' ),
'content' => array( 'type' => 'object' ),
'health' => array( 'type' => 'object' ),
),
),
'execute_callback' => 'wpvibes_maintenance_report_callback',
// Stricter permission: admins only.
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
'meta' => array(
'mcp' => array( 'public' => true ),
),
)
);
}
function wpvibes_maintenance_report_callback( $input ) {
// Update counts need these includes outside of wp-admin context.
require_once ABSPATH . 'wp-admin/includes/update.php';
require_once ABSPATH . 'wp-admin/includes/plugin.php';
$update_data = wp_get_update_data();
$comment_counts = wp_count_comments();
$theme = wp_get_theme();
return array(
'environment' => array(
'wordpress_version' => get_bloginfo( 'version' ),
'php_version' => PHP_VERSION,
'active_theme' => $theme->get( 'Name' ) . ' ' . $theme->get( 'Version' ),
'site_url' => home_url(),
),
'updates' => array(
'total_pending' => (int) $update_data['counts']['total'],
'plugin_updates' => (int) $update_data['counts']['plugins'],
'theme_updates' => (int) $update_data['counts']['themes'],
'core_update' => (bool) $update_data['counts']['wordpress'],
),
'content' => array(
'comments_awaiting_moderation' => (int) $comment_counts->moderated,
'spam_comments_in_queue' => (int) $comment_counts->spam,
'draft_posts' => (int) wp_count_posts()->draft,
'scheduled_posts' => (int) wp_count_posts()->future,
),
'health' => array(
'https_active' => is_ssl(),
'debug_mode_on' => defined( 'WP_DEBUG' ) && WP_DEBUG,
),
);
}
What’s new here:
- Stricter permission:
manage_options(admins only). Version and update information is reconnaissance data; never expose it to low-privilege users. - Structured output: we return grouped data (
environment,updates,content,health), not a pre-formatted text report. This gives AI clients the flexibility to present the information however they choose - Keep callbacks fast: we deliberately skip expensive checks like folder sizes or database size. AI agents may call abilities often; a slow ability makes every conversation slow.
Once registered, you can ask Claude:
“Give me my site’s maintenance report and tell me what needs attention.”
Claude calls the ability, analyzes the returned data, and explains what requires attention, for example, outdated plugins, a growing spam queue, or debug mode being enabled.
Conclusion
You’ve now built your first WordPress ability, tested it, and connected it to Claude using the official MCP Adapter. From here, you can apply the same pattern to expose any WordPress functionality as a secure, reusable ability that works seamlessly with AI tools.
FAQ on WordPress Abilities API
What is the WordPress Abilities API?
It’s a core WordPress feature, added in version 6.9, that lets your site describe its capabilities in a standard format. Plugins, external tools, and AI agents can then discover and safely use those capabilities without custom, one-off integration code.
Do I need to understand MCP to use the Abilities API?
No. You can register and use abilities entirely in PHP, or expose them over the REST API, without touching MCP at all. MCP is only needed if you specifically want an AI agent like Claude to discover and call your ability.
Can I use an ability without connecting it to any AI tool?
Yes. Abilities work from PHP, the REST API, and JavaScript, independent of AI. Many abilities are built purely so other plugins or your own code can call them through one consistent, permission-checked interface.
Is the MCP Adapter part of WordPress core?
No, it’s a separate official plugin. WordPress deliberately keeps it outside core so abilities remain protocol-independent. If MCP is ever replaced by something newer, your registered abilities keep working unchanged.
What’s the difference between an ability and a REST API endpoint?
A REST endpoint is one specific way to reach functionality over HTTP. An ability is broader: register it once, and it becomes callable from PHP, REST, JavaScript, and MCP, all from a single definition.
Do abilities only work with Claude, or other AI tools too?
Any MCP-aware AI client works, including Claude Desktop, Claude Code, Cursor, and VS Code. The ability itself doesn’t know or care which AI is calling it, that’s the whole point of the standard.
Useful resources
- Abilities API: official Common APIs Handbook entry
- MCP Adapter on GitHub: docs, releases, and transport guides
- Model Context Protocol specification
How to Create a WordPress Application Password
- Go to Users → Profile (or Users → All Users → click your name).
- Scroll to Application Passwords.
- Type a name for it, something like “Claude MCP,” so you remember what it’s for.
- Click Add New Application Password.
- Copy the generated password immediately. WordPress shows it only once. If you lose it, you’ll need to generate a new one.
- Use this password, not your regular login password, wherever the guide asks for
WP_API_PASSWORD.





Leave a Reply