Setyenv · Docs
  • English
  • Español
  • 中文
  • 日本語
  • العربية
  • Deutsch
  • Français
  • हिन्दी
  • Bahasa Indonesia
  • Italiano
  • Nederlands
  • Português
  • Русский
  • Türkçe

MCP security model

The MCP server gives an AI app the agent's administrator power, so every request is checked in full, in this order.

Who can connect

  • The switch. While the server is off it answers every request with 403 and lists nothing.
  • Only agent tokens. A request must carry Authorization: Bearer pfam_…. WordPress passwords, application passwords and login cookies are not accepted here, and an agent token is not accepted anywhere else: it opens no other WordPress route.
  • Only administrators, checked every time. The token's owner must hold the manage_options capability on every single request. Demote or delete the owner and their tokens stop at once, not when they expire. Deleting an account deletes its tokens.
  • Browsers from other sites are refused. A request whose Origin is not this site is refused (403), which protects against DNS rebinding. Other origins can be allowed with the pfa_mcp_allowed_origins filter.

The tokens

  • 160 random bits, shown once; the site stores only their SHA-256.
  • Every token expires: 30 days by default, 365 at most.
  • Each one records when it was created, when it expires, when it was last used and from which address, and whether it was revoked.
  • Scope read reaches only the tools that read. Scope write reaches every tool the owner's account may use.

What a write token may do

The agent's permission rules — the same ones the chat's confirmation dialog follows, and like there they govern the tools that change the site — apply:

  • deny — the tool is not listed and a call is refused.
  • allow — the call runs without the second confirmation step.
  • otherwise (the default) — your AI app marks each change as needing your approval and asks you before the call.

Destructive calls take two steps. A call is destructive when it can take away something that exists and no tool can give it back:

  • deleting a data model, record or application, or a workflow;
  • changing a data model with the data-loss guard overridden (force);
  • saving over an active workflow, which puts it back to draft;
  • moving content to the trash, unpublishing it, or replacing its terms;
  • rewriting a user account or a site setting;
  • trashing or marking as spam a comment or a form entry;
  • with WooCommerce, LearnDash or MemberPress: changing an order's status, cancelling it, removing a line, unpublishing a product, unenrolling a student, revoking a membership.

The first call changes nothing and answers with a summary of exactly what would be lost — the data model and how many records go with it, the title of the post, the setting with its current and new value. Only the identical call carrying the single-use confirmation it returned runs, within ten minutes. A different call, a changed argument or a reused confirmation is refused.

What is recorded

Every tool call is written to the agent's trace log: which token (by its id, never the token itself), which tool, the outcome, how long it took, and the arguments — shortened, with anything shaped like a secret removed. Requests turned away at the door are recorded too.

Each token is rate-limited on its own (120 requests a minute by default; the pfa_rate_limit_defaults filter changes it). The agent's AI provider keys are never reachable through the server.

Protocol

MCP over Streamable HTTP, stateless: JSON-RPC messages (or batches of them) sent by POST and answered with JSON — no event stream, no session. Protocol versions 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05. Requests larger than 1 MB are refused, and very large tool answers are cut with a notice saying so.

Troubleshooting

  • 401 — the token is missing, mistyped, expired or revoked. Create a new one.
  • 403 — the server is switched off, the token's owner is no longer an administrator, or the request came from another site's page.
  • 401 even with a valid token — the web server may be dropping the Authorization header before WordPress sees it. WordPress's standard .htaccess passes it on; custom server configurations must too.