WordPress + AI series · Part 2 of 2
← Part 1: How to connect your WordPress site to Claude with MCP
If you followed part one of this series, you now have Claude connected to your WordPress site via the Vibe AI MCP plugin. You can ask Claude to list your posts, create drafts, upload images, and manage categories — all from a chat prompt.
There is one thing that doesn’t work out of the box, though: Yoast SEO. If you try to set a focus keyphrase, meta description, or Open Graph data through Claude, nothing happens. No error, no confirmation — the fields are simply ignored. This guide explains why, and shows you how to fix it with about 50 lines of PHP.
Why Yoast blocks REST API write access by default
Yoast stores its per-post data in WordPress’s post_meta table, under keys like _yoast_wpseo_metadesc and _yoast_wpseo_focuskw. In WordPress, any plugin can register post meta fields for the REST API by calling register_post_meta() with show_in_rest => true. When that flag is set, the field becomes readable and writable via /wp/v2/posts/{id}.
Yoast deliberately does not set that flag. Their policy is that write access to SEO fields happens through their own Gutenberg sidebar — a JavaScript frontend store that bypasses the public REST API entirely. The yoast/v1 REST namespace that Yoast does expose is read-only: it returns scores, statistics, and readability data, but offers no endpoint for writing per-post meta.
The result: a POST request to /wp/v2/posts/{id} with Yoast meta keys in the meta object succeeds without error, but WordPress silently drops the unregistered fields. From an MCP client’s point of view, the operation appears to work — but nothing is saved.
The solution: a mu-plugin
The fix is to call register_post_meta() yourself, for exactly the Yoast fields you want to expose. Once registered, WordPress lets the REST API read and write those fields like any other meta.
The right place to put this code is a must-use plugin, or mu-plugin. Unlike regular plugins, mu-plugins:
- Load automatically on every request — no activation step needed
- Cannot be accidentally deactivated from the plugin dashboard
- Survive theme changes and theme updates (unlike
functions.php) - Are stored in
wp-content/mu-plugins/, separate from your regular plugins
A single PHP file in that folder is all you need.
Step 1: Create the file
Using your hosting file manager, FTP client, or SSH, navigate to wp-content/mu-plugins/. If the folder doesn’t exist, create it — WordPress recognises it automatically.
Create a new file named yoast-rest-bridge.php. The name doesn’t matter functionally, but something descriptive helps if you come back to this later.
Step 2: Add the code
Paste the following into the file and save:
<?php
/**
* Plugin Name: Yoast SEO REST Bridge
* Description: Registers Yoast SEO meta fields for REST API write access,
* so AI tools like Claude can manage them via MCP.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
function factnetize_yoast_velden_rest() {
$post_types = array( 'post', 'page' );
// Text fields
$tekst_keys = array(
'_yoast_wpseo_title',
'_yoast_wpseo_metadesc',
'_yoast_wpseo_focuskw',
'_yoast_wpseo_meta-robots-noindex',
'_yoast_wpseo_opengraph-title',
'_yoast_wpseo_opengraph-description',
'_yoast_wpseo_twitter-title',
'_yoast_wpseo_twitter-description',
'_yoast_wpseo_primary_category',
);
// URL fields
$url_keys = array(
'_yoast_wpseo_canonical',
'_yoast_wpseo_opengraph-image',
'_yoast_wpseo_twitter-image',
);
$auth = function() {
return current_user_can( 'edit_posts' );
};
foreach ( $post_types as $type ) {
foreach ( $tekst_keys as $key ) {
register_post_meta( $type, $key, array(
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'auth_callback' => $auth,
'sanitize_callback' => 'sanitize_text_field',
) );
}
foreach ( $url_keys as $key ) {
register_post_meta( $type, $key, array(
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'auth_callback' => $auth,
'sanitize_callback' => 'esc_url_raw',
) );
}
}
}
add_action( 'rest_api_init', 'factnetize_yoast_velden_rest' );
No further configuration is needed. The file is active the moment it’s saved.
Why two groups of fields?
You’ll notice the code splits fields into two arrays. This matters for security. The sanitize_text_field function strips HTML tags and extra whitespace — correct for titles, descriptions, and keyphrases. The esc_url_raw function is designed for URLs and rejects malformed values — correct for canonical URLs and image paths. Using sanitize_text_field on a URL field would silently corrupt values that contain characters like = or &.
Step 3: Verify it works
Open a new chat in Claude (or whichever MCP client you use) and ask:
“Fetch the meta fields for post [ID] on my WordPress site.”
If the registration is working, you’ll see all twelve Yoast keys in the response — initially empty if you haven’t filled them in yet:
_yoast_wpseo_focuskw_yoast_wpseo_title_yoast_wpseo_metadesc_yoast_wpseo_meta-robots-noindex_yoast_wpseo_canonical_yoast_wpseo_opengraph-title_yoast_wpseo_opengraph-description_yoast_wpseo_opengraph-image_yoast_wpseo_twitter-title_yoast_wpseo_twitter-description_yoast_wpseo_twitter-image_yoast_wpseo_primary_category
Now ask Claude to fill them in:
“Set the focus keyphrase of post [ID] to ‘connect WordPress to Claude’, write a good meta description, and fill in the Open Graph and Twitter card fields.”
Claude will generate appropriate values and write all fields in a single REST call. You can verify the result in the Yoast sidebar of the Gutenberg editor — the fields will be populated exactly as Claude wrote them.
The auth callback: who can write these fields?
The auth_callback in the code checks current_user_can( 'edit_posts' ). This means any WordPress user with at least the Author role can write to these fields — but only on posts they own. Editors and administrators can write to any post.
This is intentional. If you followed the security advice in part one and created a dedicated AI user with the Author role, that user can now manage Yoast SEO for all posts it creates. It cannot touch SEO fields on posts owned by other authors — which is exactly the right boundary.
If you want the AI user to be able to manage SEO on all posts (including older ones written by other authors), promote it to Editor. The Editor role adds edit_others_posts without granting access to plugins, themes, or site settings.
Limitations
A few things this bridge does not cover:
- Yoast Premium fields — fields introduced by Yoast Premium (redirects, internal linking suggestions, etc.) are not registered here. You can add them to the arrays using the same pattern.
- Schema type override — Yoast’s per-post schema settings are stored differently and are not exposed via this approach.
- Rank Math and AIOSEO — if you switch SEO plugins, the field keys change. The bridge only works for Yoast SEO.
Conclusion
With this mu-plugin in place, Claude can now manage your complete Yoast SEO setup — focus keyphrase, meta description, Open Graph, Twitter cards, canonical URL, and primary category — as part of the same workflow it uses to write and publish your articles.
The fix is small (one file, ~50 lines), survives updates and theme changes, respects WordPress permission levels, and uses the correct sanitisation for each field type. It is the missing piece that makes the WordPress + Claude MCP stack production-ready for content publishing.
← Part 1: How to connect your WordPress site to Claude with MCP