
WordPress MCP
io.github.konkomaji由 GitHub 帳號 konkomaji 發布v2.0.0更新於 Oct 26, 2026
Talk to your WordPress site: content, SEO, WooCommerce, page builders, media and speed, with undo.
概覽
一個 WordPress 外掛,透過 MCP 讓 AI 用戶端操作網站的內容、SEO、WooCommerce、頁面建構器、媒體與診斷,並支援預演、復原和稽核日誌。
- 可以做什麼
- 搜尋、讀取、建立、更新、刪除和批次編輯文章、頁面、分類項目、自訂欄位、媒體與修訂版本,以及 SEO 欄位、結構化資料、重新導向、robots 與 llms.txt
- 執行 SEO 稽核、失效連結掃描、網站地圖檢查、IndexNow 提交和圖片最佳化,支援預覽與還原點
- 管理 WooCommerce 商品、變體、優惠券和商店設定,可選管理訂單、退款與客戶紀錄
- 編輯頁面建構器版面、選單、小工具和佈景主題設定,並讀取 Google Site Kit 的搜尋與分析報表
- 檢視網站健康狀態、外掛、佈景主題、錯誤日誌和稽核日誌,可選管理外掛、使用者、檔案和原始 SQL
- 將最多 50 個操作合併為一次可整體復原的批次處理,並透過日誌復原寫入
- 適用情境
- 適合讓助理直接在 WordPress 網站上工作:稽核並修正 SEO、撰寫或批次修改內容、調整 WooCommerce 商品目錄,或排查效能與網站健康問題。既適合個人站長,也適合代理商為客戶或員工發放限定範圍、可過期的金鑰。
- 執行需求
- 需要自架的 WordPress 網站(5.6 以上)與 PHP 7.4 以上,安裝並啟用 WordPress MCP 外掛,並透過 HTTPS 存取。用戶端使用連線金鑰(Authorization Bearer 標頭、X-API-Key 標頭或網址中的金鑰)連線到網站的 MCP 端點,或透過 WordPress 的 OAuth 登入。WooCommerce 與 Google Site Kit 為選用,僅在對應外掛啟用時出現相關工具。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 WordPress MCP,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
WordPress MCP
Turn any WordPress site into a remote MCP server that any AI assistant can read, see, and work on directly, with an undo button.
Engine-agnostic SEO (Yoast / Rank Math) · AEO/GEO (JSON-LD, llms.txt, robots, redirects, IndexNow) · complete WooCommerce store operations · page-builder aware editing · site speed auditing and optimisation · live Search Console / GA4 / PageSpeed, all over one authenticated endpoint.
[Works with any MCP client] [Version] [License]
[WordPress] [PHP] [MCP] [Tools] [Undo] [Agency safe]
Works with every AI client that speaks MCP
🔌 One endpoint, every AI client
WordPress MCP speaks the open Model Context Protocol over standard Streamable HTTP. It isn't tied to any one AI vendor. Install it on a client site and connect whichever assistant you or your team use: Claude, ChatGPT, Codex, Gemini, Cursor, Copilot, or all of them at once. The settings screen generates a ready-to-paste config for each one. The agent can then see the real site and implement changes itself. No copy-paste, no second OAuth, no guessing which SEO plugin the client runs.
Table of contents
- What it does
- Quick start
- Connect your AI client
- Prompts, resources, search and fetch
- Sign in with WordPress (OAuth)
- Teams, clients and approvals
- Health checks and the connection test
- WP-CLI
- Structured output and live progress
- MCP Registry
- Who it's for
- Why it exists
- What makes it different
- Architecture
- Request lifecycle (the wire)
- Safety: dry run, undo, audit
- Error handling
- Capability model
- Tool catalogue (165)
- Media and images
- Page-builder editing
- Commerce operations
- Engine-agnostic SEO layer
- Technical SEO: links, sitemaps, IndexNow
- AEO / GEO layer
- Performance layer
- Editing WordPress from a coding agent
- Google Site Kit bridge
- Security model
- Protocol compliance
- Workflow: how an agency uses it
- Extending
- Release history
- License
What it does
One plugin, one endpoint, 165 typed tools, plus ready-made prompts and browsable resources. Any MCP client connects once per site and can then do the whole job rather than describe it:
Quick start
- Upload the ZIP from Releases via Plugins → Add New → Upload Plugin, or copy the folder to
wp-content/plugins/wordpress-mcp/. - Activate WordPress MCP.
- Open WordPress MCP in the admin menu. Under Connect an AI client, pick your client and copy the snippet. The site's real endpoint and key are already filled in. Claude and ChatGPT need only the URL: you sign in with WordPress when they ask.
- Click Test connection to confirm AI clients can get through. It catches stripped headers, firewalls and caches.
- Toggle the capability groups you need. The defaults are safe. To give someone else access, create a connection key with a preset instead of sharing yours.
Then ask for something real:
"Audit this site's SEO, show me the ten worst pages, and fix the missing meta descriptions."
Requires WordPress 5.6+ and PHP 7.4+. WooCommerce and Google Site Kit are optional. Their tools appear only when those plugins are active.
Connect your AI client
Prompt and resource support depends on the client and changes often. Tools work in every client above.
Every client connects to the same endpoint:
A client can present the key in any of these ways:
The snippets below use my-site and YOUR_KEY. The settings screen generates them with your real values.
[圖片] Claude Code
[圖片] Claude (claude.ai web & Claude Desktop)
Go to Settings → Connectors → Add custom connector, paste the plain endpoint URL and click Connect. Claude sends you to your site to sign in and approve:
To skip sign-in, paste the URL with a key built in instead: …/wp-json/wp-mcp/v1/mcp?key=YOUR_KEY.
[圖片] ChatGPT (developer mode)
- Go to Settings → Apps & Connectors → Advanced settings and turn on Developer mode.
- Click Create, paste the plain endpoint URL, and choose OAuth. ChatGPT sends you to your site to sign in and approve.
Alternatively, choose No authentication and paste the URL with a key built in: …/wp-json/wp-mcp/v1/mcp?key=YOUR_KEY.
ChatGPT asks for confirmation before any tool that writes. Read-only tools carry readOnlyHint, so ChatGPT runs them without asking. The search and fetch tools follow the shape ChatGPT's deep research expects.
[圖片] OpenAI Codex (CLI & IDE extension)
You can also write it straight into ~/.codex/config.toml, which the CLI and the IDE extension share:
[圖片] Gemini CLI
Or put it in ~/.gemini/settings.json. Use httpUrl, because url selects the old SSE transport:
This site's prompts show up in Gemini CLI as slash commands, for example /seo_audit.
[圖片] Cursor
Add this to ~/.cursor/mcp.json for all projects, or .cursor/mcp.json for one:
[圖片] VS Code (GitHub Copilot agent mode)
Add this to .vscode/mcp.json. VS Code prompts for the key once and stores it securely:
[圖片] Windsurf
Windsurf loads at most 100 tools across all servers, so narrow the catalogue with ?groups= (see below):
[圖片] Zed · Cline / Roo Code · Continue · LM Studio
[圖片] Anything else (including stdio-only clients)
Any client that supports remote Streamable HTTP MCP servers works. Point it at the endpoint and send the Bearer header. If a client can only launch local (stdio) servers, bridge it with mcp-remote:
Fewer tools per connection: ?groups=
Every model picks tools more reliably from a shorter list, and some clients cap how many tools they load. To expose only some capability groups on a connection, add ?groups= to the endpoint URL or send an X-WPMCP-Groups header:
Group keys are content, woocommerce, wc_orders, performance, builders, appearance, sitekit, diagnostics, site_mgmt, filesystem and database. Diagnostics always stays in scope so the agent can orient itself.
?groups= only narrows what's exposed. It can never switch on a group that's disabled in settings. One practical setup is two connections to the same site: a content writer with ?groups=content and a developer with everything.
HTTPS. When the key travels in the URL, serve the site over HTTPS so the key is encrypted in transit. Regenerate the key if a URL is ever shared. Header auth keeps the key out of URLs entirely.
Prompts, resources, search and fetch
MCP has more than tools, and clients use the other parts differently. This server implements the ones that help:
Prompts: ready-made workflows. They appear as commands in the client, for example /mcp__my-site__seo_audit in Claude Code, /seo_audit in Gemini CLI, and the / menu in VS Code and Cursor. A marketer can run a whole engagement step without knowing the tool catalogue. Each prompt is offered only when the tools it needs are enabled on the site.
Resources: content you can attach. In Claude Desktop, VS Code, Cursor, Zed and similar clients, you can @-mention or attach site content directly. wordpress://site/overview gives the stack and capabilities. wordpress://content/{id} gives any post, page or product as clean text. The most recently updated items are listed for browsing.
search and fetch: universal retrieval. search returns {results: [{id, title, url, text}]} and fetch returns {id, title, text, url, metadata} for an ID or a URL on the site. That's the shape ChatGPT's deep research and company-knowledge connectors look for. It also gives any agent a cheap way to find something and then read it before reaching for the heavier list_content / get_content.
Sign in with WordPress (OAuth)
Clients that support MCP sign-in, such as Claude and ChatGPT, don't need a key at all. Add the plain endpoint URL, and the connection works like "Log in with Google":
- The client finds this site's sign-in details on its own (MCP authorization, RFC 9728 and RFC 8414).
- It opens a consent page on your site. You log in to WordPress as usual. Only administrators can approve.
- You choose what the connection may do: a preset, read-only, or "ask me to approve every change". Then you click Approve.
- The client receives a one-hour access token and a refresh token that rotates on every use.
Each approved sign-in appears under Connection keys with the client's name, for example "ChatGPT (signed in by priya)". The audit log names it on every call, and Revoke cuts it off immediately.
Teams, clients and approvals
The plugin was built for two situations: one person running their own site, and an agency running many client sites. Both come down to the same question: who can do what, and who checks it?
Connection keys
The owner key (shown on the settings screen) has full access. For anyone or anything else, create a connection key under Connection keys → New key:
The key itself is shown once, then stored only as a hash. Revoking one key never affects the others.
Presets
A preset also works on a single connection without creating a key. Add ?preset=writer to the endpoint URL, or pick it from Connect an AI client → Tool preset. Presets, key groups and ?groups= only ever narrow each other. They're enforced on every call, including the steps inside a batch.
Approvals
Give a client's team a key with Needs approval turned on. Their AI can still read everything and preview changes (dry_run=true). Anything that would change the site is stored as a change request, and the agent is told it's waiting:
You review it under WordPress MCP → Approvals, which shows a side-by-side diff for content and SEO edits. Approve it or reject it with a note. Approving runs the change exactly as requested, and it's journalled, so undo_operation still reverses it. The agent checks the outcome with list_change_requests. From a terminal, use wp mcp approvals list and wp mcp approvals approve <id>.
A typical agency setup
Health checks and the connection test
Most failed connections aren't bugs. The usual causes are a host that strips the Authorization header, a firewall answering with an HTML block page, a cache replaying an old response, or a site without HTTPS. Three tools find these problems and say how to fix each one:
- Test connection, on the settings screen, calls the endpoint from your browser the way an AI client would. It tries the Bearer header,
X-API-Key, the key in the URL,tools/listandGET, then runs the server-side checks. - Tools → Site Health gains two WordPress MCP checks: configuration and endpoint reachability.
wp mcp doctorruns the same checks from a terminal.
They detect HTTPS problems, plain permalinks, an Authorization header stripped by the host (with the exact .htaccess line to fix it), firewall or WAF block pages, CDN or page caches answering MCP requests, slow responses, high-risk groups left on, unused or expired keys, pending approvals, a short PHP time limit, and recent PHP crashes inside tools.
WP-CLI
Everything on the settings screen is also available as wp mcp commands. With WP-CLI aliases, an agency can run them across every client site from one terminal.
Structured output and live progress
Structured output. Tools with a stable result shape declare an outputSchema and return their result as typed structuredContent too. Clients can then render tables and pass the data straight to code instead of parsing JSON out of text. The tools are search, fetch, site_info, seo_status, get_seo, list_content, list_operations, list_change_requests and mcp_status.
Live progress. Long sweeps such as seo_audit, find_broken_links, product_seo_fix and regenerate_thumbnails send progress notifications while they work, as long as the client asks for them. The client asks by sending a progressToken and accepting text/event-stream. Those calls are answered as a short event stream, and every other call stays plain JSON. Clients that don't ask can still poll get_progress from a second connection.
MCP Registry
The plugin is described in server.json for the official MCP Registry. Registry-aware clients and directories can list it, and you set it up by entering your site's domain and, optionally, a key. A GitHub Actions workflow (.github/workflows/publish-mcp-registry.yml) publishes it when run from the Actions tab, and it checks first that the listed version matches the plugin.
Who it's for
Powerful by default for content and SEO. Safe by configuration for everything dangerous.
Why it exists
You manage SEO for many clients on WordPress. The friction: an AI assistant can't see a client's site (what's published, which schema exists, what the product photo actually shows, what Search Console reports), and it can't act on it without you shuttling data between tools.
WordPress MCP closes that loop. It turns each client site into a remote MCP server. You connect it to your AI client once per site with a single command or pasted config; from then on the agent can list content, audit SEO, look at images, write meta to whichever SEO plugin the client runs, generate schema, publish optimised posts and products, control llms.txt / robots / redirects / sitemaps, and pull live Google data, all over one authenticated endpoint.
Design principle: the core SEO and content work is always available and safe to delegate; the dangerous power (filesystem, raw SQL, user and plugin management) exists but ships off, behind explicit capability switches; and anything that writes can be previewed first and reversed afterwards.
What makes it different
Plenty of things can talk to WordPress. What matters is what happens when an agent is actually trusted with a client's live site.
0. It works with the AI you already use
There's no vendor lock-in and no per-assistant plugin. One standards-compliant MCP endpoint serves Claude, ChatGPT, Codex, Gemini, Cursor, Copilot and anything else that speaks the protocol. Different people on the team can use different assistants against the same site at the same time. → Connect your AI client
1. It writes to the SEO plugin the client already has
You never tell the agent whether it's Yoast or Rank Math. One normalised field set maps to the right meta keys, including the social-image pair that most integrations get half-right. → Engine-agnostic SEO layer
2. It edits builder pages without destroying them
Overwriting post_content on an Elementor or Divi page does nothing, or wrecks the layout. These tools read the page as an addressable tree and rewrite one element inside the builder's own data. → Page-builder editing
3. Every write can be taken back
Bulk repricing, a site-wide search and replace, and an SEO sweep are all one-way in most tooling. Here each instrumented write records the value it overwrites, and undo_operation puts it back. → Safety: dry run, undo, audit
4. The agent can see the images
Alt text written from a filename is a guess. get_image_bytes hands the model the actual picture, so the alt text, the caption and the product description describe what is in it. You can also confirm the right photo is on the right product. → Media and images
5. Failures are recoverable, not just reported
Every error carries a stable code, a human message, an actionable hint, and structured details. The agent fixes its own call instead of guessing. → Error handling
6. Long jobs finish
A three-thousand-product audit doesn't die at PHP's execution limit. Sweeps stop early, return a resume offset, and report progress to a second connection while they run. → Safety: dry run, undo, audit
7. Two hundred edits, one request
batch runs up to 50 tool calls in a single round trip, each with its own result or error, reversible as one operation.
8. Danger is opt-in, per site, per engagement
Filesystem, raw SQL, site management and customer data each sit behind their own switch, enforced server-side. A content engagement cannot read user emails. → Capability model
Architecture
A single bootstrap file wires focused, single-purpose classes. Nothing is global except the constants and one accessor.
A tool is two things in one place: a definition appended to a defs_*() method, and a tool_<name>( $args ) handler beside it. registry() maps one to the other by convention, so nothing central changes when you add one.
Responsibility map
Request lifecycle (the wire)
Every interaction is one POST to a single endpoint speaking JSON-RPC 2.0 over MCP's Streamable HTTP transport. Responses are plain application/json, which the transport allows in every protocol revision and every client supports.
MCP methods implemented: initialize, server/discover, ping, logging/setLevel, tools/list, tools/call, prompts/list, prompts/get, resources/list, resources/templates/list, resources/read. Notifications are accepted with 202. See Protocol compliance.
Two gates on every tools/call:
- Transport auth: valid key (constant-time compare) + brute-force throttle, else
401/429. - Capability gate: the tool's group must be enabled in settings, else a tool error. Tools whose group is off are also hidden from
tools/list, so the agent never sees them.
Orientation on connect. initialize returns an instructions string describing this site: its name, the SEO engine detected, whether WooCommerce is active, which groups are on, that writes are journalled and reversible, and that batch exists. The agent starts oriented instead of probing.
Safety: dry run, undo, audit
Three independent layers, because the interesting failures are different at each one: "don't do that", "put that back", and "what did it do last Tuesday?"
Preview: dry_run
Anything that writes broadly defaults to dry_run=true and returns exactly what it would change: bulk_update_products, bulk_update_content, search_replace_content, bulk_set_seo, bulk_set_image_alt, product_seo_fix, bulk_assign_variation_images, optimize_site, optimize_image, database_cleanup, indexnow_submit, and category-wide update_inventory. Repeat with dry_run=false to apply.
Reverse: the undo journal
Every write goes through a journal that snapshots the value it is about to overwrite. Nothing is stored if a tool changes nothing, so read calls cost nothing.
Not everything is reversible, and the plugin doesn't pretend otherwise: database_cleanup deletes rows for good, and file writes are covered by their own backup system (list_backups / restore_file) rather than the journal.
Account: the audit log
get_audit_log returns every call the server has handled: tool, arguments with secrets redacted, success or failure, duration, requesting IP, a result headline, and the operation_id that would undo it. Filter by tool, writes_only, errors_only, or since. The error log says what broke; this says what was done.
Finish: progress and the time budget
A catalogue-wide sweep used to be a coin flip against max_execution_time. Now:
get_progress: call it on a second connection while a run is working to see the tool, items done out of total, elapsed seconds and the current item.- A budget: at 70% of the server's execution limit, long sweeps stop, return
stopped_earlywith anext_offset, and say how to continue. Applies toseo_audit,product_seo_audit,product_seo_fix,find_broken_links,sitemap_audit,optimize_image,regenerate_thumbnails,find_unused_media,create_restore_pointandbatch.
Fewer round trips: batch
Up to 50 steps. Each reports its own result or its own typed error, so one failure does not lose the rest. The whole batch reverses as one operation. A batch cannot contain a batch.
Error handling
An agent can only recover from a failure it can understand. Every error crossing the wire is therefore structured, not prose:
code is stable and machine-readable: invalid_argument, missing_argument, not_found, capability_disabled, dependency_missing, permission_denied, conflict, io_failed, upstream_failed, unknown_tool, tool_failed, fatal_error.
Five layers, outermost first
Beyond reporting. PHP warnings raised during a call are captured and returned alongside a successful result under _warnings, so a deprecation inside a theme hook is visible rather than silent. Warning capture is re-entrant, so a tool running inside batch doesn't clear the outer call's state. Every failure lands in a rolling 30-entry log readable with get_error_log and shown on the settings screen. mcp_status reports the plugin's own view of itself: enabled groups, exposed versus defined tool counts, dependency state, memory, and recent failures.
A failed write is still undoable. When a handler throws halfway through a bulk run, the journal is kept rather than discarded, because a half-finished change is exactly the one you want to reverse.
Preventing the unrecoverable one. Writing broken PHP to a live site takes down the site and this endpoint. There is no second call to fix it with. So PHP is parsed before every write and refused if it would not compile, and every overwrite or delete keeps a timestamped backup that restore_file can roll back.
Capability model
Every tool is tagged with exactly one capability group. A group must be enabled before its tools are exposed or runnable. The gate is enforced server-side in dispatch(), not just hidden in the UI. Defaults are agency-safe: the destructive groups ship off.
* WooCommerce and Site Kit tools auto-hide when the dependency plugin isn't active, regardless of the toggle.
The Content & SEO group is locked on. It's the reason the plugin exists, and it carries the undo tools that everything else relies on. User-meta access (emails, capabilities) is deliberately not in this group; it requires Site Management, so a "content-only" connection can't read user emails or escalate roles.
Tool catalogue (165)
Content & SEO (56): search, fetch, batch, list_change_requests, list_operations, undo_operation, create_restore_point, find_broken_links, get_sitemap, sitemap_audit, indexnow_submit, get_image_bytes, list_content, get_content, publish_content, update_content, delete_content, duplicate_content, bulk_update_content, search_replace_content, get_seo, set_seo, bulk_set_seo, serp_preview, set_schema, get_schema, generate_schema, analyze_content, internal_link_opportunities, manage_llms_txt, manage_robots_txt, manage_redirects, seo_audit, list_post_types, list_taxonomies, list_terms, save_term, delete_term, get_meta, set_meta, delete_meta, upload_media, list_media, delete_media, set_image_alt, bulk_set_image_alt, set_featured_image, optimize_image, restore_image, regenerate_thumbnails, find_duplicate_media, find_unused_media, list_revisions, restore_revision, list_comments, moderate_comment
With PSEOEWP active (4 more): ai_readiness_audit, set_short_answer, ai_visibility_report, pseoewp_settings. See PSEOEWP.
WooCommerce catalogue (29): list_products, get_product, create_product, update_product, delete_product, duplicate_product, bulk_update_products, list_product_variations, save_product_variation, delete_product_variation, generate_product_variations, bulk_assign_variation_images, list_product_attributes, save_product_attribute, list_product_categories, save_product_category, delete_product_category, manage_product_images, update_inventory, inventory_report, product_seo_audit, product_seo_fix, generate_product_schema, list_coupons, save_coupon, delete_coupon, store_report, get_store_settings, update_store_settings
WooCommerce orders & customers (8): list_orders, get_order, update_order, add_order_note, refund_order, list_customers, get_customer, customer_insights
Performance (8): performance_audit, optimize_site, performance_settings, database_cleanup, clear_cache, image_optimization_report, analyze_page_speed, list_autoloaded_options
Page builders (8): detect_page_builder, get_page_structure, edit_page_element, insert_page_section, delete_page_element, list_builder_templates, manage_global_styles, render_page_preview
Appearance (9): list_menus, list_menu_items, save_menu, delete_menu, manage_menu_items, list_widgets, save_widget, theme_customizer, manage_site_identity
Google Site Kit (6): sitekit_status, sitekit_search_analytics, sitekit_analytics_report, sitekit_pagespeed, sitekit_keyword_opportunities, sitekit_get
Diagnostics (9): get_progress, get_audit_log, site_info, site_health, seo_status, list_plugins, list_themes, mcp_status, get_error_log
Site Management (16): install_plugin, activate_plugin, deactivate_plugin, update_plugin, delete_plugin, install_theme, switch_theme, delete_theme, get_option, update_option, delete_option, list_users, save_user, delete_user, manage_cron, manage_permalinks
Filesystem (13): list_files, read_file, write_file, edit_file, search_files, copy_file, move_file, make_dir, delete_file, file_info, list_backups, restore_file, create_child_theme
Raw Database (3): sql_query, sql_execute, describe_tables
Each tool ships a JSON Schema inputSchema, a display title, and behaviour annotations: readOnlyHint, destructiveHint, idempotentHint and openWorldHint. Clients use the annotations to decide what needs the user's confirmation. The server validates and coerces arguments before the handler runs, so a wrong type fails with id must be an integer, got string rather than a PHP error five frames deep.
Schemas stay inside the subset every major model provider accepts. Every property has one type, every array declares its items, and none use anyOf, $ref or type unions. The same catalogue therefore loads unchanged in Claude, ChatGPT/Codex (OpenAI function calling) and Gemini. Free-form values such as set_meta / update_option travel as strings with value_format=json when a structure is meant.
Media and images
Every route into the media library (upload_media, product images, variation images, the bulk tools) goes through one ingest engine, so they all behave the same way.
Seeing the picture. get_image_bytes returns an attachment as a real inline image, by ID, by post (featured image) or by product (main plus gallery). It is downscaled and re-encoded for transfer, and the original is untouched. This is what makes alt text, captions and product descriptions describe the photograph rather than the filename, and what lets you ask "is the right image on the right product?" and get an answer.
Library maintenance. optimize_image downscales, converts and re-encodes what is already stored. It keeps a restorable original, never replaces a file with a larger one, and can optionally rewrite the old URLs in post content and Elementor data when a conversion changes them; restore_image undoes it. The library tools also include regenerate_thumbnails (batched, resumable), find_duplicate_media (byte-identical groups, naming the copy actually in use), find_unused_media (media nothing references, checked across featured images, product galleries, post content and builder layouts) and bulk_set_image_alt (template across the library).
Page-builder editing
A WordPress page is only "HTML in post_content" on a classic site. Elementor keeps a JSON tree in post meta; Gutenberg keeps block comments in the content; Divi and WPBakery keep nested shortcodes; Beaver Builder keeps serialised objects. Overwriting post_content on any of those either does nothing or destroys the layout, because the builder re-renders from its own data.
So these tools detect what actually built a page and edit it in that builder's own structure.
Also here: insert_page_section (a heading, paragraph, image, button, spacer or raw builder data, placed at the start, the end, or beside an existing element, and generated in the right shape for that page's builder), delete_page_element, list_builder_templates (Elementor library, reusable blocks, block patterns, Divi and Beaver layouts), and manage_global_styles for Elementor kit colours/fonts or the block theme's theme.json palette, which restyles the whole site at once rather than page by page.
Read-only builders fail loudly rather than silently corrupting a layout: the error names the builder, says what is supported, and points at the tools that do work.
Commerce operations
The WooCommerce surface is built for running a store, not just describing one.
Product structured data that Merchant Center accepts. A bare Product + Offer pair validates and earns nothing. generate_product_schema builds the whole object from what WooCommerce already knows plus the few policy facts only the merchant can supply: gtin / mpn / sku, brand (probed across five brand taxonomies), priceValidUntil, shippingDetails, hasMerchantReturnPolicy, itemCondition, colour / size / material, weight, ratings and embedded reviews. A variable product becomes a ProductGroup with every variation as a hasVariant offer. Shipping and return policy can be saved once with save_defaults and reused across the catalogue, and the tool reports what is still missing rather than emitting a silently incomplete object.
Guards that matter in a live store: bulk_update_products and category-wide update_inventory preview before they write and are reversible afterwards; refund_order refuses to exceed the refundable balance and records a manual refund unless explicitly told to call the gateway; update_store_settings only accepts a whitelist of keys, so no path leads to payment credentials; setting a stock quantity keeps stock status coherent automatically.
Engine-agnostic SEO layer
You never tell the agent which SEO plugin a client uses. WPMCP_SEO detects the active engine and maps one normalised field set to the right meta keys:
- Yoast detected via
WPSEO_VERSION/WPSEO_Options. - Rank Math detected via
RankMath/RANK_MATH_VERSION. - Neither → values stored as standard post meta; they render once a supported engine is active.
All writes pass through sanitize_text_field. The same normalisation covers terms (category/tag SEO) and WooCommerce products, and every SEO write is journalled, so a bad sweep reverses in one call.
The image fields accept either an attachment ID or a URL and always write both the URL and the attachment ID. A social image set with only one of the pair is the usual reason a preview silently fails to render.
At scale. bulk_set_seo applies templates ({title}, {category}, {brand}, {sku}, {price}, {site}, {separator}) across a whole post type; product_seo_fix repairs what product_seo_audit reports, writing from the product's own facts. Both check each result against rendered pixel width, not character count. serp_preview shows why that matters: Illinois and MMMMMMMM are both eight characters, and one is three times wider.
Technical SEO: links, sitemaps, IndexNow
AEO / GEO layer
Pairs with PSEOEWP. PSEOEWP is the open source SEO, AEO and GEO plugin from the same developer. When it is active, WordPress MCP writes SEO fields, redirects, llms.txt and IndexNow through PSEOEWP's API, and adds tools to audit pages for answer engines, set quotable short answers and report on visits from AI assistants and crawlers. Ask: "Audit my ten newest posts for AI readiness and fix what you can."
Answer-Engine and Generative-Engine optimisation, managed by the agent and rendered by WPMCP_Frontend:
- JSON-LD schema: stored per post in
_wpmcp_jsonld, validated on write, emitted inwp_headon singular views. Output is hex-escaped (JSON_HEX_TAG|HEX_AMP|HEX_QUOT|HEX_APOS) so a string value can never break out of the<script>element.generate_schemabuilds Article/FAQPage/HowTo/BreadcrumbList/Product JSON-LD straight from post data; for a real WooCommerce product it hands off toWPMCP_Schemafor the fullProduct/ProductGroupobject. llms.txt: a single site-wide document served at/llms.txt(text/plain) via a rewrite rule, managed withmanage_llms_txt. Tells generative engines what the site is and how to use it.robots.txtcontrol:manage_robots_txtappends managed directives to the virtual robots.txt, including explicit allow/deny for AI crawlers (GPTBot, ClaudeBot, Google-Extended, PerplexityBot, CCBot, …). This is the GEO crawl-control surface.- Redirects:
manage_redirectsstores 301/302/307/308 rules served early ontemplate_redirectviawp_safe_redirect, so reorganised content keeps its link equity. - Instant indexing:
indexnow_submitfor the engines that support it; Google still discovers changes through the sitemap, whichsitemap_auditkeeps honest.
Performance layer
"Make my website faster" resolves to a three-call loop.
Every front-end tweak is a stored flag applied on the public side only, listed with its risk level by performance_settings, and reversible at any time. Nothing here edits theme files or installs another plugin. Supporting tools: database_cleanup, clear_cache (object cache, rewrite rules, OPcache, and eleven caching plugins), image_optimization_report, list_autoloaded_options.
Image weight is usually the biggest number in the report, and optimize_image is the tool that actually moves it (see Media and images).
Editing WordPress from a coding agent
With the plugin connected, a coding agent such as Claude Code, Codex, Gemini CLI, Cursor or Copilot works on the site the way it works on a repository.
The filesystem and site-management groups ship off. Turn them on for the work, then turn them back off.
Google Site Kit bridge
If the client already runs and has connected Google Site Kit, this plugin reuses its OAuth, so there is no second authorisation and no stored Google credentials of our own. WPMCP_SiteKit temporarily switches to the connected administrator and issues an internal GET to Site Kit's own REST routes, then restores the previous user in a finally block:
All Site Kit access is read-only (GET data endpoints only).
Security model
Threat model. One API key authenticates the endpoint. Within the enabled capability groups the key is trusted to act, so the key is the crown jewel. The design keeps the default attack surface small and pushes everything dangerous behind explicit switches.
Controls in place
- Constant-time auth.
hash_equals()over every presented credential (Bearer header,X-API-Key/X-WP-MCP-Key,?key=), with no early exit. The key is 48 characters and URL-safe. Regenerating it from settings kills the old key immediately. - Works behind other auth plugins. JWT and similar plugins claim every
Bearerheader site-wide and reject the request before it reaches this endpoint. For this one route only, a request that carries the valid MCP key clears their error. Every other REST route keeps their protection. TheAuthorizationheader is also recovered fromREDIRECT_HTTP_AUTHORIZATIONon Apache CGI/FastCGI hosts that strip it. - Clean auth failures. A missing or wrong key returns
401with a JSON-RPC body and a plainWWW-Authenticate: Bearerchallenge that carries no OAuth metadata. Clients then report "needs a key" instead of starting an OAuth discovery that cannot succeed. - Origin validation (opt-in strict). The endpoint never honours cookies, so a browser page on another origin gains nothing without the key, and any origin is accepted by default. Return a list from the
wpmcp_allowed_originsfilter to reject everything else with403. - Brute-force lockout. Per-IP failure counter (transient): after 10 failed attempts the endpoint returns
429for 15 minutes. A successful auth clears the counter. Hardens a key that lives on many public client sites. - No cookie/CSRF surface. The endpoint authenticates only by its API key; it does not honour WordPress login cookies, so cross-site requests can't ride an admin session.
- Capability gate server-side. Enforced in
dispatch(), not just hidden intools/list. A disabled group's tools are unreachable. - Default-safe groups. Filesystem, Raw Database, and Site Management are off on a fresh install.
- Hardened media ingest. Every upload path (URL, base64, server path) validates the filename against
get_allowed_mime_types(), rejects script/executable extensions (php,phtml,phar,svg,html, …), and re-verifies the written bytes withwp_check_filetype_and_ext, closing the "uploadshell.phpto/uploads" RCE path. Reading an image from a serverpathadditionally requires the Filesystem capability and is confined to the WordPress root byrealpath. - Outbound requests are validated. URLs reaching
wp_remote_*(sideloads, link checks, sitemap fetches, page measurement) passwp_http_validate_url(), so the server cannot be used to probelocalhostor private ranges. - Path containment.
safe_path()rejects..segments and NUL bytes, resolves withrealpath, and confirms the result is the WP root or a true descendant using a trailing-separator compare (so a sibling like/var/www/htmlXcan't masquerade as inside/var/www/html). New nested paths resolve against their nearest existing ancestor. - "Read-only" SQL is read-only.
sql_queryrequires a leadingSELECT/SHOW/DESCRIBE/EXPLAINand blocksINTO OUTFILE,INTO DUMPFILE, andLOAD_FILE(). - Guarded write SQL.
sql_executerefusesDROP,TRUNCATE, andDELETE/UPDATEwith noWHEREclause unlessconfirm=trueis passed, and blocks SQL-level file I/O in both SQL tools, so the database group cannot be used to bypass the filesystem gate. - Stored-XSS hardening. JSON-LD output is hex-escaped;
llms.txt,robots.txtrules and the IndexNow key file are served as plain text. - User-data scope. Reading/writing user meta (emails,
wp_capabilities) requires the Site Management capability. - Personal data is its own switch. Orders, refunds and customer records sit in
wc_orders, off by default, so a catalogue or SEO engagement never carries access to names, emails and addresses. - Secrets never reach the logs. The audit log and the undo journal redact
content,base64,password,api_key,key,secretandtokenarguments before storing anything. - PHP is parsed before it is written. A file write that would not compile is refused, because a fatal in a theme file takes down the site and this endpoint, leaving no way back in to fix it.
- Automatic, restorable backups. Every overwrite and delete copies the previous version into a protected directory under uploads (
.htaccessdeny +index.php), restorable withrestore_file, capped at 100 entries. - Store settings are whitelisted.
update_store_settingsaccepts a fixed list of WooCommerce options; no path through it reaches payment gateway credentials. - Refunds are manual by default.
refund_orderrecords a refund without calling the payment gateway unlessvia_gateway=true, and never exceeds the amount still refundable. - Self-preservation. The plugin refuses to deactivate or delete itself, or to delete the option holding its own API key, since each would silently sever the connection mid-session.
- HTTPS nudge. The settings screen warns when the endpoint isn't HTTPS, since the key travels on every request.
- Errors do not leak paths. File paths in error payloads are reported relative to the WordPress root, never as absolute server paths.
Connections and keys
- Every limit is enforced on every call. A key's preset, groups and read-only flag are checked in the dispatcher for each call, including each step of a
batchand each resource read. They are not only hidden fromtools/list. Tools that reach into another group's data check that group too. User meta needs Site Management on the connection, and reading an image from a server path needs Filesystem on the connection. - Approvals cannot widen a key. A queued
batchhas every step checked against the requesting key when it is queued. Approving runs the change as the requesting key, with its scope, never with the reviewer's rights. Requests from a key revoked since queuing cannot be approved. Only tools that really preview skip the queue withdry_run=true. - Tools run as a real user, with HTML filtering for limited keys. The owner key acts as an administrator. Any other key acts as the administrator who created or approved it, so posts get an author, but WordPress's HTML filtering (kses) stays on. A limited key cannot store script in content that visitors or administrators will load. The
wpmcp_owner_user_idfilter chooses the user. - Undo history is per key. A connection lists and undoes only its own operations. The owner key sees all of them.
- Keys are stored as hashes. Connection keys and OAuth tokens are SHA-256 hashed and shown once. Refresh tokens rotate and are bound to their client.
- Lockout that cannot be turned against you. A valid key always gets in, even from an address that is locked out. Expired tokens and revoked keys are not counted as guesses, so a busy shared proxy cannot lock everyone out.
Data the agent cannot touch
- Protected meta keys. The generic meta tools refuse keys WordPress treats as trusted internal state, such as attachment file paths, attachment metadata, page templates, user capabilities and session tokens, and this plugin's own
_wpmcp_*keys. Each has a dedicated, validated tool. - File operations stay in uploads. Image optimisation, restore and thumbnail clean-up only change attachments whose files live inside the uploads folder.
- Secrets stay secret.
read_file,search_filesandcopy_filerefusewp-config.php,.envfiles, private keys and the plugin's backups. The option tools refuse this plugin's options and WordPress's salts. The site owner can allow a specific file with thewpmcp_allow_secret_filefilter. - Private post types are off limits. Content tools refuse WooCommerce orders, privacy requests and other internal types. Orders have their own group, which is off by default.
- No request forgery through fetch tools. Tools that fetch a URL use WordPress's safe HTTP functions, which refuse private and loopback addresses, including after redirects. Media downloads and base64 uploads are capped at 25 MB.
- Hidden backups. File backups live in a folder with a random name, with random file names and deny rules for Apache and IIS, so they cannot be guessed on servers that ignore
.htaccess. - Backslashes survive. Everything written from agent input is slashed the way WordPress expects, so block markup (
\u003c), Windows paths and JSON in meta come back byte for byte.
Sign-in (OAuth)
- Nothing happens for a visitor who is not logged in. The consent page logs in first, so it cannot be used as an open redirect or to make the site fetch arbitrary URLs.
- The consent page labels every application as unverified, shows the full return address, and preselects read-only access.
- A key is created only when the sign-in completes, and signing in again reuses it. Registration is size capped and rate limited per address.
Operational guidance
- Use a separate key per client; never reuse.
- Enable Filesystem / Raw Database / Site Management only for the duration you need them, then turn them back off.
- Serve client sites over HTTPS before connecting over the public internet.
- Review
get_audit_logafter an unattended run. It is the record of what the agent actually did. - Treat the key like an admin password: anyone holding it has whatever the enabled groups allow.
Workflow: how an agency uses it
Prompts that work end to end
"Make my website faster." →
performance_audit→optimize_site(preview, then apply) →optimize_image→analyze_page_speedto show the before/after.
"Write proper alt text for every product photo." →
get_image_bytes→ look →manage_product_imagesorbulk_set_image_alt.
"Change the hero heading on the services page." →
detect_page_builder→get_page_structure→edit_page_element→render_page_preview.
"Put everything in the Winter category on 20% off until the 31st." →
bulk_update_productswithprice_adjustandsale_to, dry run first, andundo_operationif the client changes their mind.
"Why isn't Google indexing our new pages?" →
sitemap_audit→ fix noindex withset_seo→indexnow_submit.
"Which products are hurting us in search?" →
product_seo_audit→sitekit_search_analytics→product_seo_fix.
Extending
Add a tool in two steps, inside the trait for its group (includes/tools/trait-wpmcp-*.php):
- Append a definition (
group,name,description,inputSchema) to that trait'sdefs_*()method. - Add a
tool_<name>( $args )handler beside it, returning a serialisable array.
registry() maps name → tool_<name> by convention, so nothing central changes. Argument validation, the capability gate, error typing, the undo journal, the audit log and the JSON-RPC envelope all apply automatically.
Failing well. Throw with a code and a hint rather than a bare string. That is what lets the agent recover on its own:
WPMCP_Errors::from_wp_error( $wp_error, $code, $hint ) converts a WP_Error and keeps its data. Anything else a handler throws is caught, typed, logged, and returned as tool_failed, so a bug is still a readable answer, never a 500.
Making a write reversible. Snapshot before you overwrite; the journal is already open around your handler:
That is all: dispatch() closes the journal, returns the operation_id in your result, and undo_operation replays it.
Behaving well in a long run. If your tool walks a large set, report progress and respect the budget:
Conventions worth keeping
- Anything that writes site-wide takes
dry_run, defaulting to true. - Use
WPMCP_Util::word_count()/::len()rather thanstr_word_count()/strlen(), so non-Latin sites are measured correctly. - Return
success, the affectedid, and enough state for the caller to verify without a second call. - Add a
next_stepto results that imply one. It is what turns a tool into a workflow.
To add a whole capability group: add it to WPMCP_Settings::groups(), create includes/tools/trait-wpmcp-<group>.php, then require_once + use it in class-wpmcp-tools.php and add its defs_*() to definitions().
Release history
Full notes for every version: Releases.
Protocol compliance
Built against the MCP specification. It has been exercised end to end with the official TypeScript and Python SDK clients and Claude Code, and its connection verified with Gemini CLI.
License
GPL-2.0-or-later. See LICENSE.
Client logos are the official marks from each company's brand kit. The Gemini icon comes from Simple Icons (CC0), because Google's kit requires a partner login. All logos are trademarks of their owners and appear only to show compatibility. No affiliation or endorsement is implied. Sources and usage terms: assets/clients/.
Author
Built by Konko Maji (LinkedIn).
來源:README.md,提交 caa5234
工具
0版本歷史
1- v2.0.0最新Sep 24, 2026
評分
已滿足適用檢查項的 60%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通必需25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式必需10 分
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分