Skip to content

WPShout Newsletter

Sign up for news, tips, and insights to help you build better websites.

Newsletter - Top banner

Self-Hosted vs Hosted WordPress MCP: Privacy, Security and the Multi-Site Trade-off

A self-hosted MCP server runs inside your WordPress site, and the AI client talks straight to it. A hosted MCP server runs on someone else’s infrastructure and reaches your site through a stored credential or a platform connection. That one difference decides where your credential lives, how far a single leaked credential reaches, and what uninstalling actually removes.

To compare the two models with evidence rather than feature lists, we ran the same everyday prompts on three setups: Easy MCP AI for the self-hosted model, and two hosted services that reach a site in different ways. WPVibe is a relay, where the provider’s servers call your site’s REST API. WordPress.com’s MCP is a platform connection, reaching your site through Jetpack. Easy MCP AI is built by Themeisle, the team behind WPShout.

What follows is the teardown: the request paths with real log lines, the credential each model holds, who the AI acts as, what each one logs, and the exact removal steps. If MCP on WordPress is new territory, the WPShout guide to WordPress MCP covers the basics first.

lf-Hosted vs Hosted WordPress MCP

How the tests were run 🧪

The test rigs were two sandbox sites: a WooCommerce store (“Harbor Coffee Co.”, 32 users, 24 orders) and a blog (“Trail Notes”), both running WordPress 7.1.4 on PHP 8.3. The self-hosted setup ran Easy MCP AI on each site. The relay setup ran WPVibe’s vibe-ai 1.21.3 plugin. The platform setup ran Jetpack 16.3.1 on a free connection, with MCP switched on in the WordPress.com account preferences.

The client was Claude Code 2.1.312 in headless mode (model Sonnet), with a fresh session per run and three runs per prompt. The test harness sent its requests with a custom user agent, t1-mcp-test-client/1.0, which is what the self-hosted log line below shows. To watch the wire, a small must-use plugin on the sites logged the time, the client IP, the user agent, the method, the path and the auth type of incoming requests, never a password or token value. In the lines shown below, IPs are truncated to a range or redacted, and Jetpack’s signed parameters are removed.

The request path: who sits between the AI and your site 🔌

Request paths: self-hosted goes AI client to site; hosted goes AI client to service to site

Self-hosted keeps the path short. The AI client POSTs straight to an endpoint on your site, /wp-json/easy-mcp-ai/v1/mcp in the tested plugin, authenticating with a Bearer token (OAuth 2.1 with PKCE, or an API key). One hop, no third party in the middle:

2026-11-02T12:01:13+00:00  [client IP redacted]  t1-mcp-test-client/1.0  POST  /wp-json/easy-mcp-ai/v1/mcp  auth=Bearer

With a relay, your site is the last hop. The client connects to mcp.wpvibe.ai, and WPVibe’s servers call your site’s REST API from Cloudflare addresses over HTTP Basic auth, using a stored WordPress Application Password:

