Royal MCP Direct-Connect Threat Model
Royal MCP is a direct-connect MCP server: AI clients authenticate against your WordPress install, not a third-party relay. This document explains the authentication boundary on each of Royal MCP’s three transports (MCP OAuth 2.1, WordPress Abilities API adapter, WP REST API), lists the threat scenarios covered and the mitigations in place, and gives your security or agency team a production-readiness checklist to run before enabling AI access on a customer-facing site.
What “direct-connect” means for your site
An MCP server can be reached in one of two shapes. Some products run a relay-based architecture: the AI client talks to the vendor’s relay service, and the relay talks to your WordPress site on your behalf. Auth happens on the relay, not on your site. Convenient to set up, but every call in and out passes through a third-party trust boundary you don’t operate. Others — Royal MCP included — run direct-connect: the AI client authenticates against your WordPress install itself, and every subsequent call reaches WordPress with no intermediary.
The direct-connect trade-off is straightforward. You keep every request inside a trust boundary you already own. Your capability model, your audit trail, your rate limits, your incident response. Nothing is metered by anyone else. In exchange, you accept full responsibility for the auth surface on your own site — the same responsibility you already carry for the WordPress admin panel and the WP REST API. This document describes what that surface looks like on Royal MCP so your team can evaluate it against your existing security posture.
The three transports Royal MCP exposes
Post-1.4.38 there are three distinct paths a caller can use to reach a Royal MCP tool. All three routes converge on the same set of tool handlers and the same per-tool capability checks. Only the transport-level authentication differs.
| Transport | Auth mechanism | Typical caller |
|---|---|---|
MCP server/wp-json/royal-mcp/v1/mcp |
OAuth 2.1 with Dynamic Client Registration + PKCE-S256, or long-lived API key | Claude Desktop, Claude Code, Claude.ai web, ChatGPT MCP connector, any MCP-compatible client |
| WordPress Abilities API (via WP MCP Adapter) |
Delegated to the adapter’s auth (typically the adapter’s own OAuth flow) | Sites running the WP MCP Adapter plugin alongside Royal MCP; ecosystem clients that speak to the adapter |
WordPress REST API/wp-json/wp-abilities/v1/abilities/royal-mcp/{name}/run |
WP-native: Application Passwords, cookie + nonce for logged-in browsers | Custom scripts, WP-CLI, automation platforms, existing REST clients |
The same tool (say, wp_get_posts) is reachable via all three transports as of 1.4.38. What changes between them is who authenticates and how — not what the tool does or which WordPress capabilities are required to run it. A caller who reaches wp_create_post via the REST endpoint still needs the edit_posts capability on the WordPress user attached to their Application Password, exactly as an MCP-transport caller would.
The MCP transport is the primary path for AI clients. The Abilities API path lets Royal MCP co-exist with the emerging WordPress MCP Adapter ecosystem so buyers who standardize on the adapter can use our tools without changing tooling. The REST path lets existing automation (WP-CLI, custom scripts, third-party workflow tools) call the same tools without needing to speak MCP. Same authorization gate on all three — three transports, one capability model.
The OAuth 2.1 model on the MCP transport
Royal MCP implements the OAuth 2.1 draft profile (draft-ietf-oauth-v2-1) with the extensions the MCP specification requires. The relevant properties for a security review:
- Dynamic Client Registration (RFC 7591). Clients register themselves at first connection by POSTing a small metadata document to
/register. No pre-shared client secret is baked into the AI client — every install gets its own client record scoped to your site. This is what lets a new customer plug in Claude or ChatGPT without you handing out credentials manually. - PKCE-S256 (RFC 7636). Required on every authorization request. Mitigates the code-interception class of attack where a malicious app on the same device intercepts the authorization code before the client exchanges it for a token.
- Access token TTL: 3600 seconds (1 hour). Short-lived so a leaked access token becomes useless within an hour. Every read and write is signed by the current access token; the moment it expires, the client must present a refresh token.
- Refresh token TTL: 2,592,000 seconds (30 days), rotating on use. Each refresh issues a new refresh token and invalidates the previous one, so a replayed refresh token is caught the next time the legitimate client refreshes.
- Consent flow requires a WordPress session. The authorization step requires the current visitor to be logged into WordPress. If the visitor is not logged in they are redirected to
wp-login.phpand returned to the consent screen after successful login. The consent record is bound to whichever WordPress user completed the flow, and the resulting client can only exercise capabilities that that user holds. - Per-request WordPress capability check. The OAuth token establishes who the caller is (which WordPress user); it does not grant blanket access to every tool. Every tool handler runs a
current_user_can()check against the appropriate capability (read_post,edit_posts,manage_options, etc.) at call time. A stolen token still can’t exceed the capabilities of the user it was issued to, and a client authorized by an Editor cannot call an Administrator-scoped tool no matter what its token says.
A separate long-lived API key path is available for callers that can’t complete an interactive OAuth flow (headless CI, scheduled scripts). API keys are generated in Royal MCP → Settings, tied to a WordPress user, and can be revoked from the same screen. They carry the same capability boundary as OAuth-issued tokens — they don’t bypass any check.
The REST-transport authentication model
The REST endpoints Royal MCP registers as of 1.4.38 use the WordPress-native REST authentication stack. This is deliberate: WordPress’ REST auth is well-understood by the WordPress security ecosystem, has been reviewed for over a decade, and any Application Password or cookie-auth issue is already handled by every WordPress security plugin your customers may already run.
- Application Passwords. WordPress-native, per-user, 24-character machine-generated. Created in Users → Profile → Application Passwords, individually named (so you can revoke one without breaking the others), and revocable at any time by the user or a site administrator. Every REST call using an Application Password carries the WordPress capabilities of the owning user — a low-privilege user can call read-only tools; an editor can call editor-scoped tools; only an administrator can call admin tools.
- Cookie + nonce. For browser-based callers (an admin loading a custom dashboard page that hits the REST API from JavaScript). Only works when the user is already logged into WordPress. Nonces expire on the standard WordPress schedule (24 hours by default).
- No Basic Auth by default. Royal MCP does not add Basic Auth. Some sites install a separate Basic Auth plugin for CI purposes — if you do, be aware that Basic Auth transmits the user’s password on every request and should only be used on HTTPS-only sites behind additional network controls.
- Same per-tool capability check. Reaching
wp_create_postvia/wp-json/wp-abilities/v1/abilities/royal-mcp/wp-create-post/runruns the identicaledit_postscapability check as reaching it via the MCP transport. Neither transport can bypass the other’s authorization model.
Royal MCP’s OAuth 2.1 layer authenticates MCP-transport callers only. It does not participate in REST-transport auth. This is by design — keeping the two auth stacks separate means each transport uses the mechanism the WordPress ecosystem already expects, and neither becomes a bypass path around the other. If your organization’s policy prohibits Application Passwords, disable them site-wide via the wp_is_application_passwords_available filter; Royal MCP’s MCP transport will continue to work unaffected.
Threat scenarios and mitigations
The scenarios below cover the classes of attack a security team commonly asks about when evaluating a direct-connect MCP server. For each scenario we describe the attack shape, the mitigation Royal MCP already applies, and the residual risk you should be aware of.
1. AI client compromised, live tokens stolen
An attacker gains access to the AI client (compromised laptop, malware, insider) and extracts live access + refresh tokens from the client’s storage.
Mitigation
Access tokens are 1-hour TTL, so a stolen access token expires within the hour. Refresh tokens rotate on every use — if the attacker uses the refresh token, the legitimate client’s next refresh fails, alerting the user. Every tool call still runs the WordPress capability check on the owning user, so the blast radius is bounded by that user’s capabilities. A stolen editor-scoped token cannot escalate to admin actions.
Residual risk
Within the 1-hour window before the access token expires, the attacker can invoke any tool the token’s user is authorized to invoke. Standard WordPress incident response applies: revoke the OAuth client from Royal MCP → Settings → Connected Clients, rotate the user’s password if the compromise was broader, review the WordPress audit log.
2. Token leaked via debug log, bug report, or screenshot
A token ends up in a text log, a support conversation, a screenshot posted publicly, or a copy-pasted terminal transcript. An attacker discovers it before the user does.
Mitigation
Same short-TTL and rotating-refresh properties as scenario 1. Additionally: Royal MCP does not log tokens or authorization codes to the WordPress error log or Activity Log — token values are redacted before any log write. If a token leaks it’s from the client side or an operator’s environment, not from Royal MCP’s own outputs.
Residual risk
Same 1-hour window. Recommend the operator revoke the affected OAuth client immediately upon discovery rather than waiting for TTL expiry.
3. Token replayed from a different origin
An attacker captures a token and attempts to use it from a different network / device / geographic location than the legitimate client.
Mitigation
Tokens are bearer tokens by design in the OAuth 2.1 profile — they don’t carry origin binding today. Every call still runs the capability check. If your site is behind Cloudflare or a WAF, IP-based or geo-based rules on /wp-json/royal-mcp/v1/* add a layer here at the operator’s discretion.
Residual risk
A leaked token can be used from any origin. Origin-binding is an area of active OAuth extension work (DPoP, mTLS) that Royal MCP will evaluate for future releases; not shipped today.
4. Man-in-the-middle on the token exchange
An attacker positioned on the network path between the AI client and your WordPress site intercepts the OAuth authorization code or the token exchange request.
Mitigation
Every OAuth endpoint requires HTTPS. Royal MCP refuses to complete an OAuth flow over plain HTTP. PKCE-S256 mitigates the code-interception class specifically — an attacker who captures the authorization code cannot exchange it for a token without also having captured the client’s PKCE code-verifier, which never traverses the network in cleartext.
Residual risk
A TLS-terminating MITM (compromised CA, corporate SSL-inspection proxy) can still see the exchange. This is a network-layer concern outside Royal MCP’s trust boundary; addressed by your certificate management practices.
5. Encrypted-traffic sniff / passive network capture
An attacker records encrypted traffic between AI client and site with the intent of decrypting it later.
Mitigation
HTTPS-only enforcement means everything is TLS-encrypted in transit. Royal MCP does not require or accept any downgrade to plain HTTP. Ensure your site’s TLS configuration is 1.2+ with modern cipher suites — use the SSL Checker linked at the bottom of this page to verify.
Residual risk
Standard TLS-lifecycle concerns (private key compromise, retroactive decryption if a weak cipher was in use). Not Royal MCP-specific.
6. Application Password stolen or leaked
A WordPress Application Password (used for REST-transport calls) is disclosed to an attacker.
Mitigation
Application Passwords are per-user and per-name — the compromised password can be revoked in Users → Profile → Application Passwords without disturbing other passwords the same user has issued. The password’s reach is bounded by the owning user’s WordPress capabilities. All standard WordPress security-plugin monitoring (failed-login alerts, geographic anomalies) applies to Application Password auth.
Residual risk
Application Passwords transmit on every request as a bearer credential. Sites that consider this shape unacceptable can disable the feature site-wide via the wp_is_application_passwords_available filter and rely exclusively on the MCP OAuth 2.1 path.
7. Malicious plugin attempts to register a colliding ability namespace
Another plugin on the site registers an ability with a name in the royal-mcp/ namespace, attempting to intercept calls meant for a Royal MCP tool.
Mitigation
WordPress core’s wp_register_ability() refuses to register duplicates — the first registration wins. Royal MCP registers its abilities on wp_abilities_api_init priority 10, and pre-registers its 10 categories on priority 5. A later plugin cannot displace an already-registered ability. Namespace collision as an attack shape is closed by WP core’s registration model, not by anything Royal MCP does specially.
Residual risk
None on this shape. A malicious plugin can still register abilities in its own namespace and register handlers there — the same trust-your-installed-plugins concern that already applies to WordPress generally.
8. Bulk-scan for open MCP endpoints on the public internet
An attacker sweeps IP ranges or WordPress sites looking for exposed /wp-json/royal-mcp/v1/* endpoints they can call without authentication.
Mitigation
Every MCP endpoint requires an authenticated session from the first call. There is no anonymous read path, no unauthenticated tool listing that leaks tool names, and no default-open configuration. A scanner sees the OAuth discovery document (which is intentionally public per RFC 8414) but cannot invoke any tool without completing DCR + admin authorization + PKCE flow.
Residual risk
The public discovery document reveals that Royal MCP is installed. This is a fingerprinting concern, not an authorization bypass. Sites that want to hide the plugin’s presence can add path-blocking rules at their edge (Cloudflare WAF, nginx rewrite) to require an additional header on /.well-known/oauth-authorization-server.
9. Insider abuse: legitimate admin uses AI to exfiltrate data
A user who is legitimately authorized to use Royal MCP uses their access to exfiltrate content the site owner didn’t intend to share (bulk-download all draft posts, export all user emails, etc.).
Mitigation
This is outside the authentication model — the user is who they say they are, and they hold the capabilities the exfiltration requires. The mitigation is monitoring, not prevention. Royal MCP writes every tool invocation to the plugin’s Activity Log with the invoking user, tool name, and timestamp. A future Pro-tier release will add a universal cross-tool audit log with export and 90-day retention; see the Royal MCP Pro pages for the current status of that feature.
Residual risk
Real. Insider abuse is a monitoring and human-controls problem, not an authentication one. Standard mitigations: least-privilege WordPress roles for AI-connected users, review of the Activity Log on a routine cadence, alerting on unusual bulk-read patterns via a security plugin.
Direct-connect vs relay-based architectures
Direct-connect and relay-based MCP servers make different trade-offs. Neither shape is universally better — each fits different customer profiles. The comparison below is architectural, not product-vs-product.
| Property | Direct-connect (Royal MCP) | Relay-based |
|---|---|---|
| Auth boundary | Your WordPress install | Vendor’s relay service |
| Third-party trust boundary in the call path | None — client talks straight to your site | Vendor’s relay sees every request and response |
| Rate-limit ownership | Your site’s server + WAF settings | Vendor’s tier limits (typically per-day action caps) |
| Incident response | You + your hosting provider | You + vendor + your hosting provider |
| Latency | One network hop: client → site | Two hops: client → relay → site |
| Setup friction | OAuth flow with DCR, admin approval per client | Typically vendor account + connector configuration |
| Auth surface exposed to public internet | Yes — your WordPress install is the OAuth server | No — relay handles the public-facing auth |
| Data residency | Everything stays within your site + client boundary | Data traverses the relay, subject to their region + retention policies |
Direct-connect fits organizations that already own their WordPress security posture and prefer to keep the AI-agent trust boundary on infrastructure they operate. Relay-based fits organizations that want to offload the auth surface to a vendor and accept the third-party trust and rate-limit trade-offs that come with it. If your compliance framework mandates that authentication for a specific data class must terminate on your infrastructure, direct-connect is likely the required shape.
Before enabling Royal MCP on a production site
Run through this checklist before authorizing an AI client against a customer-facing site for the first time. Everything here is achievable in about 30 minutes on a site that already follows standard WordPress security practice.
Enforce HTTPS site-wide, TLS 1.2+
Every Royal MCP transport (MCP OAuth, Abilities API, REST) requires HTTPS. Use the SSL Checker to confirm your site accepts TLS 1.2 or 1.3 and rejects lower versions. If your host still permits TLS 1.0 or 1.1, upgrade before enabling MCP.
Verify the authorizing-user capability boundary
Any logged-in WordPress user can complete an OAuth consent for a new MCP client — the resulting client inherits the capabilities of the user who authorized it. Verify this bound holds: authorize a client while logged in as a low-privilege user (Subscriber or Contributor), then try to invoke an Administrator-scoped tool (royal_mcp_connection_health, a settings-write tool) from that client — the call should return a rest_forbidden / capability-denied error. If your policy is that only Administrators may authorize AI clients, restrict which users can log into your site or use a role-management plugin to constrain non-admin roles before enabling MCP.
Decide your Application Password policy
WordPress ships Application Passwords enabled by default. If your organization’s policy is to disable them, apply the wp_is_application_passwords_available filter in a must-use plugin. Royal MCP’s MCP transport works with or without Application Passwords — the choice is about which REST-transport callers you want to permit.
Put a backup + restore plan in place
MCP-connected AI can create, update, or delete content within the connected user’s capabilities. Ensure you have a recent backup and a tested restore procedure before enabling write-capable tools on a production site. SiteVault or any WordPress backup plugin your team already uses works.
Enable WordPress error-log monitoring
Set WP_DEBUG_LOG or an equivalent logging plugin so that authorization failures, capability errors, and unexpected tool errors are recorded and reviewable. Royal MCP’s built-in Activity Log covers plugin-side events; site-wide logging catches everything else.
Consider WAF rules on the MCP paths
If you run Cloudflare or a WordPress WAF plugin, decide whether you want rate-limit or geo-fencing rules on /wp-json/royal-mcp/v1/* and /wp-json/wp-abilities/v1/abilities/royal-mcp/*. This is optional but useful for sites that expect connections from a known set of geographies or IP ranges.
Call royal_mcp_connection_health from your AI client
Once your first AI client is connected, invoke the royal_mcp_connection_health tool. It returns your site’s current auth model, active OAuth session TTLs, negotiated MCP capabilities, and Royal MCP + WordPress version strings. Screenshot the response for your security review file — it’s the plugin’s own confirmation of what’s live.
Independent security review invitation
If your team runs an internal security review, penetration test, or vendor risk assessment on WordPress plugins, we welcome the report. Reach out via the support form and note that you’re requesting a security-review contact. We’ll route the conversation to someone who can answer follow-up questions on specifics that aren’t covered in this document.
Responsible-disclosure reports are appreciated. Royal Plugins does not currently operate a paid bug-bounty program, but we credit contributors publicly (with your permission) and prioritize confirmed vulnerabilities on the fastest-possible release cadence.
What this document does not cover
The scope of this threat model is the authentication and authorization boundary between an AI client and your WordPress site. Related concerns that sit outside that boundary are called out here so your team knows where to look for them.
- Prompt-injection defense. Whether an AI client can be manipulated by adversarial content it reads (via
wp_get_post,wp_search, etc.) is a client-side and model-side concern. Royal MCP faithfully returns the content the caller requested; hardening the AI against injection attacks in that content is the client’s responsibility. - Trust in the underlying AI model. You choose which AI model to connect. Model-provider trust, data-training policies, and content-retention behavior are governed by your agreement with the model provider (Anthropic, OpenAI, etc.), not by Royal MCP.
- Data-classification and content policy. What content is safe to expose to an AI agent is a site-owner decision. Royal MCP’s capability model bounds what a caller can reach; the policy of what should be reachable is set by how you configure WordPress roles and which options you enable in Royal MCP → Settings.
- Universal cross-tool audit log with export and long-term retention. Available as a Pro-tier feature. The Free tier provides per-tool Activity Log entries.
See also
- Royal MCP support hub — full setup guides, tool reference, and troubleshooting index.
- Connecting to Claude — step-by-step OAuth 2.1 + PKCE setup for Claude Desktop and Claude.ai.
- Static OAuth Client Setup — when Dynamic Client Registration is not an option for your MCP client.
- Connect via API Key (skip OAuth) — scoped-key path for scripted or bridge-based clients.
- Connection troubleshooting — diagnostic flow for OAuth, discovery, and transport failures.