メインコンテンツまでスキップ

Security Model

MCPify is built security-first. The guiding rule: all authorization is decided on the server, and the browser is never trusted. Whatever an agent claims, the plugin re-checks the permission for every call.

Access tiers​

Every tool declares the tier it requires. The access gate enforces it on each request.

TierWho may callTypical tools
AnonymousAnyone (public, like the WordPress REST API)Public reads: search, products, categories
SessionAny visitor carrying a valid same-session nonce - a guest countsCart and checkout, which must work for shoppers who never sign in
AuthenticatedA real logged-in accountA customer's own orders, memberships, courses, subscriptions
CapabilityA user whose role holds a specific WordPress capabilitySensitive or custom tools you restrict

Reads of already-public content are public by design - the same information is on your pages and in WordPress' own REST API. Anything that acts, or that touches a specific user's data, needs more.

Why Session and Authenticated are separate

A wp_rest nonce issued to a logged-out visitor is bound to the nonce tick, not to an account, and the front-end bridge prints one on every public page so guest carts can work. That makes a nonce a useful same-site and CSRF control, but it is not proof of identity. So a nonce alone can never satisfy the Authenticated tier - otherwise "require login" would mean "scrape the homepage". Tools that read a person's own data check for a real login.

Writes always require a nonce​

Every write tool requires a valid same-session WordPress nonce (X-WP-Nonce) in addition to its tier check. This is standard WordPress CSRF protection: a write cannot be triggered by a request that did not originate in the visitor's own session.

The in-page WebMCP bridge supplies the nonce automatically. Over REST, you send it in the X-WP-Nonce header. Server-side agents use an API key instead.

Rate limiting​

Two limits protect your site from a runaway or abusive agent:

  • Per-caller - how many calls a single identity may make in a window. A tool that declares its own stricter limit (coupon attempts, for example) gets its own counter, so cheap calls cannot drain a sensitive tool's budget.
  • Global - caps total tool traffic across the whole site, which resists IP rotation.

Metering runs before the permission decision, on purpose. A rejected call still costs the caller quota, so wrong API keys and unknown tool names cannot be guessed for free.

Requests over the limit are rejected with a rate-limit error and never reach the tool. Both limits are configurable in MCPify > Settings.

Everything denied is logged​

The activity log records denials as well as successes - forbidden, rate_limited, invalid_params and tool_unavailable all appear. A product whose whole value is a controlled boundary has to let you see when that boundary is being tested, so an attempt on your site shows up in your own dashboard rather than passing silently.

Per-agent API keys (Pro)​

A server-side agent authenticates with the X-MCPify-Key header instead of a browser session.

  • Keys are stored hashed (SHA-256). The database never holds a usable credential, and a key cannot be displayed again after you save it - record it once, at creation.
  • Minimum length is 32 characters; shorter entries are rejected with a notice rather than silently dropped.
  • The comparison is timing-safe.
  • An API key can never reach a capability-gated tool. That is enforced in the identity itself, not by convention.

Confirmation for sensitive actions (Pro)​

Any tool you flag in Settings > Require confirmation refuses to run on first call. It returns a single-use token, bound to that caller, that tool and those exact parameters, valid for five minutes. The call proceeds only when the token comes back as _confirm.

Stated honestly: no server-side check can prove a person approved something, because the agent makes the request either way. What this does guarantee is that asking first becomes a protocol the agent must follow rather than a suggestion it can drop, and that one approval can never authorise a different or repeated action. The pending response is success: false with HTTP 202, so a client that checks only the success flag can never record an action that did not happen.

Output sanitization​

Tool output is sanitized before it leaves the server. This matters for AI agents specifically: stored content - a product description, a post body, a title - could otherwise carry text crafted to hijack the agent, a form of prompt injection.

This is defence in depth, not a boundary. A denylist can always be worded around; the durable answer is provenance, meaning the consuming agent treats returned human-authored text as untrusted data. Do not rely on sanitization alone for anything high-value.

What MCPify never exposes​

Even through read tools, MCPify withholds:

  • Administrator or user email addresses and usernames
  • Private or draft content
  • Password-protected posts - these are still publish, and WordPress' password gate lives in get_the_content() rather than in a content filter, so MCPify checks for a password explicitly on every read path and excludes protected posts from every listing
  • Private post meta, secrets, keys, or credentials
  • Anything not already public on your site

WooCommerce order tools are scoped to the logged-in customer's own orders. Guest order tracking works on guest orders only and needs the order number plus the matching billing email; a registered customer's order cannot be opened that way, because that would undercut the login their own order tools require.

Privacy​

MCPify's tools collect no telemetry and make no external calls - everything runs inside your WordPress install, and the optional activity log holds recent tool calls with no personal data (clear or disable it anytime).

Three optional things can send or store data, and only once you turn them on:

  • The bundled Freemius SDK contacts freemius.com for licensing and updates. Its usage tracking is opt-in and skippable, and every free tool works without an account.
  • Webhooks (Pro) POST a small metadata-only notification - tool name, success, status, time, site URL - to URLs you enter yourself. Never parameters or results.
  • Back-in-stock notifications (Pro) store the account email of a signed-in customer who asks to be notified. The address is taken from their account, never from the request, so nobody can be subscribed by someone else.

Hardening tips​

  • Enable only the tools you actually want agents to use (MCPify > Tools).
  • Use granular per-tool overrides to tighten specific tools. Overrides can only ever raise the requirement, never lower it, so a misconfiguration cannot open a capability-gated tool to the public.
  • Set rate limits appropriate to your traffic, and a tight one on anything that reveals whether a value is valid.
  • Allowlist deliberately. Custom post types, ACF fields and submittable forms all start empty and expose nothing until you name what may be shared.
  • Flag anything consequential under Require confirmation.
  • Review the activity log periodically, including the denials.

Known limitations​

Stated plainly, because knowing where a control stops is part of using it:

  • Behind a reverse proxy (Cloudflare, nginx, a load balancer), rate limiting and the IP allowlist read REMOTE_ADDR only. Ignoring X-Forwarded-For is the safe default - trusting it would let anyone spoof past the allowlist - but it does mean that behind a proxy every anonymous caller shares one bucket and the allowlist sees the proxy rather than the visitor.
  • Without a persistent object cache, the rate-limit counter is read-then-write, so it can undercount under heavy concurrency.
  • Guest carts are readable cross-origin. This follows from the Session tier existing at all, and WooCommerce's own Store API has the same property.