Project
WordPress MCP Server
A self-hosted MCP server that lets me build and manage this whole site by talking to an AI assistant — no clicking around wp-admin.
Why I built it
I wanted to drive my WordPress site from an AI assistant — drafting posts, building pages, tweaking the design — without living in wp-admin. There are paid services that do this, but they route your site through their own servers and meter usage. I self-host on purpose, and I didn’t love the idea of a third party holding my keys or charging per action.
So I built my own. It’s free, fully self-hosted (nothing leaves my VPS), and I control exactly which tools exist and what they’re allowed to do. It runs on the official WordPress MCP Adapter and the Abilities API (now in WordPress core), with a small must-use plugin that registers the tools I want and exposes them as an MCP server.
How it works
The MCP Adapter bridges WordPress’s Abilities API to the Model Context Protocol. My plugin registers abilities — create and update content, upload media, manage menus and taxonomy, change settings and CSS, even the footer — and exposes them as a custom MCP server at a REST endpoint. The AI client connects over HTTPS with a WordPress Application Password, so it can only do what that user can. No delete tools unless I add them — least privilege by default.
The code
Here’s the whole thing — the full must-use plugin that runs this site, fourteen tools all registered the same way. Personal naming is swapped for generic placeholders; drop it into wp-content/mu-plugins/ and change the namespace to your own.
<?php
/**
* Plugin Name: MCP Content Abilities
* Description: Registers site-building abilities and exposes them as MCP tools via the WordPress MCP Adapter.
* Version: 2.0.0
* Author: Your Name
*
* Drop this file in wp-content/mu-plugins/ (it auto-loads, no activation needed).
* Requires: WordPress 6.9+ (Abilities API in core) and the WordPress/mcp-adapter plugin active.
* Endpoint it creates: /wp-json/site-mcp/mcp
*
* Tools: list/get/create/update/delete content, upload media (from URL),
* set featured image, list/create/assign terms, update site settings
* (incl. static homepage), set custom CSS, set navigation.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/* -------------------------------------------------------------------------
* 1. Ability category
* ---------------------------------------------------------------------- */
add_action( 'wp_abilities_api_categories_init', function () {
wp_register_ability_category( 'site-content', array(
'label' => __( 'Site building', 'site-mcp' ),
'description' => __( 'Read and write content, media, taxonomy, settings, design, and navigation.', 'site-mcp' ),
) );
} );
/* -------------------------------------------------------------------------
* 2. Abilities
* ---------------------------------------------------------------------- */
add_action( 'wp_abilities_api_init', function () {
/* ---- Content ----------------------------------------------------- */
wp_register_ability( 'site/list-content', array(
'label' => __( 'List content', 'site-mcp' ),
'description' => __( 'List posts or pages, optionally filtered by status or a search term.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'post_type' => array( 'type' => 'string', 'enum' => array( 'post', 'page' ), 'default' => 'post', 'description' => 'Which content type to list.' ),
'post_status' => array( 'type' => 'string', 'default' => 'any', 'description' => 'publish, draft, pending, private, or any.' ),
'search' => array( 'type' => 'string', 'description' => 'Optional search term.' ),
'number' => array( 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100, 'description' => 'Max items to return.' ),
),
),
'output_schema' => array( 'type' => 'array' ),
'execute_callback' => 'site_mcp_list_content',
'permission_callback' => 'site_mcp_can_edit',
) );
wp_register_ability( 'site/get-content', array(
'label' => __( 'Get content', 'site-mcp' ),
'description' => __( 'Retrieve a single post or page by ID, including its full content.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'id' ),
'properties' => array( 'id' => array( 'type' => 'integer', 'description' => 'The post or page ID.' ) ),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_get_content',
'permission_callback' => 'site_mcp_can_edit',
) );
wp_register_ability( 'site/create-content', array(
'label' => __( 'Create content', 'site-mcp' ),
'description' => __( 'Create a new post or page. Defaults to a draft post. Content accepts HTML or block markup.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'title' ),
'properties' => array(
'title' => array( 'type' => 'string', 'description' => 'The title.' ),
'content' => array( 'type' => 'string', 'description' => 'Body content (HTML or block markup).' ),
'excerpt' => array( 'type' => 'string', 'description' => 'Optional excerpt.' ),
'post_type' => array( 'type' => 'string', 'enum' => array( 'post', 'page' ), 'default' => 'post' ),
'status' => array( 'type' => 'string', 'enum' => array( 'draft', 'publish', 'pending', 'private' ), 'default' => 'draft' ),
),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_create_content',
'permission_callback' => 'site_mcp_can_edit',
) );
wp_register_ability( 'site/update-content', array(
'label' => __( 'Update content', 'site-mcp' ),
'description' => __( 'Update an existing post or page by ID. Only the fields you pass are changed.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'id' ),
'properties' => array(
'id' => array( 'type' => 'integer', 'description' => 'The post or page ID to update.' ),
'title' => array( 'type' => 'string' ),
'content' => array( 'type' => 'string' ),
'excerpt' => array( 'type' => 'string' ),
'status' => array( 'type' => 'string', 'enum' => array( 'draft', 'publish', 'pending', 'private' ) ),
'slug' => array( 'type' => 'string' ),
),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_update_content',
'permission_callback' => 'site_mcp_can_edit',
) );
wp_register_ability( 'site/delete-content', array(
'label' => __( 'Delete content', 'site-mcp' ),
'description' => __( 'Move a post or page to the trash (reversible). Does not permanently delete.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'id' ),
'properties' => array( 'id' => array( 'type' => 'integer', 'description' => 'The post or page ID to trash.' ) ),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_delete_content',
'permission_callback' => 'site_mcp_can_delete',
) );
/* ---- Media ------------------------------------------------------- */
wp_register_ability( 'site/upload-media', array(
'label' => __( 'Upload media from URL', 'site-mcp' ),
'description' => __( 'Download an image from a URL into the media library. Returns the attachment ID and URL.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'url' ),
'properties' => array(
'url' => array( 'type' => 'string', 'description' => 'Source image URL (https).' ),
'title' => array( 'type' => 'string', 'description' => 'Optional title for the attachment.' ),
'alt' => array( 'type' => 'string', 'description' => 'Optional alt text.' ),
),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_upload_media',
'permission_callback' => 'site_mcp_can_upload',
) );
wp_register_ability( 'site/set-featured-image', array(
'label' => __( 'Set featured image', 'site-mcp' ),
'description' => __( 'Set the featured image of a post or page to an existing media attachment.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'post_id', 'attachment_id' ),
'properties' => array(
'post_id' => array( 'type' => 'integer', 'description' => 'The post or page ID.' ),
'attachment_id' => array( 'type' => 'integer', 'description' => 'The media attachment ID.' ),
),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_set_featured_image',
'permission_callback' => 'site_mcp_can_edit',
) );
/* ---- Taxonomy ---------------------------------------------------- */
wp_register_ability( 'site/list-terms', array(
'label' => __( 'List terms', 'site-mcp' ),
'description' => __( 'List categories or tags.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'taxonomy' => array( 'type' => 'string', 'enum' => array( 'category', 'post_tag' ), 'default' => 'category' ),
),
),
'output_schema' => array( 'type' => 'array' ),
'execute_callback' => 'site_mcp_list_terms',
'permission_callback' => 'site_mcp_can_edit',
) );
wp_register_ability( 'site/create-term', array(
'label' => __( 'Create term', 'site-mcp' ),
'description' => __( 'Create a category or tag.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'name' ),
'properties' => array(
'name' => array( 'type' => 'string' ),
'taxonomy' => array( 'type' => 'string', 'enum' => array( 'category', 'post_tag' ), 'default' => 'category' ),
'description' => array( 'type' => 'string' ),
),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_create_term',
'permission_callback' => 'site_mcp_can_manage',
) );
wp_register_ability( 'site/assign-terms', array(
'label' => __( 'Assign terms', 'site-mcp' ),
'description' => __( 'Assign categories or tags (by name) to a post. Replaces existing terms for that taxonomy.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'post_id', 'terms' ),
'properties' => array(
'post_id' => array( 'type' => 'integer' ),
'taxonomy' => array( 'type' => 'string', 'enum' => array( 'category', 'post_tag' ), 'default' => 'category' ),
'terms' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ), 'description' => 'Term names. Missing ones are created.' ),
),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_assign_terms',
'permission_callback' => 'site_mcp_can_edit',
) );
/* ---- Site settings, design, navigation --------------------------- */
wp_register_ability( 'site/update-site-settings', array(
'label' => __( 'Update site settings', 'site-mcp' ),
'description' => __( 'Update site title, tagline, and the static front page / posts page.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'title' => array( 'type' => 'string', 'description' => 'Site title (blogname).' ),
'tagline' => array( 'type' => 'string', 'description' => 'Tagline (blogdescription).' ),
'front_page_id' => array( 'type' => 'integer', 'description' => 'Page ID to use as the static homepage. Sets show_on_front to "page".' ),
'posts_page_id' => array( 'type' => 'integer', 'description' => 'Page ID to use as the blog/posts page.' ),
),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_update_site_settings',
'permission_callback' => 'site_mcp_can_manage',
) );
wp_register_ability( 'site/set-custom-css', array(
'label' => __( 'Set custom CSS', 'site-mcp' ),
'description' => __( "Replace the active theme's Additional CSS (the Customizer custom CSS).", 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'css' ),
'properties' => array( 'css' => array( 'type' => 'string', 'description' => 'The full CSS to store (replaces existing).' ) ),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_set_custom_css',
'permission_callback' => 'site_mcp_can_manage',
) );
wp_register_ability( 'site/set-navigation', array(
'label' => __( 'Set navigation', 'site-mcp' ),
'description' => __( "Set the site's primary navigation (block-theme wp_navigation) to a list of links. Replaces the current menu.", 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'items' ),
'properties' => array(
'items' => array(
'type' => 'array',
'description' => 'Ordered nav items.',
'items' => array(
'type' => 'object',
'required' => array( 'label' ),
'properties' => array(
'label' => array( 'type' => 'string' ),
'page_id' => array( 'type' => 'integer', 'description' => 'Link to this page ID.' ),
'url' => array( 'type' => 'string', 'description' => 'Or a custom URL (used if no page_id).' ),
),
),
),
),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_set_navigation',
'permission_callback' => 'site_mcp_can_manage',
) );
wp_register_ability( 'site/set-footer', array(
'label' => __( 'Set footer', 'site-mcp' ),
'description' => __( 'Replace the site footer (the block-theme footer template part) with the given block markup.', 'site-mcp' ),
'category' => 'site-content',
'input_schema' => array(
'type' => 'object',
'required' => array( 'content' ),
'properties' => array( 'content' => array( 'type' => 'string', 'description' => 'Full block markup for the footer (replaces the current footer).' ) ),
),
'output_schema' => array( 'type' => 'object' ),
'execute_callback' => 'site_mcp_set_footer',
'permission_callback' => 'site_mcp_can_manage',
) );
} );
/* -------------------------------------------------------------------------
* 3. Expose the abilities as tools on a dedicated MCP server.
* Endpoint: /wp-json/site-mcp/mcp
* ---------------------------------------------------------------------- */
add_action( 'mcp_adapter_init', function ( $adapter ) {
$adapter->create_server(
'site-mcp-server',
'site-mcp',
'mcp',
'Site Content MCP',
'Build & manage example.com: content, media, taxonomy, settings, design, navigation',
'v2.0.0',
array( \WP\MCP\Transport\HttpTransport::class ),
\WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class,
\WP\MCP\Infrastructure\Observability\NullMcpObservabilityHandler::class,
array(
'site/list-content',
'site/get-content',
'site/create-content',
'site/update-content',
'site/delete-content',
'site/upload-media',
'site/set-featured-image',
'site/list-terms',
'site/create-term',
'site/assign-terms',
'site/update-site-settings',
'site/set-custom-css',
'site/set-navigation',
'site/set-footer',
),
array(),
array()
);
} );
/* -------------------------------------------------------------------------
* 4. Permission checks.
* ---------------------------------------------------------------------- */
function site_mcp_can_edit() {
return current_user_can( 'edit_posts' );
}
function site_mcp_can_delete() {
return current_user_can( 'delete_posts' );
}
function site_mcp_can_upload() {
return current_user_can( 'upload_files' );
}
function site_mcp_can_manage() {
return current_user_can( 'manage_options' );
}
/* -------------------------------------------------------------------------
* 5. Callbacks.
* ---------------------------------------------------------------------- */
function site_mcp_list_content( array $input = array() ): array {
$query = new WP_Query( array(
'post_type' => $input['post_type'] ?? 'post',
'post_status' => $input['post_status'] ?? 'any',
's' => $input['search'] ?? '',
'posts_per_page' => $input['number'] ?? 20,
'no_found_rows' => true,
) );
$out = array();
foreach ( $query->posts as $post ) {
$out[] = array(
'id' => (int) $post->ID,
'title' => get_the_title( $post ),
'status' => $post->post_status,
'type' => $post->post_type,
'link' => (string) get_permalink( $post ),
'date' => $post->post_date_gmt,
);
}
return $out;
}
function site_mcp_get_content( array $input = array() ) {
$post = get_post( (int) ( $input['id'] ?? 0 ) );
if ( ! $post ) {
return new WP_Error( 'not_found', 'No post or page with that ID.' );
}
return array(
'id' => (int) $post->ID,
'title' => $post->post_title,
'content' => $post->post_content,
'excerpt' => $post->post_excerpt,
'status' => $post->post_status,
'type' => $post->post_type,
'slug' => $post->post_name,
'link' => (string) get_permalink( $post ),
'date' => $post->post_date_gmt,
);
}
function site_mcp_create_content( array $input = array() ) {
$id = wp_insert_post( array(
'post_title' => $input['title'] ?? '',
'post_content' => $input['content'] ?? '',
'post_excerpt' => $input['excerpt'] ?? '',
'post_type' => $input['post_type'] ?? 'post',
'post_status' => $input['status'] ?? 'draft',
), true );
if ( is_wp_error( $id ) ) {
return $id;
}
$post = get_post( $id );
return array(
'id' => (int) $post->ID,
'link' => (string) get_permalink( $post ),
'status' => $post->post_status,
);
}
function site_mcp_update_content( array $input = array() ) {
$id = (int) ( $input['id'] ?? 0 );
if ( ! get_post( $id ) ) {
return new WP_Error( 'not_found', 'No post or page with that ID.' );
}
$data = array( 'ID' => $id );
$map = array(
'title' => 'post_title',
'content' => 'post_content',
'excerpt' => 'post_excerpt',
'status' => 'post_status',
'slug' => 'post_name',
);
foreach ( $map as $in => $field ) {
if ( isset( $input[ $in ] ) ) {
$data[ $field ] = $input[ $in ];
}
}
$result = wp_update_post( $data, true );
if ( is_wp_error( $result ) ) {
return $result;
}
$post = get_post( $id );
return array(
'id' => (int) $post->ID,
'link' => (string) get_permalink( $post ),
'status' => $post->post_status,
);
}
function site_mcp_delete_content( array $input = array() ) {
$id = (int) ( $input['id'] ?? 0 );
if ( ! get_post( $id ) ) {
return new WP_Error( 'not_found', 'No post or page with that ID.' );
}
$result = wp_trash_post( $id );
if ( ! $result ) {
return new WP_Error( 'trash_failed', 'Could not move the item to trash.' );
}
return array( 'id' => $id, 'status' => 'trash' );
}
function site_mcp_upload_media( array $input = array() ) {
$url = $input['url'] ?? '';
if ( ! $url ) {
return new WP_Error( 'no_url', 'A url is required.' );
}
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
$tmp = download_url( $url );
if ( is_wp_error( $tmp ) ) {
return $tmp;
}
$name = basename( wp_parse_url( $url, PHP_URL_PATH ) );
$file_array = array(
'name' => $name ? $name : 'image.jpg',
'tmp_name' => $tmp,
);
$attachment_id = media_handle_sideload( $file_array, 0, $input['title'] ?? null );
if ( is_wp_error( $attachment_id ) ) {
@unlink( $tmp );
return $attachment_id;
}
if ( ! empty( $input['alt'] ) ) {
update_post_meta( $attachment_id, '_wp_attachment_image_alt', sanitize_text_field( $input['alt'] ) );
}
return array(
'id' => (int) $attachment_id,
'url' => (string) wp_get_attachment_url( $attachment_id ),
);
}
function site_mcp_set_featured_image( array $input = array() ) {
$post_id = (int) ( $input['post_id'] ?? 0 );
$attachment_id = (int) ( $input['attachment_id'] ?? 0 );
if ( ! get_post( $post_id ) ) {
return new WP_Error( 'not_found', 'No post or page with that ID.' );
}
set_post_thumbnail( $post_id, $attachment_id );
return array( 'post_id' => $post_id, 'attachment_id' => $attachment_id );
}
function site_mcp_list_terms( array $input = array() ): array {
$terms = get_terms( array(
'taxonomy' => $input['taxonomy'] ?? 'category',
'hide_empty' => false,
) );
$out = array();
if ( ! is_wp_error( $terms ) ) {
foreach ( $terms as $t ) {
$out[] = array( 'id' => (int) $t->term_id, 'name' => $t->name, 'slug' => $t->slug, 'count' => (int) $t->count );
}
}
return $out;
}
function site_mcp_create_term( array $input = array() ) {
$res = wp_insert_term(
$input['name'] ?? '',
$input['taxonomy'] ?? 'category',
array( 'description' => $input['description'] ?? '' )
);
if ( is_wp_error( $res ) ) {
return $res;
}
return array( 'id' => (int) $res['term_id'], 'name' => $input['name'] ?? '' );
}
function site_mcp_assign_terms( array $input = array() ) {
$post_id = (int) ( $input['post_id'] ?? 0 );
$taxonomy = $input['taxonomy'] ?? 'category';
$terms = $input['terms'] ?? array();
if ( ! get_post( $post_id ) ) {
return new WP_Error( 'not_found', 'No post or page with that ID.' );
}
$res = wp_set_object_terms( $post_id, $terms, $taxonomy, false );
if ( is_wp_error( $res ) ) {
return $res;
}
return array( 'post_id' => $post_id, 'taxonomy' => $taxonomy, 'assigned' => array_values( $terms ) );
}
function site_mcp_update_site_settings( array $input = array() ) {
if ( isset( $input['title'] ) ) {
update_option( 'blogname', sanitize_text_field( $input['title'] ) );
}
if ( isset( $input['tagline'] ) ) {
update_option( 'blogdescription', sanitize_text_field( $input['tagline'] ) );
}
if ( ! empty( $input['front_page_id'] ) ) {
update_option( 'show_on_front', 'page' );
update_option( 'page_on_front', (int) $input['front_page_id'] );
}
if ( ! empty( $input['posts_page_id'] ) ) {
update_option( 'page_for_posts', (int) $input['posts_page_id'] );
}
return array(
'title' => get_option( 'blogname' ),
'tagline' => get_option( 'blogdescription' ),
'show_on_front' => get_option( 'show_on_front' ),
'page_on_front' => (int) get_option( 'page_on_front' ),
'page_for_posts'=> (int) get_option( 'page_for_posts' ),
);
}
function site_mcp_set_custom_css( array $input = array() ) {
$css = $input['css'] ?? '';
$res = wp_update_custom_css_post( $css );
if ( is_wp_error( $res ) ) {
return $res;
}
return array( 'ok' => true, 'bytes' => strlen( $css ), 'stylesheet' => get_stylesheet() );
}
function site_mcp_set_navigation( array $input = array() ) {
$items = $input['items'] ?? array();
if ( empty( $items ) || ! is_array( $items ) ) {
return new WP_Error( 'no_items', 'Provide at least one nav item.' );
}
$inner = '';
foreach ( $items as $it ) {
$label = isset( $it['label'] ) ? $it['label'] : '';
if ( ! empty( $it['page_id'] ) ) {
$pid = (int) $it['page_id'];
$href = get_permalink( $pid );
$attrs = array( 'label' => $label, 'type' => 'page', 'id' => $pid, 'url' => $href, 'kind' => 'post-type' );
} else {
$attrs = array( 'label' => $label, 'url' => $it['url'] ?? '#', 'kind' => 'custom' );
}
$inner .= '<!-- wp:navigation-link ' . wp_json_encode( $attrs ) . ' /-->';
}
$existing = get_posts( array(
'post_type' => 'wp_navigation',
'posts_per_page' => 1,
'post_status' => 'publish',
'orderby' => 'ID',
'order' => 'ASC',
) );
if ( $existing ) {
$nav_id = $existing[0]->ID;
wp_update_post( array( 'ID' => $nav_id, 'post_content' => $inner ) );
} else {
$nav_id = wp_insert_post( array(
'post_type' => 'wp_navigation',
'post_status' => 'publish',
'post_title' => 'Navigation',
'post_content' => $inner,
) );
}
return array( 'navigation_id' => (int) $nav_id, 'items' => count( $items ) );
}
function site_mcp_set_footer( array $input = array() ) {
$content = $input['content'] ?? '';
if ( '' === trim( $content ) ) {
return new WP_Error( 'no_content', 'Footer content is required.' );
}
$theme = get_stylesheet();
$existing = get_posts( array(
'post_type' => 'wp_template_part',
'name' => 'footer',
'post_status' => 'any',
'posts_per_page' => 1,
'tax_query' => array( array( 'taxonomy' => 'wp_theme', 'field' => 'name', 'terms' => $theme ) ),
) );
if ( $existing ) {
$id = $existing[0]->ID;
wp_update_post( array( 'ID' => $id, 'post_content' => $content ) );
} else {
$id = wp_insert_post( array(
'post_type' => 'wp_template_part',
'post_name' => 'footer',
'post_title' => 'Footer',
'post_status' => 'publish',
'post_content' => $content,
), true );
if ( is_wp_error( $id ) ) {
return $id;
}
wp_set_object_terms( $id, $theme, 'wp_theme' );
wp_set_object_terms( $id, 'footer', 'wp_template_part_area' );
}
return array( 'template_part_id' => (int) $id, 'theme' => $theme );
}
Install it yourself
- Run WordPress 6.9+ — the Abilities API ships in core.
- Install the official WordPress/mcp-adapter plugin from its GitHub releases.
- Drop the plugin above into
wp-content/mu-plugins/— it auto-loads, no activation needed. - Create an Application Password under Users → Profile, and copy it.
- Confirm the server registered:
wp mcp-adapter list.
Connect Claude
In Claude Desktop, open Settings → Developer → Edit Config and add this to claude_desktop_config.json (you’ll need Node.js 22+). It runs a small local proxy that connects to your site’s REST endpoint with your application password:
{
"mcpServers": {
"my-site": {
"command": "npx",
"args": ["-y", "@automattic/mcp-wordpress-remote@latest"],
"env": {
"WP_API_URL": "https://example.com/wp-json/site-mcp/mcp",
"WP_API_USERNAME": "your-wp-username",
"WP_API_PASSWORD": "your application password"
}
}
}
}
Then fully quit and relaunch Claude Desktop — new servers only load on a restart. Ask it to “list my WordPress posts” to confirm you’re connected.
Updates
Build log
A full site-building toolkit
Expanded to fourteen tools — media, menus, taxonomy, settings, custom CSS, navigation, and footer. I can now build and style whole pages just by talking to the assistant.
First working server
Got content read and write working over MCP end to end — listing, creating, and updating posts and pages straight from chat.