Real Usage

Combining the AI Client SDK with the MCP Adapter

⏱ 17 min

Every other lesson in this course has been about one direction: your own WordPress code calling out to an AI provider. Course 1 also covered the opposite direction: an external AI agent calling into WordPress through an ability, exposed via the MCP Adapter. Those two directions aren’t mutually exclusive, and the most useful pattern in this whole course is where they meet: an ability’s execute_callback that, when triggered by an external agent over MCP, itself calls out to an LLM using the PHP AI Client SDK to do real work before returning a result. This lesson builds exactly that, end to end.

What you'll learn in this lesson
Where the two directions actually meet
Inside a single execute_callback, not as two separate features.
Building an ability that does AI work internally
A summarize-post ability that calls wp_ai_client_prompt() from inside its own execute_callback.
Keeping the permission model straight
Who is allowed to trigger the ability, versus which provider is configured to service the AI call it makes.
Why this pattern matters
It lets an external agent request real content work without needing its own AI provider credentials.
Prerequisites

Course 1’s Abilities API lesson (wp_register_ability(), permission_callback, execute_callback, meta.mcp.public), and Lessons 1 through 7 of this course. If you’ve also taken Courses 2 through 4, the MCP server setup steps there apply directly here, but they’re not required to follow this lesson.

Step 1: Recognize where the two directions meet

An ability’s execute_callback is just a PHP callback, whatever a normal WordPress function can do, an ability can do. Nothing about the Abilities API restricts what runs inside it to reading or writing WordPress data directly. Calling wp_ai_client_prompt() from inside execute_callback is no different, architecturally, from calling get_posts() from inside it, it’s simply what the callback does when it runs:

External MCP agent  --(calls ability over MCP)-->  execute_callback
                                                          |
                                                          v
                                                 wp_ai_client_prompt()->generate_text()
                                                          |
                                                          v
                                                  Configured provider (Claude/GPT/Gemini)

The external agent triggering the ability doesn’t need its own AI provider credentials, doesn’t even need to know an LLM call happens inside the ability at all. As far as that agent is concerned, it called an ability named my-plugin/summarize-post and got a summary back. What happened inside is your site’s own concern.

Step 2: Register the ability

This follows the exact shape Course 1 covered, registered on wp_abilities_api_init, with a permission_callback gating who can trigger it and an execute_callback doing the actual work:

// File: my-plugin/class-summarize-ability.php
add_action( 'wp_abilities_api_init', function () {
	wp_register_ability(
		'my-plugin/summarize-post',
		array(
			'label'               => __( 'Summarize post', 'my-plugin' ),
			'description'         => __( 'Generates a short summary of a published post by its ID.', 'my-plugin' ),
			'category'            => 'content',
			'input_schema'        => array(
				'type'       => 'object',
				'properties' => array(
					'post_id' => array( 'type' => 'integer' ),
				),
				'required'   => array( 'post_id' ),
			),
			'output_schema'       => array(
				'type'       => 'object',
				'properties' => array(
					'summary' => array( 'type' => 'string' ),
				),
			),
			'permission_callback' => function ( $input ) {
				return current_user_can( 'edit_post', $input['post_id'] );
			},
			'execute_callback'    => 'my_plugin_summarize_post_ability',
			'meta'                => array(
				'annotations' => array( 'readonly' => true ),
				'mcp'         => array( 'public' => true ),
			),
		)
	);
} );

meta.mcp.public is set to true here, deliberately, this is the field that decides whether an external MCP client can reach this ability at all, and for an ability whose whole purpose is being triggered by an external agent, that’s the point.

Step 3: The execute_callback, where both SDKs meet

// File: my-plugin/class-summarize-ability.php
function my_plugin_summarize_post_ability( $input ) {
	$post = get_post( $input['post_id'] );

	if ( ! $post || 'publish' !== $post->post_status ) {
		return new WP_Error( 'invalid_post', 'Post not found or not published.' );
	}

	try {
		$result = wp_ai_client_prompt()
			->with_text( wp_strip_all_tags( $post->post_content ) )
			->using_system_instruction( 'Summarize this WordPress post in three sentences or fewer.' )
			->using_temperature( 0.3 )
			->using_model_preference( 'claude-sonnet-4-5', 'gemini-3-pro-preview', 'gpt-5.1' )
			->generate_text();

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return array( 'summary' => $result );
	} catch ( Throwable $e ) {
		return new WP_Error( 'summary_failed', $e->getMessage() );
	}
}

This is, on purpose, almost identical to Lesson 7’s meta box handler. The permission check, the error handling, the builder chain, all the same techniques from earlier in this course. The only real difference is who’s calling it: a button click inside wp-admin in Lesson 7, an external MCP agent here. The execute_callback itself doesn’t need to know or care which.

Step 4: Keep the two permission models straight

There are two entirely separate gates here, and it’s worth being precise about which is which:

  • permission_callback decides whether the calling user (whoever the MCP Adapter has authenticated as, on behalf of the external agent) is allowed to trigger this ability for this specific post. This is a WordPress capability check, same as any other ability.
  • The configured AI provider decides whether the execute_callback’s internal call to wp_ai_client_prompt() can succeed at all. This has nothing to do with the calling agent’s permissions, it’s whether your site has a working provider configured, exactly as covered in Lessons 1 and 5.

An external agent with full permission to trigger this ability still gets a WP_Error back if your site’s provider plugin has no working API key. That’s expected and correct, permission to ask isn’t the same thing as the site being able to deliver.

Don't let an ability make unbounded AI calls on someone else's behalf

Because an ability triggered over MCP can be called repeatedly by an automated agent, not just by a human clicking a button once, it’s worth thinking about volume here differently than in Lesson 7’s admin-only feature. Consider rate-limiting or caching summaries per post (a transient keyed on post ID and content hash works well) so a misbehaving or overly persistent external agent can’t drive unbounded API spend against your configured provider.

Test it: call the ability directly, then confirm it’s MCP-visible

First, confirm the ability itself works, independent of any MCP client:

wp-env run cli wp eval '
$result = my_plugin_summarize_post_ability( array( "post_id" => 1 ) );
var_dump( $result );
'

You should see an array with a summary key holding real generated text, or a WP_Error if post 1 isn’t published or no provider is configured.

Then, with an MCP server plugin connected (Courses 2 through 4 cover choosing and setting one up), confirm the ability is actually listed as an available tool from an external client, an AI assistant like Claude connected over MCP should be able to call my-plugin/summarize-post with a post ID and get a real summary back, generated by whichever provider your site has configured, without the assistant’s own model ever seeing your site’s API key.

Recap

An ability’s execute_callback is ordinary PHP, and there’s no architectural reason it can’t call out to an LLM using the AI Client SDK while it’s servicing a request that came in from an external MCP agent. The permission model for triggering the ability (permission_callback, meta.mcp.public) stays entirely separate from whether the internal AI call itself succeeds (a configured provider, from Lessons 1 and 5). This pattern, an external agent’s request triggering your own site’s AI-powered logic without ever handling your provider credentials directly, is the real payoff of having built both directions in this track.

Resources & further reading

← Adding AI Features to Your Own Plugin Finish ✓