Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![WordPress](https://img.shields.io/badge/WordPress-5.8+-21759B?style=flat-square&logo=wordpress)](https://wordpress.org/plugins/royal-mcp/)
[![PHP](https://img.shields.io/badge/PHP-7.4+-777BB4?style=flat-square&logo=php)](https://www.php.net/)
[![License](https://img.shields.io/badge/License-GPLv2-blue?style=flat-square)](https://www.gnu.org/licenses/gpl-2.0.html)
[![Version](https://img.shields.io/badge/Version-1.4.37-C9A227?style=flat-square)](https://wordpress.org/plugins/royal-mcp/)
[![Version](https://img.shields.io/badge/Version-1.4.38-C9A227?style=flat-square)](https://wordpress.org/plugins/royal-mcp/)

[Download on WordPress.org](https://wordpress.org/plugins/royal-mcp/) · [Documentation](https://royalplugins.com/support/royal-mcp/) · [Royal Plugins](https://royalplugins.com)

Expand All @@ -23,7 +23,8 @@ A WordPress plugin that exposes your site as a [Model Context Protocol](https://
|---|---|
| **Auth** | API key (`X-Royal-MCP-API-Key`) **or** OAuth 2.1 with PKCE + Dynamic Client Registration (RFC 7591) |
| **Transport** | MCP 2025-11-25 Streamable HTTP (single `/mcp` endpoint, POST/GET/DELETE) |
| **Tool count** | Up to 129 (69 WordPress core + 60 conditional plugin integrations) |
| **Tool count** | Up to 144 (73 WordPress core + 71 conditional plugin integrations) |
| **Abilities API** | WP 6.9+ — every tool also registers as a WordPress ability, reachable via WP core REST at `/wp-json/wp-abilities/v1/abilities/{name}/run` and via the WordPress MCP Adapter's named `royal-mcp-server` |
| **Rate limit** | 60 req/min per IP (configurable) |
| **Session model** | Sliding 24h TTL with refresh-on-access |
| **Activity log** | Every tool call logged (tool name + arg keys; argument values are never recorded) |
Expand All @@ -33,7 +34,7 @@ A WordPress plugin that exposes your site as a [Model Context Protocol](https://

## Capabilities

### WordPress core (69 tools, always available)
### WordPress core (73 tools, always available)

- **Content** — Posts, pages, custom post types (full CRUD + revisions + featured images)
- **Taxonomies** — Categories, tags, custom taxonomies, term meta, post-term linking
Expand All @@ -45,22 +46,35 @@ A WordPress plugin that exposes your site as a [Model Context Protocol](https://
- **Site** — Permalink structure, options (allowlisted), site info
- **Search** — Cross-content search by query
- **SEO** — Yoast / Rank Math / AIOSEO meta read/write where the plugin is active
- **Diagnostics** — Site status (WP/PHP/MySQL/plugins/themes/cron in one call), PHP error-log tail, WP cron schedule, and MCP `royal_mcp_connection_health` (returns route, auth method, session ID, plugin version, and active page-builder versions for Divi + Elementor + Gutenberg)

### Plugin integrations (60 tools, conditional)
### Plugin integrations (71 tools, conditional)

Auto-register only when the integrated plugin is active.

| Plugin | Tools | What's covered |
|---|---|---|
| WooCommerce | 26 | Products, variations, attributes, coupons, orders, customers, store stats |
| Elementor | 8 | Clone pages, replace text, swap images, get outline, read single element, list templates, import templates, add widget |
| GuardPress | 7 | Security score, failed logins, blocked IPs, vulnerability scans, audit log |
| Royal AI Firewall | 6 | Dashboard stats, recent bot hits, per-bot policies (allow / block / challenge), daily rollups, one-call emergency lockdown |
| SiteVault | 6 | Trigger backups, monitor progress, list schedules |
| Elementor | 7 | Clone pages, replace text, swap images, get outline, list templates, import templates, add widget |
| **Redirection** (new in 1.4.38) | **4** | **List redirects with group + URL-substring filters, create + update redirects (301 / 302 / 307 / regex / groups), list redirect groups** |
| Advanced Custom Fields | 4 | Read/write ACF fields with each field's Return Format respected (hydrated post objects, parsed repeater rows, image arrays); enumerate field groups for AI-driven discovery |
| Royal Ledger | 4 | Software costs, renewal dates, license keys (values never exposed) |
| **Advanced Custom Fields** (new in 1.4.24) | **4** | **Read/write ACF fields with each field's Return Format respected (hydrated post objects, parsed repeater rows, image arrays); enumerate field groups for AI-driven discovery** |
| Royal Links | 3 | Branded short links, click stats |
| ForgeCache | 3 | Cache stats, clear cache, purge URL |

## WordPress Abilities API bridge (WP 6.9+)

WordPress 6.9 shipped the [Abilities API](https://developer.wordpress.org/plugins/abilities-api/) — a primitive that lets plugins register typed capabilities AI agents can call. As of 1.4.38, every Royal MCP tool also registers as a WordPress ability, giving you three ways to reach the same handlers:

1. **Native** — Royal MCP's `/wp-json/royal-mcp/v1/mcp` Streamable HTTP endpoint (unchanged, always available).
2. **WP MCP Adapter** — if the [`wordpress/mcp-adapter`](https://github.com/WordPress/mcp-adapter) package is installed, Royal MCP registers a named `royal-mcp-server` on the `mcp_adapter_init` hook alongside adapter's default server.
3. **WP core REST** — direct ability invocation at `/wp-json/wp-abilities/v1/abilities/{name}/run` for callers that prefer the core WP endpoint.

Same handlers, three transports, one set of per-tool capability gates. Bridge can be disabled with the `royal_mcp_abilities_registration_enabled` option (default: on).

## What we don't do

Explicit scope boundaries — the integration model is "narrow tools that work reliably," not "expose every API surface."
Expand Down Expand Up @@ -88,6 +102,16 @@ Easiest path — no config file edits, no API key in your client.
2. URL: `https://yoursite.com/wp-json/royal-mcp/v1/mcp`
3. Approve the OAuth consent screen when prompted. Claude.ai handles dynamic client registration + PKCE flow against your site.

### Claude Desktop (.mcpb one-click bundle — new in 1.4.38)

Easiest Claude Desktop path — no `mcp-remote`, no npx, no config-file editing.

1. Download `royal-mcp-1.4.38.mcpb` from the [latest GitHub release](https://github.com/royalplugins/royal-mcp/releases/latest).
2. Double-click the `.mcpb` file — Claude Desktop opens the install prompt.
3. Enter your site URL + API key when prompted. Connection is live.

The bundle ships a zero-dependency stdio-to-HTTPS bridge in Node ≥18, which Claude Desktop already includes. See [Claude Desktop MCP Bundles](https://modelcontextprotocol.io/) for the .mcpb spec.

### Claude Desktop (OAuth via mcp-remote)

```json
Expand Down
89 changes: 89 additions & 0 deletions assets/css/chrome.css
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,95 @@
outline: none;
}

/* ======================================================================
4b. Royal MCP Pro Founding Members card (P5a)
Sibling of Founders Bundle callout — same visual language, different
content. Sits directly below it on the Royal Tools page.
====================================================================== */
.royal-mcp-founding {
display: grid;
grid-template-columns: auto 1fr auto;
gap: 20px;
align-items: center;
padding: 20px 24px;
margin-bottom: 28px;
background: linear-gradient(135deg, #1a1438 0%, #2c1e5a 60%, #3a1f6b 100%);
color: var(--rmc-white);
border-radius: 8px;
position: relative;
}
.royal-mcp-founding-icon {
width: 48px;
height: 48px;
background: var(--rmc-gold);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: var(--rmc-charcoal);
}
.royal-mcp-founding-body h3 {
font-family: 'Cormorant Garamond', 'Times New Roman', serif;
font-weight: 600;
font-size: 20px;
margin: 0 0 4px 0;
color: var(--rmc-white);
}
.royal-mcp-founding-body p {
font-size: 13px;
opacity: 0.82;
margin: 0;
line-height: 1.55;
color: var(--rmc-white);
}
.royal-mcp-founding-body p .gold {
color: var(--rmc-gold);
opacity: 1;
font-weight: 600;
}
.royal-mcp-founding-body p .regular {
color: rgba(255, 255, 255, 0.55);
text-decoration: line-through;
text-decoration-thickness: 1px;
}
.royal-mcp-founding-scarcity {
display: inline-block;
margin-top: 8px !important;
padding: 3px 10px;
background: rgba(240, 200, 58, 0.15);
border: 1px solid rgba(240, 200, 58, 0.35);
border-radius: 100px;
color: var(--rmc-gold) !important;
font-size: 11px !important;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
opacity: 1 !important;
}
.royal-mcp-founding-cta {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 12px 22px;
background: var(--rmc-gold);
color: var(--rmc-charcoal);
text-decoration: none;
font-weight: 600;
font-size: 14px;
border-radius: 4px;
letter-spacing: 0.02em;
transition: all 0.2s;
}
.royal-mcp-founding-cta:hover,
.royal-mcp-founding-cta:focus {
background: var(--rmc-gold-light);
transform: translateY(-1px);
color: var(--rmc-charcoal);
outline: none;
box-shadow: none;
}

/* ======================================================================
Section head
====================================================================== */
Expand Down
106 changes: 106 additions & 0 deletions includes/Abilities/Categories.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php
/**
* Pre-registers the 10 Royal MCP ability categories on wp_abilities_api_init (priority 5),
* before ability registration walks the tool registry at priority 10.
*
* WP core requires every ability to reference a pre-registered category slug — an ability
* whose `category` arg does not resolve to a registered category throws at registration time.
*/

namespace Royal_MCP\Abilities;

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

class Categories {

const NAMESPACE_PREFIX = 'royal-mcp';

/**
* Category slug (short key) → label + description.
*
* Keyed by the short slug; full registered slug is composed via {@see category_slug()}.
*/
private static function catalog(): array {
return array(
'core' => array(
'label' => __( 'Royal MCP: Core', 'royal-mcp' ),
'description' => __( 'Core WordPress operations: posts, pages, media, terms, comments, users, options, menus, themes, SEO meta, permalinks, revisions, cron, error log, connection health, search, site info.', 'royal-mcp' ),
),
'woocommerce' => array(
'label' => __( 'Royal MCP: WooCommerce', 'royal-mcp' ),
'description' => __( 'WooCommerce products, orders, coupons, variations, customers, and store stats.', 'royal-mcp' ),
),
'elementor' => array(
'label' => __( 'Royal MCP: Elementor', 'royal-mcp' ),
'description' => __( 'Elementor page operations: outline read, clone, replace text, replace image, import template, add widget, list local templates.', 'royal-mcp' ),
),
'forgecache' => array(
'label' => __( 'Royal MCP: ForgeCache', 'royal-mcp' ),
'description' => __( 'ForgeCache cache statistics, URL purge, and full cache clear.', 'royal-mcp' ),
),
'sitevault' => array(
'label' => __( 'Royal MCP: SiteVault', 'royal-mcp' ),
'description' => __( 'SiteVault backups: create, read, status, schedules, and stats.', 'royal-mcp' ),
),
'guardpress' => array(
'label' => __( 'Royal MCP: GuardPress', 'royal-mcp' ),
'description' => __( 'GuardPress security: audit log, blocked IPs, failed logins, vulnerability scan, and security status.', 'royal-mcp' ),
),
'royal-links' => array(
'label' => __( 'Royal MCP: Royal Links', 'royal-mcp' ),
'description' => __( 'Royal Links link management and click statistics.', 'royal-mcp' ),
),
'royal-ledger' => array(
'label' => __( 'Royal MCP: Royal Ledger', 'royal-mcp' ),
'description' => __( 'Royal Ledger renewals, costs, and license keys.', 'royal-mcp' ),
),
'acf' => array(
'label' => __( 'Royal MCP: ACF', 'royal-mcp' ),
'description' => __( 'Advanced Custom Fields (ACF) field read/update and group enumeration.', 'royal-mcp' ),
),
'raif' => array(
'label' => __( 'Royal MCP: Royal AI Firewall', 'royal-mcp' ),
'description' => __( 'Royal AI Firewall bot classification, block list, and firewall statistics.', 'royal-mcp' ),
),
'redirection' => array(
'label' => __( 'Royal MCP: Redirection', 'royal-mcp' ),
'description' => __( 'Redirection plugin: list/create/update redirects, list groups.', 'royal-mcp' ),
),
);
}

/**
* Fires on wp_abilities_api_init at priority 5, before ability registration at priority 10.
*/
public static function register(): void {
if ( ! function_exists( 'wp_register_ability_category' ) ) {
return;
}
foreach ( self::catalog() as $short_slug => $spec ) {
wp_register_ability_category(
self::category_slug( $short_slug ),
array(
'label' => $spec['label'],
'description' => $spec['description'],
)
);
}
}

/**
* Compose the full registered category slug from a short key.
*/
public static function category_slug( string $short_slug ): string {
return self::NAMESPACE_PREFIX . '-' . $short_slug;
}

/**
* All registered category slugs (full form). Used by e2e assertions and by the Registrar
* lookup path when dispatching an ability to its category.
*/
public static function get_all_slugs(): array {
return array_map( array( __CLASS__, 'category_slug' ), array_keys( self::catalog() ) );
}
}
82 changes: 82 additions & 0 deletions includes/Abilities/MCP_Adapter_Server.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
/**
* Registers "royal-mcp-server" with the WordPress MCP Adapter (Option C from
* SCOPE_1.4.38.md). Enables customers running MCP Adapter to reach Royal MCP tools
* over MCP Adapter's HTTP transport in addition to our native /wp-json/royal-mcp/mcp
* endpoint and WP core /wp-json/wp-abilities/v1/* REST.
*
* Named-server registration + explicit ability list means our abilities do NOT
* auto-enroll on MCP Adapter's default server (that path requires meta.mcp.public=true,
* which we intentionally do not set — see Meta_Config.php + SCOPE addendum).
*
* Silent no-op if the MCP Adapter plugin isn't installed or hasn't loaded yet.
*/

namespace Royal_MCP\Abilities;

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

class MCP_Adapter_Server {

const SERVER_ID = 'royal-mcp-server';
const NAMESPACE_ROUTE = 'royal-mcp-server/v1';
const ROUTE = 'mcp';
const SERVER_NAME = 'Royal MCP';
const SERVER_DESC = 'Royal MCP — WordPress core operations plus WooCommerce, Elementor, SEO, and cross-plugin abilities. Same handlers as the native /wp-json/royal-mcp/mcp endpoint; MCP Adapter transport is an additional access path, not a replacement.';

/**
* Fires on mcp_adapter_init (dispatched by MCP Adapter core on its own init).
* By this hook, wp_abilities_api_init has already fired and the abilities
* registry is populated — we can safely collect ability names.
*/
public static function register(): void {
if ( ! class_exists( '\WP\MCP\Core\McpAdapter' ) ) {
return;
}
if ( ! class_exists( '\WP\MCP\Transport\HttpTransport' ) ) {
return;
}
if ( ! function_exists( 'wp_get_abilities' ) ) {
return;
}

$ability_names = self::collect_our_ability_names();
if ( empty( $ability_names ) ) {
return;
}

$adapter = \WP\MCP\Core\McpAdapter::instance();
$adapter->create_server(
self::SERVER_ID,
self::NAMESPACE_ROUTE,
self::ROUTE,
self::SERVER_NAME,
self::SERVER_DESC,
defined( 'ROYAL_MCP_VERSION' ) ? ROYAL_MCP_VERSION : '1.0.0',
array( \WP\MCP\Transport\HttpTransport::class ),
null, // error_handler → NullMcpErrorHandler default
null, // observability_handler → NullMcpObservabilityHandler default
$ability_names // tools (explicit list)
);
}

/**
* Walk WP abilities registry, return every royal-mcp/* ability name.
*/
private static function collect_our_ability_names(): array {
$names = array();
$all = wp_get_abilities();
foreach ( $all as $ability ) {
if ( ! is_object( $ability ) || ! method_exists( $ability, 'get_name' ) ) {
continue;
}
$name = $ability->get_name();
if ( strpos( $name, 'royal-mcp/' ) === 0 ) {
$names[] = $name;
}
}
return $names;
}
}
Loading
Loading