mcp-wordpress
# mcp-wordpress
> Model Context Protocol server for WordPress — content, SEO and marketing automation
[](https://opensource.org/licenses/MIT)
[](https://nodejs.org)
A [Model Context Protocol](https://modelcontextprotocol.io) server that gives Claude (or any MCP-compatible LLM) direct access to a self-hosted WordPress site over its REST API.
**Status:** v1.2.0 release candidate — 73 tools, 373 tests, full release pipeline ready.
## v1.1 — what works today
### System
- `wp_health_check` — verify connectivity, detect REST namespaces, detect MCP Bridge and SEO plugins
### Posts (full CRUD)
- `wp_posts_list` — list with filters (status, search, author, categories, tags, date range, ordering, pagination)
- `wp_posts_get` — fetch a single post by id with full content
- `wp_posts_create` — create a draft (default) or any status (publish/future/private require publish tier)
- `wp_posts_update` — partial update by id
- `wp_posts_publish` — shortcut to set status=publish
- `wp_posts_schedule` — schedule for future publication (date must be in the future)
- `wp_posts_delete` — to trash by default; force+confirm for hard delete (MODE=full)
- `wp_posts_get_revisions` — list revisions/autosaves of a post
### Pages (full CRUD)
- `wp_pages_list` — list with filters (parent for hierarchy, status, search, author, ordering)
- `wp_pages_get` — fetch a single page by id with full content
- `wp_pages_create` — create with hierarchy (parent) and ordering (menu_order)
- `wp_pages_update` — partial update including re-parenting
- `wp_pages_delete` — to trash by default; force+confirm for hard delete
### Media (full CRUD)
- `wp_media_list` — list with filters (media_type, mime_type, search, date range, parent post)
- `wp_media_upload` — upload from URL or base64; optional title/alt_text/caption/description; max 25 MB
- `wp_media_set_alt_text` — update alt_text + caption + description for accessibility and SEO
- `wp_media_delete` — to trash by default; force+confirm for hard delete
### Taxonomies
- `wp_taxonomies_list_categories` — list with search, parent (hierarchy), hide_empty, ordering
- `wp_taxonomies_create_category` — create with optional parent
- `wp_taxonomies_list_tags` — list flat tags with post counts
- `wp_taxonomies_create_tag` — create a tag
- `wp_taxonomies_assign_to_post` — replace categories and/or tags on a post
### Search
- `wp_search_posts` — full-text search via `/wp/v2/search` across posts, pages, CPTs, and terms
### SEO meta
- `wp_seo_detect_provider` — detect active provider (bridge / yoast / rankmath / null) and its capabilities
- `wp_seo_get_meta` — read SEO meta for a post via the active provider
- `wp_seo_set_meta` — write SEO meta (title, description, OG, Twitter, canonical, robots, JSON-LD, focus_keyword) via the active provider
- `wp_seo_analyze_post` — readability (Flesch-Kincaid ES/EN/PT) + keyword density + headings hierarchy lint + length stats; optional focus_keyword for presence check
- `wp_seo_suggest_internal_links` — ranked related-post suggestions from a keyword or post_id; title matches weighted 3x over excerpt + recency boost
### Bulk operations (MODE=full)
- `wp_bulk_find_replace` — find/replace literal substring across post content. Dry-run by default; apply with confirm token.
- `wp_bulk_update_seo` — apply same SEO meta to many posts via active provider. Dry-run by default; apply with confirm token.
Both bulk tools require `MODE=full` (set `MODE=full` env var). The dry-run response includes the exact `confirm_token` you need to pass to actually apply the change.
### System
- `wp_health_check` (covered earlier) — REST namespaces + bridge + SEO plugin detection
- `wp_get_site_info` — title, tagline, URL, language, timezone, sitemap URL, posts-per-page
- `wp_detect_page_builder` — warn when Elementor/Divi/Beaver/WPBakery is active (their content can't be edited via posts tools)
### Comments (5)
- `wp_comments_list` — filter by post/author/status/type
- `wp_comments_get` — single comment with full author details
- `wp_comments_create` — create or reply (parent)
- `wp_comments_update` — moderate (approve/spam/trash) or edit
- `wp_comments_delete` — trash or hard delete
### Users (6)
- `wp_users_list` — search, filter by role, who=authors
- `wp_users_get` — single user
- `wp_users_get_current` — whoami (current Application Password user)
- `wp_users_create` — new user with role
- `wp_users_update` — partial update including role changes
- `wp_users_delete` — permanent delete with content reassignment (MODE=full)
### Discovery (4)
- `wp_get_post_types` — all registered post types (built-in + custom)
- `wp_get_taxonomies` — all taxonomies (built-in + custom), optional type filter
- `wp_get_post_statuses` — available post statuses
- `wp_get_site_capabilities` — single-call summary of types + taxonomies + namespaces + plugin detection
### Custom Post Types — Generic CRUD (5)
- `wp_cpt_list` — list any CPT (WooCommerce products, events, custom)
- `wp_cpt_get`, `wp_cpt_create`, `wp_cpt_update`, `wp_cpt_delete`
### Taxonomies — completeness (6 new in v1.1)
- `wp_taxonomies_get_category` / `_update_category` / `_delete_category`
- `wp_taxonomies_get_tag` / `_update_tag` / `_delete_tag`
### Reusable Blocks (5)
- `wp_blocks_list` / `_get` / `_create` / `_update` / `_delete` — Gutenberg reusable block CRUD
### Revisions detail + restore (4 — extending posts and pages)
- `wp_posts_get_revision` — fetch one revision
- `wp_posts_restore_revision` — rollback a post to an earlier state
- `wp_pages_get_revisions` — list revisions of a page
- `wp_pages_get_revision` / `_restore_revision` — same for pages
### Media completeness (2)
- `wp_media_get` — full media metadata
- `wp_media_update` — generic partial update of any media field
### System (1 new)
- `wp_update_site_settings` — write title, tagline, timezone, language, posts_per_page, etc.
### Batch (1)
- `wp_batch` — execute up to 25 REST requests in one round-trip
## Architecture
- TypeScript / Node ≥22, ESM modules
- `@modelcontextprotocol/sdk` over stdio transport
- Application Password authentication (native since WP 5.6)
- Rate limiting (Bottleneck), retry with exponential backoff, in-memory LRU+TTL cache
- Three operational modes: `read-only` / `safe` (default) / `full`
- Audit log of all write operations
See `docs/superpowers/specs/2026-06-01-mcp-wordpress-design.md` for the full design.
## Installation
```bash
npm install -g @iamsamuelfraga/mcp-wordpress
```
Or via `npx` (no installation):
```bash
npx -y @iamsamuelfraga/mcp-wordpress
```
## Setup
### 1. Create an Application Password in WordPress
Go to **WP Admin → Users → Your Profile → Application Passwords**. Enter a name (e.g., "Claude MCP") and click "Add New Application Password". Copy the 24-character password (formatted as 6 groups of 4 characters).
### 2. Configure Claude Desktop
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
```json
{
"mcpServers": {
"wordpress": {
"command": "npx",
"args": ["-y", "@iamsamuelfraga/mcp-wordpress"],
"env": {
"WP_URL": "https://your-site.com",
"WP_USER": "your_admin_username",
"WP_APP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx",
"MODE": "safe"
}
}
}
}
```
Restart Claude Desktop.
### 3. Verify
In Claude, ask: *"Run a health check on my WordPress site."* — you should see your site title, the detected REST namespaces, and whether SEO plugins are present.
## Companion plugin (optional but recommended)
For full SEO functionality (rendering meta description, Open Graph, Twitter Card, JSON-LD, and per-post robots directives in your site's `<head>`), install the **MCP WordPress Bridge** companion plugin on your WordPress site.
### Install via GitHub Releases
1. Download `mcp-wordpress-bridge-X.Y.Z.zip` from the [Releases page](https://github.com/iamsamuelfraga/mcp-wordpress/releases).
2. In `wp-admin`, go to Plugins → Add New → Upload Plugin → choose the zip → Install → Activate.
3. No further configuration needed. The plugin auto-detects Yoast / Rank Math / AIOSEO / SEOPress and defaults to coexist mode (no double-rendering).
### Mode constants (optional, in `wp-config.php`)
```php
define('MCP_BRIDGE_MODE', 'coexist'); // default: skip if another SEO plugin is active
// define('MCP_BRIDGE_MODE', 'override'); // always render our meta
// define('MCP_BRIDGE_MODE', 'disable'); // don't render anything (data layer only)
```
### What the plugin exposes
Once installed, the MCP server detects the bridge via `wp_health_check` (look for `bridge.installed: true`). New REST endpoints become available under `/wp-json/mcp/v1/*`:
- `GET /info` — plugin version, capabilities, detected SEO plugins
- `GET|POST|DELETE /seo/{post_id}` — read/write/clear our SEO meta for a post
- `GET /plugins` — active plugins list
- `GET /theme` — active theme info
- `GET /permalinks` — read-only permalink structure
- `GET /health` — health check (no auth)
## Configuration
All settings via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
| `WP_URL` | yes | — | Base URL of your WordPress site |
| `WP_USER` | yes | — | WordPress username |
| `WP_APP_PASSWORD` | yes | — | Application Password (24 chars, with or without spaces) |
| `MODE` | no | `safe` | `read-only` \| `safe` \| `full` |
| `TOOLSETS` | no | all | Comma-separated: `posts,pages,media,taxonomies,search,seo,bulk,system` |
| `RATE_LIMIT_RPS` | no | `10` | Requests per second cap |
| `CACHE_TTL_SECONDS` | no | `300` | In-memory cache TTL |
| `LOG_LEVEL` | no | `info` | `debug` \| `info` \| `warn` \| `error` |
| `SEO_PROVIDER` | no | `auto` | `auto` \| `bridge` \| `yoast` \| `rankmath` \| `null` |
| `TIMEOUT_MS` | no | `30000` | Per-request timeout |
| `AUDIT_LOG_PATH` | no | `~/.local/state/mcp-wordpress/audit.log` | Where write ops are logged |
## Development
```bash
git clone https://github.com/iamsamuelfraga/mcp-wordpress.git
cd mcp-wordpress
npm install
npm run build
npm test
```
## Integration tests (Docker)
```bash
# 1. Start the Dockerized WordPress
docker compose -f docker-compose.test.yml up -d
# 2. Seed WP and capture an Application Password (printed on stdout)
APP_PASSWORD=$(./tests/integration/wp-seed.sh)
# 3. Run integration tests
WP_URL=http://localhost:8080 \
WP_USER=admin \
WP_APP_PASSWORD="$APP_PASSWORD" \
npm run test:integration
# 4. Tear down
docker compose -f docker-compose.test.yml down -v
```
## Releases
This project uses [semantic-release](https://github.com/semantic-release/semantic-release) to automate versioning, changelog generation, and publishing. Every push to `main` runs the release workflow:
- Inspects commits using [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, etc.).
- Bumps the version accordingly (patch / minor / major).
- Updates `CHANGELOG.md`.
- Publishes to npm as `@iamsamuelfraga/mcp-wordpress`.
- Creates a GitHub Release with the companion plugin `.zip` attached.
To enable npm publishing, the repository owner must add an `NPM_TOKEN` secret in GitHub repo settings.
## License
MIT © 2026 Samuel Fraga
TDQS
Scored across 73 tools
Most tools have clearly distinct resource+action pairs, and descriptions explicitly disambiguate overlapping areas (e.g., wp_media_update vs wp_media_set_alt_text, wp_cpt_* vs wp_posts_*). A few near-duplicates like wp_posts_publish/schedule vs wp_posts_update exist, but they are shortcuts with clear purposes.
The dominant pattern is wp_<resource>_<action> (e.g., wp_pages_get, wp_taxonomies_create_tag), which is consistent and readable. However, there are notable exceptions: a cluster of wp_get_* tools (wp_get_site_info, wp_get_post_types, wp_get_taxonomies), plus standalone wp_health_check, wp_batch, wp_search_posts, and wp_detect_page_builder, mixing conventions.
At 73 tools, the server is extremely large and likely overwhelming for agents to navigate efficiently. While WordPress is a broad domain, many tools are thin wrappers or convenience shortcuts (e.g., wp_posts_publish, wp_media_set_alt_text) that could be consolidated without losing functionality.
The tool set provides comprehensive CRUD coverage for posts, pages, media, comments, users, taxonomies, CPTs, and blocks, plus SEO, site settings, batch operations, search, and health checks. It covers the full lifecycle of WordPress content management with no obvious dead ends.