2026-11-02T12:41:49+00:00  2a06:98c0::/32  WPVibe/1.0 (+https://wpvibe.ai)  POST  /?rest_route=/wp/v2/posts/6  auth=BasicCode language: JavaScript (javascript)

Content edits like the one above travel over the core REST API; WPVibe’s WP-CLI tool uses the vibe-ai plugin’s own route (/wpvibe/v1/cli/run) on the same Basic credential.

With a platform MCP, the client connects to WordPress.com, and WordPress.com reaches your site as Jetpack signed requests from Automattic’s 192.0.x range. On arrival, an AI call looks like any other Jetpack call:

2026-11-02T12:04:18+00:00  192.0.87.0/24  Jetpack by WordPress.com  POST  /?rest_route=/wp/v2/posts/46&_for=jetpack  auth=X_JETPACKCode language: JavaScript (javascript)

(The logged path also carried Jetpack’s signed token key ID and timestamp parameters; removed here.) Three wire paths, three very different places for a credential to live.

The code you can read differs along the same lines. Easy MCP AI’s server is the plugin itself, published on WordPress.org. WPVibe’s site plugin is open source under GPL, but its own README describes the hosted MCP server (gateway, OAuth, tool registration) as a closed-source service, separate from the plugin. For WordPress.com’s MCP server, no published source turned up; the public pieces are Jetpack and Automattic’s client-side proxy tools.

The credential each model holds 🔑

Easy MCP AI issues its credentials on the site itself: OAuth 2.1 with PKCE S256 and dynamic client registration, or plain API keys. Every credential is tied to one WordPress user, the tokens live in the site’s own database, and the consent screen offers Full access, Read-only or Custom per connection. On top of that sit a per-credential tool allowlist, an optional IP allowlist, a default rate limit of 120 requests a minute per credential, and a Pause AI access switch. Your database holds the keys, which also makes them your job to protect.

WPVibe’s credential is a standard WordPress Application Password. Each site is approved once: the AI’s connect_site call returns a one-click link, valid for one hour, that opens wp-admin, and approving creates the password, which WPVibe then stores on its servers. From that point, one AI-client connection reaches every approved site. It is one approval per site, not zero setup.

Users > Profile > Application Passwords list showing the password WPVibe-20261102-095025
The Application Password WPVibe created on the store during approval, under Users > Profile.

WordPress.com keeps two halves: the Jetpack connection on your site, and an OAuth grant on your WordPress.com account with the scope “global”. MCP is switched on in the account preferences, and the consent screen has no per-connection access level and no site picker. Authorizing covers the account.

Who the AI acts as 👤

Easy MCP AI runs each call as the WordPress user behind the credential. In a live check on the blog, an Editor account was given an API token; the update succeeded, the revision was credited to that editor, and the audit and change log rows carried that user’s ID. An Editor token can do at most what an Editor can do, and capability checks still apply on top of any tool allowlist.

WPVibe acts as whichever account approved its Application Password. Approved as an Editor on the blog, content edits worked and were credited to that editor, while run_wp_cli returned 403 because it needs manage_options, and a read of /wp/v2/plugins returned 403 rest_cannot_view_plugins. After approval, WPVibe flags the limited access on screen (“content tools work but site-wide actions … will be blocked”), and WPVibe’s own guidance is to approve as an administrator.

What the AI can reach 🧰

Easy MCP AI exposes dedicated tools for common jobs (users, posts) plus a generic REST read; fetching orders used it on the WooCommerce route. WPVibe exposes 37 tools; the test runs relied on two general-purpose ones: rest_api, which can call any REST route, and run_wp_cli. WordPress.com’s server exposes 17 tools that mix site facades (content authoring, plugin and user management) with account-level tools (domains, DNS, checkout URL, create site).

On the three everyday prompts, all run on the store: listing users succeeded 3/3 on all three setups, and renaming a post succeeded 3/3 on all three. Recent orders came back 3/3 from Easy MCP AI and WPVibe, and 0/3 from WordPress.com, where the exposed content types were Products, Forms and Form Responses and an orders operation was rejected as unsupported. Timings were similar, about 10 to 23 seconds per prompt, with WordPress.com’s user listing up to 42 seconds, and Easy MCP AI and WPVibe produced zero tool errors. For a deeper look at store access, see WPShout’s guide to WooCommerce MCP.

What each model logs on your site 📜

Self-hosted keeps the evidence on the site. In the tests, Easy MCP AI wrote one audit row per tool call (tool, arguments, result, IP, the WordPress user, duration; 30-day retention by default) and a change history with before and after values for each write made through MCP, linked to the audit row and to the WordPress revision. One row from the blog, made with the Editor API token:

id 33 | audit_id 32 | tool_name wp_update_post | action update | object_type post | object_id 6 | wp_user_id 3 | auth_source legacy | changed_fields ["post_title"] | revision_id 21 | capture_mode hooked | created_at 2026-11-02 12:27:20
before_value: {"post_title":"Trail Etiquette 101","post_status":"publish","post_type":"post", ...}
after_value:  {"post_title":"Edited with an Editor API token","post_status":"publish","post_type":"post", ...}Code language: JavaScript (javascript)

WordPress revisions record the user, never that an AI made the change. The change history is the place that shows the AI origin (“By: admin / AI client (OAuth)”).

Easy MCP AI Change history screen with before and after rows credited to admin / AI client (OAuth)
Activity > Change history on the store: before/after rows, each linked to its originating call.

WPVibe’s on-site audit table is an Approval Log of destructive operations. After 13 rest_api calls, 3 run_wp_cli calls and 12 list_sites calls, including three title edits, it held zero rows; reads and ordinary edits are not recorded there. A wpvibe_recent_activity option keeps short summaries such as “Post updated: …” with a timestamp and no before value.

WordPress.com stores nothing MCP-specific on the site. Its calls arrive as Jetpack signed requests, so the site cannot tell an AI call from any other Jetpack traffic, and the history lives in Jetpack’s Activity Log on WordPress.com.

Uninstall: what removal actually removes 🧹

Removal is where the models diverged, and each was uninstalled on the store to see what access remained.

Easy MCP AI: wp plugin uninstall easy-mcp-ai --deactivate removed all 10 tables, about 80 options, the user meta and the cron events. An OAuth token that was still valid then got HTTP 404 rest_no_route, because the endpoint went away with the plugin. Access ends with the uninstall; the AI client keeps a dead connector entry until you remove it there.

WPVibe: wp plugin uninstall vibe-ai --deactivate removed the plugin, its tables and all but one of its options, but not the Application Password. With the plugin gone, the relay still read the store and edited a post. Revoking the password under Users > Profile > Application Passwords cut access: further calls returned 401 incorrect_password. WPVibe’s own remove_site call also revokes the password on the site; verified on the blog with the plugin still installed, where the admin account had zero Application Passwords afterwards. To its credit, WPVibe documents on its admin page that deleting the plugin is not enough: “Deactivating or deleting WPVibe does not disconnect this site and does not revoke the WordPress Application Password it uses …” Pricing and WPVibe alternatives are a separate WPShout piece.

WPVibe admin page warning that deleting the plugin does not revoke its Application Password
The “Disconnecting this site” box on WPVibe’s admin page.

WordPress.com: on the WooCommerce store, deactivating and deleting Jetpack did not end access. posts.update still changed a title, with requests still arriving as Jetpack signed requests from 192.0.x. The reason: WooCommerce bundles the Jetpack Connection package, and Jetpack’s deactivation is a soft disconnect while another plugin uses the connection. A full disconnect of the site, run through the Connection Manager (disconnect_site( true, true )), ended it: posts.update returned “You do not have permission to access this site”, and the store dropped off the account’s site list. 67 jetpack_* options remained in wp_options.

The removal checklist for each tested setup:

# Self-hosted (Easy MCP AI): the credential dies with the plugin
wp plugin uninstall easy-mcp-ai --deactivate

# Hosted relay (WPVibe): the plugin is step one of three
wp plugin uninstall vibe-ai --deactivate
# 2. wp-admin > Users > Profile > Application Passwords: revoke the WPVibe password
# 3. Remove the site in your WPVibe account (or use its remove_site tool)

# Hosted platform (WordPress.com via Jetpack)
# Removing Jetpack was not enough on a WooCommerce store.
# What ended access in the test: a full disconnect through the Connection Manager
wp eval '( new Automattic\Jetpack\Connection\Manager() )->disconnect_site( true, true );'Code language: PHP (php)

The common lesson: uninstalling the plugin ends access when the credential lives in the plugin. When it lives elsewhere, you have to kill the credential itself.

Multi-site: one connection or many 🌐

Two multi-site prompts ran as well: list the latest posts on both sites, and rename a post on the second site. Easy MCP AI handled both 3/3 with two connections, one per site, each with its own sign-in and consent. WPVibe handled both 3/3 through a single connection, after each site had been approved once. These two prompts were not run against WordPress.com; its model is one account covering every site on it.

One leaked credential, three radii. A leaked Easy MCP AI token exposes one site, as one user, within that credential’s scope and rate limit. On the relay, each site’s Application Password was created on that site, for whoever approved it there; a leaked WPVibe account connection reaches every approved site, each as the account that approved it. A leaked WordPress.com grant reaches every site on the account, plus account-level tools such as domains and DNS. The convenience of one connection and the size of the blast radius are the same property.

The same trade-off shows up in the AI client’s own guardrails. The Claude app lets you set each connector tool to allow, needs approval or block, and the setting belongs to the tool on that connector. With one connector per site, as in the self-hosted setup, you can let a staging site run edits freely while a live store asks first. Many WPVibe tools take the target site as an argument, with 25 of its 37 tools requiring a site URL, so one choice for a tool applies to it on every site linked to the account; the connector page has no per-site setting.

Claude app tool permissions for the WPVibe connector: allow, needs approval or block per tool, with no per-site option
Tool permissions for the WPVibe connector in the Claude app: one setting per tool, for every linked site.

When hosted MCP is the better choice ✅

Self-hosted wins nothing by default; it trades convenience for control. Hosted is the better choice when:

  • You run many sites and want one connection. The relay reached every approved site and the platform exposed every site on the account through a single sign-in, while the self-hosted setup needed a separate sign-in and consent per site.
  • The work is account-level, not site-level. WordPress.com’s MCP exposes account-level tools for domains, DNS, plans and checkout URLs; Easy MCP AI’s server runs inside the site.
  • You do not want to operate the endpoint. With Easy MCP AI, the tokens sit in your database, the endpoint on your site, and the rate limit and log retention settings are yours to own. A hosted service moves the MCP server off your site, while a relay plugin or the Jetpack connection still lives on it.

Self-hosted vs hosted MCP: the trade-off in one table ⚖️

Self-hosted MCPHosted MCP
Request pathAI client to your site’s endpoint, one hopAI client to the provider, then your site: REST API over Basic auth (relay) or Jetpack signed requests (platform)
CredentialOAuth 2.1 token or API key, tied to one WordPress userWordPress Application Password (relay) or Jetpack connection plus an account OAuth grant, scope “global” (platform)
Where storedYour site’s databaseThe provider’s servers, with a revocable half on your site: the Application Password entry (relay) or the Jetpack connection (platform)
Acts asThe credential’s WordPress userThe WordPress account that approved each site (relay)
Logs on siteAudit row per call plus before/after change history, in the tested pluginDestructive-operations log plus short activity summaries (relay); nothing MCP-specific (platform)
Reach per credentialOne siteEvery approved site per WPVibe account connection (relay); every site on the account (platform)
Store dataOrders listed 3/3 in the testsOrders listed 3/3 through the relay; no orders access on the platform (0/3)
Per-tool rules in ClaudeSet separately for each site (one connector per site)One setting per tool covers every site on the connector (relay, as tested)
Code you can readThe whole server (the plugin on WordPress.org)The site plugin or Jetpack; the relay server is closed source, and no published source was found for the platform server
Removal stepsUninstall the plugin, then remove the dead connector in the AI clientUninstall the plugin, revoke the Application Password, remove the site (relay); a full disconnect of the site, since removing Jetpack was not enough on a WooCommerce store (platform)
Two models, tested on 2 November 2026. The hosted column spans the relay and platform variants.

The short version: pick the model by how many sites one connection should reach, and by where you want the credential, the logs and the removal steps to live. Choose per blast radius, not per feature list. For a wider look at the field beyond these three test setups, see the WPShout roundup of the best WordPress MCP plugins.

👉 To run the self-hosted setup from these tests, grab Easy MCP AI from the WordPress.org directory and connect your AI client to your own site.

Yay! 🎉 You made it to the end of the article!
WPShout Editorial
Share:

0 Comments
Most Voted
Newest Oldest