WPAgently connects a supported MCP client to a WordPress site and uses it to produce verified blog posts and landing pages. On WordPress 7.0 or newer, an integrated chat through the native WordPress AI Client is available as an alternative. The fully documented external core path uses Claude Code, Claude Desktop, Codex, or ChatGPT. Cursor, Windsurf, Visual Studio Code, Gemini CLI, OpenCode, Cline, Roo Code, Kilo Code, Zed, GitHub Copilot, Amazon Q, and Antigravity receive guided configurations. Claude Desktop uses a locally executed MCPB bridge. The core is a verification layer that catches the five silent failure modes that naive agent setups fail on. The architecture consists of four layers.
Four layers
- MCP tools (server-side, atomic)
These customer documents are bundled with Skills 0.4.86 and describe the Companion 0.4.117 and Power 0.6.35 release set. Their evidenced scope covers 414 registered Companion abilities, including 317 in the recommended Content profile, 440 combined Companion and Power schemas, 414 Companion tools, and 34 reviewed public paths. They describe the bounded capability contracts, not a promotion, public availability, or a particular sales or update channel. The wordpress/mcp-adapter library exposes registered abilities as MCP tools. Important: the adapter does not expose abilities globally. Each provider creates its own server via create_server() on the mcp_adapter_init action. The Companion registers a dedicated server for this purpose (see below). The adapter does not provide a secure pre-execution hook for an additional WPAgently approval.
- CLI (wp-agent)
A dependency-free Node program (built-ins only). It orchestrates the ARTICLE (Markdown to verified draft) and LANDING (section list to verified page) pipelines and wraps them in the verification and safety layer. The CLI preferably signs in through browser OAuth (login, logout), lists abilities (abilities), and checks the connection (preflight, doctor). init optionally creates static MCP connection files with a separate Application Password for Claude Code, Codex, Cursor, Windsurf, Visual Studio Code, Gemini CLI, OpenCode, Cline, Roo Code, Kilo Code, Zed, GitHub Copilot, Amazon Q, and Antigravity. Alternatively, init registers Claude Code or Codex in user scope after explicit approval. Both paths pass only the canonical MCP endpoint to the respective official CLI. Claude Code completes OAuth through /mcp. Codex requires the separate, deliberate codex mcp login <name> command. No WordPress password enters a process argument or the global configuration. The pipelines create content as drafts first. The current SEO and status writers return HTTP 409 with manual_only after a fresh read or hash check, so SEO changes and publishing happen manually in WordPress.
- Agent skills (Claude Code / Codex)
The wp-site-context skill loads only the needed site context, design profile, matching site skills, and relevant memories before a task. It also checks the design-direction list. If exactly one direction is active, it loads and applies that direction's voice, rules, avoid patterns, and validated profile. Inactive directions are never applied implicitly. The wp-article, wp-landing, wp-reference, and wp-content skills provide judgment and guardrails for the verified tools. wp-feedback turns the redacted system diagnostics into a reproducible report for user review and never sends data automatically. Claude Desktop uses a local .mcpb as a transport to the remote endpoint. The bridge bounds input and output, adopts session data only from successful responses, sends the negotiated protocol version, and terminates the remote session during local shutdown. Claude Code and Codex connect directly through Streamable HTTP.
- Integrated WordPress chat (optional on WordPress 7.0 or newer)
The chat uses the native WordPress AI Client and the AI provider configured by the Customer under Settings > Connectors. During planning, the model may execute only the explicit read-only allowlist. Write Abilities are returned solely as structured proposed actions. Every action has its own short-lived identifier, is approved exactly once, and executes as a safe Editor. The administrator context is removed before the Ability call and restored afterward. The preview accepts only paths on the same site, removes page scripts and module preloads server-side, and additionally blocks scripts and forms through the iframe sandbox. Internal links are intercepted and reloaded with preview mode and a valid nonce. Encoded path separators, control characters, dot segments, and paths outside the WordPress home directory are rejected. An invalid or expired preview request fails closed with HTTP 403. A clicked visible element can be attached to the next plan as bounded, explicitly untrusted website context. Both browser and server remove the complete query string and URL fragment, and the selector is never executed.
The dedicated MCP server
A dedicated MCP server named WPAgently runs at /wp-json/wp-agent/mcp. It only comes into existence if the mcp-adapter library is loaded, because the mcp_adapter_init action only fires then. The MCP tool names are the ability names with the slash turned into a hyphen, for example wp-agent-create-post-from-markdown. The valid input schema for each tool is in tools/list and is the binding source of truth. Six MCP resources expose the explicitly stored site context, design profile, design directions, skills, and memories, plus the redacted system diagnostics. Diagnostics exclude credentials, users, content, full URLs, and plugin paths and do not trigger automatic transmission. Site skills explicitly approved for this purpose are additionally registered as native MCP prompts.
MCP schemas remain finite and closed even for provider-defined data. Native block attributes and supports travel in the versioned, integrity-bound {format,json,sha256,bytes} envelope. Elementor and Beaver Builder use their own {json,sha256,bytes} envelope, without format, for dynamic document, element, setting, and patch values. The server verifies byte length and SHA-256, decodes the value, and validates it again against the relevant provider boundary before a response or mutation. A value that exceeds that boundary or cannot be normalized safely makes the relevant contract fail. It is not truncated or emitted as an open object. This boundary applies only to the documented dynamic data surfaces and does not expand any write permission.
The OAuth 2.1 server publishes two separate resource-bound targets. MCP clients receive tokens only for /wp-json/wp-agent/mcp. The CLI receives tokens only for the WordPress REST resource at /wp-json. PKCE S256, dynamic client registration, short-lived access tokens, rotating refresh tokens, revocation, and exact resource binding prevent a token from being reused between CLI and MCP. The server advertises offline_access, accepts it only together with the required wpagently scope, and preserves the normalized scope set through every refresh rotation. The CLI writes its tokens atomically to a private local file. A per-file lock prevents concurrent refreshes from reusing the same rotating token.
Connection history stores no passwords or tokens. It records only the time at which each registered connection was used successfully and the error code of a later Application Password failure. This enables a focused administrator warning when a previously working connection breaks. The external diagnosis runs only when requested. It calls the REST index, MCP OPTIONS endpoint, and OAuth metadata through the public site address with a response-size limit and no credentials. Its support state retains only the status code, duration, expected JSON shape, and hints about a CDN, proxy, or known active security plugin. Remote response content is never stored.
A central gate applies before exposure. A valid license, the master switch, and the domain lock must all agree. The active profile (readonly, content, design, full, or custom) determines which abilities are registered and exposed through MCP. A disabled tool therefore does not exist for the agent.
Abilities from other plugins are discovered automatically but never exposed automatically. An administrator must explicitly approve each external ability in the control center. The provider's permission callback remains active, and WPAgently cannot verify the behavior of external write paths.
The Live Editor is a separate short-lived browser workspace under WPAgently > Live Editor. An administrator binds it to exactly one non-administrator who has editing rights. Five abilities read session status, enqueue bounded Gutenberg or Elementor commands, and retrieve results. The browser and server recheck authorization before every command. Every write needs an individual visible approval. A SHA-256 hash over blocks and post fields rejects Gutenberg changes based on stale state. WordPress autosaves remain locked during the session. Gutenberg content is persisted only through the separately approved save-post command. New and changed blocks must match an approved namespace and the schema actually registered in the browser. The save gate rejects active content and oversized structures while preserving safe existing blocks with provider schema inconsistencies or deactivated plugins. After individual approval, Elementor mutations run server-side with a separate document hash, revision, readback, and frontend verification. Expired and foreign sessions remain closed.
Documented capability groups
The full reference records the evidenced capability boundaries of the Companion 0.4.117 and Power 0.6.35 release set, bundled with Skills 0.4.86. It does not assert promotion or availability. The working groups are:
- Content
- Taxonomies
- Media
- Menus
- Comments
- Administration
- SEO
- Design
- Builders
- Verification
- Safety
- Integrations
- Entry data
- Order data
- Agent knowledge
- Live Editor
The Abilities called by the ARTICLE and LANDING pipelines depend on the workflow, an existing slug, and the selected options. Their fixed core paths are:
wp-agent/create-post-from-markdowncreates a new article draft from Markdown. It is the verified ARTICLE pipeline's only writer for new article content. Its update mode is currentlymanual_only.wp-agent/upsert-patterncreates a landing-page draft from declarative sections or updates a uniquely identified page when the installed Ability declares the required write semantics.wp-agent/render-verifychecks the effective rendering,core/freeform, builder mismatches, and the other documented render checks after a mutation.render-checkis only the internal name of this pipeline step, not the Ability called there.wp-agent/refresh-hooksthen catches up on follow-up work that normally only happens on an editor save. This includes rewrite rules, the post cache for recently changed posts, and the Rank Math sitemap cache when Rank Math is active. This final step is deliberately non-blocking.
For one exact existing slug, wp-agent/get-post reads the current state. Multiple or non-exact results abort without selecting a post. A local image may additionally call wp-agent/create-direct-media-upload, wp-agent/get-post, and wp-agent/set-featured-image. A remote image is uploaded through the WordPress media REST API, followed by get-post and set-featured-image. Requested SEO data uses get-seo-meta and set-seo-meta; requested publication uses get-post and set-post-status. The CLI checks the declared write semantics before its first mutation. Because set-seo-meta, set-post-status, and article updates are currently manual_only, a combined run requesting one of those operations stops before creating a draft.
Additional abilities are available through Companion or Power, but ARTICLE and LANDING do not invoke them automatically:
- Five Rank Math redirection abilities list and read existing rules or create, update, and delete exactly one local exact source. A collection hash, object hash, atomic lock, collision and cycle checks, provider read-back, and verified rollback protect changes. Complex existing rules remain readable but read-only.
- write-theme-file (write): writes a file into the active (child) theme, allows only the extensions .html, .json, and .css, and rejects path traversal.
- disable-power (write): a panic switch that disables the separate wp-agent-power plugin. The integration is functional and is checked in the power suite.
The Companion 0.4.117 and Power 0.6.35 release set, bundled with Skills 0.4.86, provides exactly 34 Free core abilities without a licence key or call limit. Its documented contracts are not a promise about future provider paths, catalog size, or schemas. Installed profiles and provider gates still determine exposure. The separate Operation Ledger is an administrative feature, not an ability or MCP tool. It records write attempts with succeeded, failed, rejected, or recovery_required status. New Pods models and missing Pods fields fail closed with HTTP 409 before mutation. Code Snippets single use has no ability and remains a later Power path. The Elementor migration produces a preview only, and content-model migration creates only new ACF-to-ACPT Lite definitions. The Live Editor finalizes only approved GenerateBlocks and Kadence provider attributes created in the Gutenberg editor. Beaver Lite exposes no native user templates, so the corresponding abilities fail closed. The primitive global-settings map is a separate real path. GenerateBlocks writes neither rich text nor arbitrary plain text. Values from the commercial Meta Box Settings Pages runtime, as well as further licensed or proprietary gaps, are not externally certified. Power only inspects extension archives in a bounded way and does not extract, install, update, overwrite, or activate a generic extension. Deferred extensions are listed in the roadmap.
The complete surface is in the Abilities reference. For a specific installation, the schema returned by tools/list remains authoritative.
The five silent failure modes
These five failures run silently into the void in naive agent setups. Catching them is the actual product.
- Invalid Gutenberg block markup silently falls back to the Classic Editor (core/freeform). Fix: server-side Markdown-to-blocks conversion; the agent never writes block markup itself.
- SEO meta gets silently discarded by the active SEO plugin. Fix: provider-neutral reads remain available, while the current SEO writers re-read and conflict-check first, then fail closed with
manual_onlyand HTTP 409 before any provider mutation instead of storing an ineffective fallback. - Hook gaps: some follow-up work (such as the sitemap cache) only fires on an admin save, not on a REST write. Fix: the refresh-hooks ability, with WP-CLI only as a fallback.
- Media problems: SSRF, missing alt text, no featured image. Fix: download-then-upload with an allowlist, mandatory alt text, and a separate verified step that reads back the alt text and featured-image assignment.
- Duplicates on repeated runs. Fix: idempotency via a slug lookup before creating.
Safety layer
The safety layer sits in the CLI and takes effect before and during every pipeline run.
- Bot-not-admin: before every pipeline, a preflight checks the authenticated user (GET /wp/v2/users/me in the edit context). If they carry the role administrator or super_admin, the engine hard-aborts, because any credential with Administrator privileges would be catastrophic in the event of a leak. If the Editor role is missing, a clear message follows.
- Draft by default: everything stays a draft.
--publishand--i-approveexpress the requested approval only. The currentset-post-statuswriter rejects the remote status transition with HTTP 409 andmanual_only. Publishing and further status changes happen manually in WordPress after review. - SSRF allowlist: images are fetched only from public http(s) URLs. Blocked are file:// and other schemes, localhost, loopback, link-local (including the cloud metadata IP 169.254.169.254), and private networks (RFC 1918, IPv6 ULA). Additionally, the host is resolved via DNS and every returned IP is checked (fail-closed); this protects against DNS rebinding and redirects to internal targets.
- Rate limit: a token bucket per client throttles outgoing requests. It starts full (burst up to 8), then four tokens per second; every real HTTP request costs one token. The throttling is purely time-based, without randomness and without dependencies.
- Idempotency: before creating, the pipeline searches by slug (across all relevant statuses, in the edit context). With an existing slug, it stops before any mutation and thereby prevents a duplicate because the current Companion declares remote updates
manual_only. Existing content is not overwritten. A stable slug is therefore mandatory.
BYO principle
The credentials (site, bot user, OAuth token, or Application Password) stay exclusively with the customer and come from the local client configuration or OAuth flow. WordPress stores revocable OAuth and Application Password references but no downloadable plaintext password. The CLI stores its OAuth grant at ~/.config/wp-agent/credentials.json by default, or at the path set in WP_AGENT_CREDENTIALS_FILE. On POSIX systems, it accepts only a private directory with mode 0700 and a file with mode 0600. Licence activation and daily revalidation connect directly to Polar. The plugins also read static update manifests and packages from wpagently.com. WordPress content and credentials are not transmitted in either flow. When the optional chat is used, WordPress sends the message and site data retrieved for the response directly to the AI provider configured by the Customer. WPAgently does not operate a proxy for this feature.
Production note
The mcp-adapter library is a Composer package, not a standalone plugin. In the original test it came from a host plugin (Rank Math bundles it). For the product, wp-agent-companion now bundles the library itself (vendor/, installed via the update guard), so the MCP server works without a third-party plugin.