Additional menu

Get MemberPress today! Start getting paid for the content you create! Get MemberPress Now
  1. Home
  2. Knowledge Base
  3. Developer Documentation
  4. MCP
  5. MemberPress AI Foundation Agent Safety Measures

MemberPress AI Foundation Agent Safety Measures

MemberPress AI Foundation enforces four safety measures on Model Context Protocol (MCP) write operations. These measures protect a site from unintended or unauthorized changes when an AI client connects through the MCP server. They operate together. A single write call can pass through scope checks, preview defaults, a confirmation gate, and the kill switch before it changes data.

This reference explains each measure, what it protects against, and how the MCP server enforces it. Two measures act when an AI client calls a tool: confirmation tokens and dry-run defaults. One measure governs what scope vocabulary a connection token carries: granular scopes. One measure lets an administrator halt every destructive write instantly: the kill switch.

How the Safety Layer Works

The four measures map to four distinct questions about a write operation:

MeasureNameThe question it answers
AConfirmation tokensWhat may a token DO when a destructive tool runs?
BDry-run defaultsWhat mode does a write tool default to when called?
CGranular scopesWhat scope vocabulary does a connection token carry?
DKill switchCan an administrator halt every destructive write at once?

Measures A, B, and D act at tool-call time. The MCP server evaluates them inside Server::handle_tool_call on every write request. Measure C acts in two places. At tool-call time, the server matches a tool's required scope against the token's granted scopes. At token-issuance time, the default access ceiling caps what scopes a new connection receives.

All four measures live in the MemberPressAI\MCP namespace and run server-side. An AI client cannot bypass them by crafting a request differently, because enforcement happens after the request reaches the server, not in the client.

Measure A — Confirmation Tokens

Confirmation tokens protect destructive operations with a mandatory two-call flow. A destructive tool cannot run on a single call. The first call returns a preview plus a single-use token; the second call must include that token to execute.

One narrow exemption applies: reversible actions skip the gate. memberpress_manage_subscription with action: "resume" and memberpress_manage_sub_account with action: "add" undo rather than destroy, and execute without a token. A single shared allowlist drives the exemption on both the MCP endpoint and the WordPress Abilities surface, so the two gates cannot drift apart. Every other destructive call requires the token.

Six tools are destructive and require this flow:

ToolWhat it does
memberpress_delete_memberDeletes a member
memberpress_refund_transactionRefunds a transaction
memberpress_delete_webhookDeletes a webhook
memberpress_reset_course_progressResets a learner's course progress
memberpress_manage_sub_accountManages a corporate sub-account
memberpress_manage_subscriptionManages a subscription

How the Two-Call Flow Works

  1. The AI client calls the destructive tool with its arguments. The server returns a preview of the effect plus a confirmation token.
  2. The AI client calls the same tool again with the same arguments plus a confirm parameter set to the token value. The server verifies the token and executes the operation.

The token carries three protections. It expires after 60 seconds (the TTL constant in ConfirmationTokens). It is single-use — the server consumes it on the executing call. The server binds it to the exact combination of tool name, arguments, and user ID. A token issued for one operation cannot authorize a different one.

The design rationale, from the development team: “just passing a boolean isn't strong enough consent for an action you can't undo.” A confirmation token proves the caller saw the specific preview for the specific operation before committing to it.

Important: Destructive tools do not accept an execute parameter. The confirmation token flow is the only way to run them. Passing execute: true has no effect on a destructive tool.

The First-Call Response

The first call returns a preview of the effect, a single-use confirmation_token, and the token's remaining lifetime. The following is the response from a memberpress_refund_transaction preview call:

{
  "preview": {
    "dry_run": true,
    "action": "refund_transaction",
    "preview": {
      "transaction_id": 432,
      "amount": "100.00",
      "total": "100.00",
      "user_id": "<user-id>",
      "product_id": 2332,
      "subscription_listing_may_show_inactive": true,
      "subscription_listing_note": "If this was the latest transaction backing the subscription, the WP admin Subscriptions list will show the subscription as \"Active: No\" — that column is derived from the latest transaction's status, not the subscription record itself. The subscription record is unchanged unless you chose to cancel the subscription as part of this refund."
    }
  },
  "confirmation_token": "ct_<token>",
  "expires_in": 60,
  "next_step": "Call memberpress_refund_transaction again with the same arguments plus confirm: \"ct_<token>\" to execute."
}

