AI support for Acorn — wraps laravel/ai and adds first-class integration with the WordPress Abilities API.
Roots is an independent open source org, supported only by developers like you. Your sponsorship funds WP Packages and the entire Roots ecosystem, and keeps them independent. Support us by purchasing Radicle or sponsoring us on GitHub — sponsors get access to our private Discord.
- PHP 8.4+
- WordPress 6.9+ (for Abilities API)
- Acorn 5.0+
composer require roots/acorn-aiPublish the config files:
# WordPress-specific config (abilities registration)
wp acorn vendor:publish --provider="Roots\AcornAi\AcornAiServiceProvider" --tag=ai-wordpress-config
# AI provider config (providers, API keys, defaults)
wp acorn vendor:publish --provider="Laravel\Ai\AiServiceProvider" --tag=ai-configAdd your provider API keys to .env:
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...API keys configured through laravel/ai are automatically passed to WordPress AI provider plugins via environment variables. This means you don't need to enter keys on the WordPress Connectors settings page, and your keys are never stored in wp_options.
Supported providers: OpenAI, Anthropic, and Gemini (Google).
roots/acorn-ai includes laravel/ai and makes it available inside WordPress via Acorn's container. You get the full laravel/ai feature set: agents, tools, embeddings, image generation, audio, transcription, and reranking — all configured through config/ai.php (published from laravel/ai).
wp acorn make:agent SupportAgentwp acorn make:tool SearchPostsRefer to the laravel/ai documentation for full usage of agents, tools, structured output, and providers.
The WordPress Abilities API (WordPress 6.9+) provides a standardized way to define capabilities that AI agents can invoke — via REST API (wp-abilities/v1/abilities/{name}/run) and the WordPress MCP Adapter.
roots/acorn-ai wraps ability registration so that your ability classes are resolved through Laravel's service container, giving you full dependency injection.
wp acorn make:ability CreatePostThis generates app/Ai/Abilities/CreatePostAbility.php:
namespace App\Ai\Abilities;
use Roots\AcornAi\Abilities\Ability;
class CreatePostAbility extends Ability
{
public function __construct(private PostRepository $posts) {}
public function label(): string
{
return 'Create Post';
}
public function description(): string
{
return 'Creates a new WordPress post with the given title and content.';
}
public function execute(array $input): mixed
{
return $this->posts->create($input['title'], $input['content'] ?? '');
}
public function inputSchema(): array
{
return [
'type' => 'object',
'properties' => [
'title' => ['type' => 'string', 'description' => 'The post title.'],
'content' => ['type' => 'string', 'description' => 'The post body.'],
],
'required' => ['title'],
];
}
}Add your ability classes to config/ai-wordpress.php:
'abilities' => [
\App\Ai\Abilities\CreatePostAbility::class,
],The default name is derived from the root namespace and class name:
\App\Ai\Abilities\CreatePostAbility → "app/create-post"
Override name() to use a custom name:
public function name(): string
{
return 'app/create-post';
}Override permission() to control access. Return true, false, or a WP_Error:
public function permission(): bool|\WP_Error
{
return current_user_can('edit_posts');
}To expose an ability as an MCP tool when the WordPress MCP Adapter plugin is active, set mcp.public in meta():
public function meta(): array
{
return [
'mcp' => ['public' => true],
];
}wp acorn ability:listThis example shows a realistic ability that uses laravel/ai under the hood to summarize a WordPress post, exposes it over REST, and makes it available as an MCP tool for AI agents like Claude or Cursor.
Generate the class:
wp acorn make:ability SummarizePostapp/Ai/Abilities/SummarizePostAbility.php:
namespace App\Ai\Abilities;
use Illuminate\Support\Facades\Cache;
use Laravel\Ai\AnonymousAgent;
use Roots\AcornAi\Abilities\Ability;
class SummarizePostAbility extends Ability
{
public function name(): string
{
return 'app/summarize-post';
}
public function label(): string
{
return 'Summarize Post';
}
public function description(): string
{
return 'Generates a concise summary of a WordPress post given its ID. '
. 'Use this when you need a brief overview of a post without retrieving its full content.';
}
public function execute(array $input): mixed
{
$post = get_post($input['post_id']);
if (! $post || $post->post_status !== 'publish') {
return new \WP_Error('not_found', 'Post not found.');
}
return Cache::remember("ai-summary-{$post->ID}", now()->addDay(), function () use ($post) {
$content = wp_strip_all_tags($post->post_content);
return AnonymousAgent::make(
instructions: 'You are a helpful assistant that summarizes blog posts in 2-3 sentences.',
messages: [],
tools: [],
)->prompt("Summarize this post:\n\n{$content}")->text;
});
}
public function permission(): bool|\WP_Error
{
return is_user_logged_in();
}
public function inputSchema(): array
{
return [
'type' => 'object',
'properties' => [
'post_id' => [
'type' => 'integer',
'description' => 'The ID of the post to summarize.',
],
],
'required' => ['post_id'],
];
}
public function meta(): array
{
return [
'mcp' => ['public' => true],
];
}
}Register it in config/ai-wordpress.php:
'abilities' => [
\App\Ai\Abilities\SummarizePostAbility::class,
],What this gives you:
- REST API — any authenticated client can call
POST /wp-json/wp-abilities/v1/abilities/app/summarize-post/runwith{"post_id": 42} - MCP tool — when the WordPress MCP Adapter is active, the ability is surfaced to connected AI agents automatically
- Caching — summaries are cached for 24 hours so repeated calls don't burn API credits
- Container injection — swap
AnonymousAgentfor a dedicated agent class or any injected service by adding constructor dependencies to the ability
Keep track of development and community news.
- Join us on Discord by sponsoring us on GitHub
- Join us on Roots Discourse
- Follow @rootswp on Twitter
- Follow the Roots Blog
- Subscribe to the Roots Newsletter