The token arrives in confirmation_token. The expires_in field gives the remaining lifetime in seconds. The next_step field states exactly how to execute. The inner preview.preview object describes the operation's effect; its fields vary by tool. The subscription_listing_* fields shown here are refund-specific — the Tools Reference and Errors documents cover refund response detail.

When a Token Is Invalid

A token that has expired, has already been used, or is presented with changed arguments fails with a single error. The server does not distinguish the three causes:

{
  "code": -32603,
  "message": "Confirmation token is invalid, expired, or arguments have changed. Call the tool without `confirm` to receive a fresh preview and token.",
  "data": {
    "mp_error_code": "CONFIRMATION_INVALID"
  },
  "request_id": "req_<id>"
}

Match on data.mp_error_code (CONFIRMATION_INVALID), not the generic JSON-RPC -32603 code. The recovery path is always the same: call the tool again without confirm to receive a fresh preview and a new token.

Measure B — Dry-Run Defaults

Write tools default to preview. A write tool does not change data unless the caller explicitly opts in to execute. This protects against an AI client running a mutation it only intended to inspect.

Write tools fall into two shapes:

Non-destructive writes (9 tools) carry an execute parameter. The tool previews by default and returns a preview payload without persisting. Passing execute: true runs the operation live. These tools include memberpress_create_member, memberpress_update_member, memberpress_create_coupon, memberpress_create_subscription, and memberpress_create_webhook, among others.

Destructive writes (6 tools) carry neither execute nor a legacy dry-run parameter. They preview by default and require the Measure A confirmation token flow to execute. The two-call gate is their only execute mechanism.

This split means a reader can tell a tool's risk level from its parameters. A tool with execute is reversible; a tool with no execute and a confirmation requirement is destructive.

Note: This document describes the MCP endpoint (/wp-json/mp-mcp/v1/mcp). Write tools on the MCP endpoint do not expose a dry_run parameter. Non-destructive tools use execute; destructive tools use the confirmation token flow. There is no other way to run a write through MCP. The WordPress Abilities surface reaches the same operations with different mechanics — see The WordPress Abilities Surface below.

The Non-Destructive Preview Shape

A non-destructive write tool called without execute: true returns a flat preview payload. The following is the response from a memberpress_create_member preview call:

{
  "dry_run": true,
  "action": "create_member",
  "preview": {
    "email": "mcp-preview-test@example.com",
    "username": "mcppreviewtest",
    "first_name": "MCP",
    "last_name": "PreviewTest"
  },
  "validation": "passed"
}

This envelope is flat: dry_run, action, preview, and validation sit at the top level, and there is no confirmation token. It differs from a destructive preview, where the preview object is nested one level deeper (preview.preview) and a confirmation_token is present. The validation field reports whether the previewed input would pass validation on execute.

Measure C — Granular Scopes

Connection tokens carry specific scopes rather than blanket access. A token scoped to read members cannot write billing data. This limits the blast radius of any single connection to exactly the operations its scopes permit.

Scope Vocabulary

The MCP server defines the following scopes in ScopeMatcher:

ScopeGrants
readRead access to all data through read tools
write:contentWrite access to content: access rules and coupons
write:membersWrite access to members
write:billingWrite access to billing: subscription management and transaction refunds
write:importsWrite access to member imports
write:webhooksWrite access to webhooks and email reminders
write:coursesWrite access to course operations
fullAll write scopes (satisfies any specific write:* requirement)

The read scope is implicit on every token. The server matches a tool's required scope against the token's granted scopes before dispatch through Server::current_token_has_scope.

Scope alone is not the only gate. Each write tool also declares the WordPress capability it requires (for example, create_users or edit_users), and the server enforces that capability before dispatch. A token with full scope still cannot create users if the token owner lacks the create_users capability. The two gates return distinct errors, and the scope gate runs first: a missing scope fails with SCOPE_INSUFFICIENT; a missing capability fails with CAPABILITY_INSUFFICIENT.

Default Access Ceiling

The default access ceiling caps the scopes the server can issue to a new connection. An administrator sets it through the “Maximum access level for new connections” setting on the MCP Settings tab. The ceiling determines the maximum scope any new token carries, regardless of what the connecting client requests.

The setting has two options: Read Only and Full Access.

When set to Read Only, the wizard hides the write-scope checkboxes entirely — they are not shown greyed out — and the server caps every new connection at read scope. The wizard's Access step shows only the always-granted read scope, with this notice:

Site administrator has set the maximum access level to Read Only. New connections cannot be issued write scopes until this is raised in MCP Settings.

When set to Full Access, the wizard's Access step shows the write-scope checkboxes, pre-checked, and the administrator unchecks any a connection does not need. A new connection then receives write scopes up to what the connecting user's WordPress capabilities allow.

The ceiling enforces server-side at four token-issuance paths. A hand-crafted request cannot bypass the wizard interface to request a wider scope:

  1. Wizard token creation;
  2. OAuth /authorize;
  3. OAuth /token exchange;
  4. Dynamic client registration (RFC 7591).

Important: The ceiling applies at issuance time only. Tightening the ceiling does not narrow scopes on tokens already issued. To narrow an existing connection, revoke it and issue a new one.

The ceiling is an extension of the scope vocabulary, not a separate mechanism. It exists only because Measure C defines the scopes it caps.

Measure D — Kill Switch

The kill switch lets an administrator halt every destructive write instantly. When active, the server refuses every destructive tool call and returns the WRITES_PAUSED error, regardless of the calling token's scopes or any valid confirmation token.

An administrator toggles the switch from the MCP Connected Clients admin page. The control is a Pause all writes button. While destructive writes are active, the page shows this status:

Destructive writes are active — Pauses refunds, cancellations, deletions, and bulk imports for every connected client. Read tools keep working.

The setting persists across regular settings saves, so a routine settings update does not silently turn it off. The server records admin.writes_paused and admin.writes_resumed events to the audit log when the switch changes state.

The kill switch is the broadest measure. Measures A, B, and C constrain individual operations; the kill switch overrides all of them for destructive calls in a single action. It is the appropriate response when an administrator suspects a connection is behaving unexpectedly and wants to stop all destructive activity before investigating.

Note: The kill switch pauses destructive writes only. Read tools and non-destructive write previews continue to function while it is active.

The WRITES_PAUSED Response

While the kill switch is active, every destructive tool call fails with this response, regardless of the calling token's scopes or any valid confirmation token:

{
  "code": -32603,
  "message": "Destructive writes are paused by an administrator. Try again later or contact the site owner.",
  "data": {
    "mp_error_code": "WRITES_PAUSED"
  },
  "request_id": "req_<id>"
}

The switch gates the entire destructive-write path: even the first (preview) call of a destructive tool returns WRITES_PAUSED, so the confirmation flow cannot begin. As with all tool errors, match on data.mp_error_code (WRITES_PAUSED), not the generic -32603.

The WordPress Abilities Surface

On WordPress 6.9 and later, AI Foundation also registers tools through the WordPress Abilities API, exposed via the WordPress.org MCP Adapter. This is a second programmatic surface that reaches the same underlying operations as the MCP endpoint, with three mechanical differences:

  • Preview control. Write operations on the Abilities surface expose a dry_run parameter: they preview by default, and the caller passes dry_run: false to run live — where the MCP endpoint uses execute: true instead;
  • Write gate. Write access through the Abilities surface is gated on the mpai_use_mcp_write capability;
  • Coverage. The surface registers the core tool families; add-on tool surfaces are exposed via MCP only.

The safety model carries over: the surface enforces the same per-tool WordPress capability requirements, destructive operations require the same confirmation-token flow (including the same reversible-action exemption), and the kill switch pauses destructive Abilities writes just as it pauses MCP writes. Reaching the operations through a different protocol does not relax the gates.

Activity Log

The Activity Log records MCP tool events for audit and troubleshooting. It lives on the MCP Connected Clients tab and lists each event with four columns: Time, Client, Tool, and Result. The Result column shows the stored outcome code for each call — success, error, confirmation_required, confirmation_invalid, confirmation_race_lost, scope_insufficient, capability_insufficient, writes_paused, and a small family of preview/encoding failure codes.

The log makes the destructive confirmation flow auditable: a destructive tool's preview call logs confirmation_required, and its execution logs success — the two calls are distinguishable. A non-destructive tool's preview and live execution both log success; call arguments are deliberately never logged, so the log does not record whether execute was passed. How long entries are kept is governed by the Log Retention setting on the MCP Settings tab (default 30 days).

A Note on AI Client Behavior

Some MCP clients automatically complete the two-call confirmation flow without surfacing the confirmation step to the user. A client may show a preview and then issue the second call internally, so the operation appears to run in one step. This is client-side workflow behavior, not a gap in the safety layer. The server still enforces the two-call requirement, and the client supplies a valid token on the second call.

To observe the confirmation gate directly, use a tool that issues each call explicitly, such as Postman or curl. A client that abstracts the flow hides the two calls, but the server still requires both.

Was this article helpful?

Related Articles

computer girl

Get MemberPress today!

Start getting paid for the content you create.