Skip to content

Enterprise-grade MCP for WordPress — 27 tools, SEO metadata, auditable, controlled, plugin-free.

License

Notifications You must be signed in to change notification settings

GeorgesAdSim/wordpress-mcp-server

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WordPress MCP Server

npm version License: MIT Node.js MCP SDK

Enterprise Governance · Audit Trail · Multi-Site · Plugin-Free

The enterprise governance layer for Claude-to-WordPress integrations — secure, auditable, and multi-site.

27 tools · SEO metadata (Yoast/RankMath/SEOPress/AIOSEO) · Execution controls · JSON audit trail · Multi-site targeting


Architecture

┌─────────────────────────┐
│     Claude Client       │  Claude Desktop · Claude Code · Any MCP client
└────────────┬────────────┘
             │ MCP Protocol (stdio)
┌────────────▼────────────┐
│  WordPress MCP Server   │  Node.js · Standalone · No WordPress plugin
├─────────────────────────┤
│  Execution Controls     │  Read-only · Draft-only · Type/status allowlists
├─────────────────────────┤
│  Audit Logging          │  JSON on stderr · 29 instrumentation points
├─────────────────────────┤
│  Rate Limiting          │  Client-side · Configurable per-minute cap
└────────────┬────────────┘
             │ HTTPS + WordPress Application Password (Basic Auth over TLS)
┌────────────▼────────────┐
│   WordPress REST API    │  Single site or multi-target
└─────────────────────────┘

Why This Server

Most WordPress MCP servers focus on what you can do. This one focuses on what you should be allowed to do — and who can verify it happened.

In regulated environments — financial services, healthcare, legal, government — AI-powered content operations need guardrails. This server provides them out of the box: read-only mode for monitoring, draft-only mode for review workflows, structured audit logs for compliance, and multi-site management for agencies operating across client portfolios.

No composer, no PHP build, no WordPress admin plugin. Point it at any WordPress site with an Application Password, configure your execution policy, and connect your Claude client.

Safety Model

This server is designed for safe operation in production environments:

  • Default non-destructive — delete operations must be explicitly enabled
  • Configurable execution modes — read-only, draft-only, or full access per deployment
  • Pre-flight enforcement — all guardrails checked before any API call is made
  • Full audit trail — every action logged with timestamp, target, outcome, and latency
  • Credential isolation — secrets never appear in logs or error outputs
  • Multi-tenant ready — independent auth and config per WordPress target

Data Retention

The server does not store or persist WordPress content. All processing is stateless — content flows through the server and is never cached, written to disk, or retained in memory beyond the scope of a single tool invocation. Audit logs are emitted to stderr in real-time and can be disabled (WP_AUDIT_LOG=off) or redirected to any logging pipeline based on deployment requirements. Zero data retention by design.


Quick Start

Requirements

  • Node.js >= 18
  • WordPress site with REST API enabled (default since WP 4.7)
  • WordPress Application Password (WP 5.6+)
  • HTTPS endpoint (required for production)

Install from npm (recommended)

# Run directly — no install needed
npx -y @adsim/wordpress-mcp-server

# Or install globally
npm install -g @adsim/wordpress-mcp-server

Install from GitHub

git clone https://github.com/GeorgesAdSim/wordpress-mcp-server.git
cd wordpress-mcp-server
npm install
npm run build

Configure

Create a .env file:

WP_API_URL=https://yoursite.com
WP_API_USERNAME=your-username
WP_API_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx

To generate an Application Password: WordPress Admin → Users → Profile → Application Passwords → Add New.

Connect to Claude Desktop

Add to claude_desktop_config.json:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "wordpress": {
      "command": "npx",
      "args": ["-y", "@adsim/wordpress-mcp-server"],
      "env": {
        "WP_API_URL": "https://yoursite.com",
        "WP_API_USERNAME": "your-username",
        "WP_API_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
      }
    }
  }
}

Connect to Claude Code

claude mcp add wordpress \
  -e WP_API_URL=https://yoursite.com \
  -e WP_API_USERNAME=your-username \
  -e WP_API_PASSWORD="xxxx xxxx xxxx xxxx xxxx xxxx" \
  -- npx -y @adsim/wordpress-mcp-server

Available Tools (27)

Content Management

Tool Description
wp_list_posts List posts with pagination, filtering by status/category/tag/author, and search
wp_get_post Get a post by ID with full content, meta fields, and taxonomy info
wp_create_post Create a post (defaults to draft). Supports HTML, categories, tags, featured image, meta
wp_update_post Update any post field. Only provided fields are modified
wp_delete_post Move to trash by default. Permanent deletion requires force=true
wp_search Full-text search across all content types
wp_list_pages List pages with hierarchy (parent/child), templates, and menu order
wp_get_page Get page content, template, and hierarchy info
wp_create_page Create a page with parent, template, and menu_order support
wp_update_page Update any page field

Media Library

Tool Description
wp_list_media Browse media with type filtering (image/video/audio/document)
wp_get_media Get URL, dimensions, alt text, caption, and all available sizes
wp_upload_media Upload a file from a public URL to the WordPress media library

Taxonomies & Structure

Tool Description
wp_list_categories List categories with hierarchy, post count, and descriptions
wp_list_tags List tags with post count
wp_create_taxonomy_term Create a new category or tag
wp_list_post_types Discover all registered post types (including custom ones)
wp_list_custom_posts List content from any custom post type (products, portfolio, events)

Engagement

Tool Description
wp_list_comments List comments with filtering by post, status, and author
wp_create_comment Create a comment or reply on any post
wp_list_users List users with roles (read-only)

SEO Metadata

Tool Description
wp_get_seo_meta Read SEO title, description, focus keyword, canonical, robots, Open Graph. Auto-detects Yoast, RankMath, SEOPress, All in One SEO
wp_update_seo_meta Update SEO metadata with automatic plugin detection
wp_audit_seo Bulk audit SEO across posts/pages with quality scoring (0-100), missing fields detection, and length checks

SEO metadata updates are subject to the same enterprise controls and execution policies as all other write operations.

Operations

Tool Description
wp_set_target Switch active WordPress site in multi-target mode
wp_site_info Site info, current user, post types, enterprise controls, and available targets

Enterprise Controls

Configure execution policy via environment variables. All restrictions are enforced before any API call is made — including SEO metadata operations.

Control Default Effect
WP_READ_ONLY false Blocks all write operations (create, update, delete, upload, SEO updates)
WP_DRAFT_ONLY false Restricts to draft and pending statuses only
WP_DISABLE_DELETE false Blocks all delete operations
WP_ALLOWED_TYPES all Restricts to specific post types (e.g., post,page)
WP_ALLOWED_STATUSES all Restricts to specific statuses (e.g., draft,pending)
WP_MAX_CALLS_PER_MINUTE unlimited Client-side rate limiting
WP_AUDIT_LOG on Structured JSON audit trail

Deployment profiles

Agency content production — writers can create and edit, but never publish or delete:

WP_DRAFT_ONLY=true
WP_DISABLE_DELETE=true
WP_ALLOWED_STATUSES=draft,pending
WP_MAX_CALLS_PER_MINUTE=30

Compliance monitoring — read-only access for auditing existing content:

WP_READ_ONLY=true
WP_AUDIT_LOG=on

Regulated publishing — restrict to specific content types in a controlled environment:

WP_ALLOWED_TYPES=post
WP_ALLOWED_STATUSES=draft,pending,publish
WP_DISABLE_DELETE=true
WP_AUDIT_LOG=on

Blocked actions return a clear error message explaining which control prevented execution, and are logged in the audit trail with status blocked.


SEO Metadata

The SEO tools auto-detect which SEO plugin is installed on your WordPress site and use the correct meta fields automatically.

Supported plugins:

  • Yoast SEO — _yoast_wpseo_title, _yoast_wpseo_metadesc, _yoast_wpseo_focuskw, plus yoast_head_json REST API extension
  • RankMath — rank_math_title, rank_math_description, rank_math_focus_keyword
  • SEOPress — _seopress_titles_title, _seopress_titles_desc, _seopress_analysis_target_kw
  • All in One SEO — _aioseo_title, _aioseo_description, _aioseo_keywords

SEO Audit Scoring

wp_audit_seo scores each post on a 100-point scale:

Check Penalty
Missing SEO title -30
SEO title too short (< 30 chars) or too long (> 60 chars) -10
Missing meta description -30
Meta description too short (< 120 chars) or too long (> 160 chars) -10
Missing focus keyword -20
Focus keyword not in SEO title -10

Exposing SEO Meta Fields (Required)

Most SEO plugins store their data in WordPress post meta fields that are not exposed via the REST API by default. Without this step, wp_get_seo_meta and wp_audit_seo will return empty results even though your SEO data exists in the database.

Add the following code to your theme's functions.php (Appearance → Theme File Editor → functions.php) or — preferably — create a custom mini-plugin (see below).

⚠️ Important: When pasting code into functions.php, make sure the file starts with exactly <?php — no extra characters before it. A stray character (like <<?php) will break the WordPress REST API by injecting invalid output before JSON responses, causing Unexpected token '<' errors in MCP.

RankMath:

add_action( 'init', function() {
    $fields = array(
        'rank_math_title',
        'rank_math_description',
        'rank_math_focus_keyword',
        'rank_math_canonical_url',
        'rank_math_robots',
        'rank_math_facebook_title',
        'rank_math_facebook_description',
        'rank_math_facebook_image',
    );
    foreach ( $fields as $field ) {
        foreach ( array( 'post', 'page' ) as $post_type ) {
            register_post_meta( $post_type, $field, array(
                'show_in_rest'  => true,
                'single'        => true,
                'type'          => 'string',
                'auth_callback' => function() {
                    return current_user_can( 'edit_posts' );
                },
            ) );
        }
    }
} );

Yoast SEO:

add_action( 'init', function() {
    $fields = array(
        '_yoast_wpseo_title',
        '_yoast_wpseo_metadesc',
        '_yoast_wpseo_focuskw',
        '_yoast_wpseo_canonical',
        '_yoast_wpseo_meta-robots-noindex',
        '_yoast_wpseo_meta-robots-nofollow',
        '_yoast_wpseo_opengraph-title',
        '_yoast_wpseo_opengraph-description',
        '_yoast_wpseo_opengraph-image',
    );
    foreach ( $fields as $field ) {
        foreach ( array( 'post', 'page' ) as $post_type ) {
            register_post_meta( $post_type, $field, array(
                'show_in_rest'  => true,
                'single'        => true,
                'type'          => 'string',
                'auth_callback' => function() {
                    return current_user_can( 'edit_posts' );
                },
            ) );
        }
    }
} );

SEOPress:

add_action( 'init', function() {
    $fields = array(
        '_seopress_titles_title',
        '_seopress_titles_desc',
        '_seopress_analysis_target_kw',
        '_seopress_robots_canonical',
        '_seopress_robots_index',
        '_seopress_social_fb_title',
        '_seopress_social_fb_desc',
        '_seopress_social_fb_img',
    );
    foreach ( $fields as $field ) {
        foreach ( array( 'post', 'page' ) as $post_type ) {
            register_post_meta( $post_type, $field, array(
                'show_in_rest'  => true,
                'single'        => true,
                'type'          => 'string',
                'auth_callback' => function() {
                    return current_user_can( 'edit_posts' );
                },
            ) );
        }
    }
} );

All in One SEO:

add_action( 'init', function() {
    $fields = array(
        '_aioseo_title',
        '_aioseo_description',
        '_aioseo_keywords',
        '_aioseo_og_title',
        '_aioseo_og_description',
        '_aioseo_og_image_url',
    );
    foreach ( $fields as $field ) {
        foreach ( array( 'post', 'page' ) as $post_type ) {
            register_post_meta( $post_type, $field, array(
                'show_in_rest'  => true,
                'single'        => true,
                'type'          => 'string',
                'auth_callback' => function() {
                    return current_user_can( 'edit_posts' );
                },
            ) );
        }
    }
} );

Alternative: MCP SEO Bridge Plugin (Recommended)

Note: Core content operations require no WordPress plugin. SEO metadata tools may require exposing meta fields via the REST API using either a theme snippet or this optional micro-plugin.

Instead of modifying your theme's functions.php (which gets overwritten on theme updates), create a standalone micro-plugin.

Create the file wp-content/plugins/mcp-seo-bridge.php:

<?php
/**
 * Plugin Name: MCP SEO Bridge
 * Description: Exposes SEO plugin meta fields via REST API for WordPress MCP Server
 * Version: 1.0.0
 * Author: AdSim
 * Author URI: https://adsim.be
 */

if ( ! defined( 'ABSPATH' ) ) exit;

add_action( 'init', function() {
    // Auto-detect SEO plugin and register appropriate fields
    $fields = array();

    if ( defined( 'RANK_MATH_VERSION' ) ) {
        $fields = array(
            'rank_math_title', 'rank_math_description', 'rank_math_focus_keyword',
            'rank_math_canonical_url', 'rank_math_robots',
            'rank_math_facebook_title', 'rank_math_facebook_description', 'rank_math_facebook_image',
        );
    } elseif ( defined( 'WPSEO_VERSION' ) ) {
        $fields = array(
            '_yoast_wpseo_title', '_yoast_wpseo_metadesc', '_yoast_wpseo_focuskw',
            '_yoast_wpseo_canonical', '_yoast_wpseo_meta-robots-noindex', '_yoast_wpseo_meta-robots-nofollow',
            '_yoast_wpseo_opengraph-title', '_yoast_wpseo_opengraph-description', '_yoast_wpseo_opengraph-image',
        );
    } elseif ( defined( 'SEOPRESS_VERSION' ) ) {
        $fields = array(
            '_seopress_titles_title', '_seopress_titles_desc', '_seopress_analysis_target_kw',
            '_seopress_robots_canonical', '_seopress_robots_index',
            '_seopress_social_fb_title', '_seopress_social_fb_desc', '_seopress_social_fb_img',
        );
    } elseif ( defined( 'AIOSEO_VERSION' ) ) {
        $fields = array(
            '_aioseo_title', '_aioseo_description', '_aioseo_keywords',
            '_aioseo_og_title', '_aioseo_og_description', '_aioseo_og_image_url',
        );
    }

    foreach ( $fields as $field ) {
        foreach ( array( 'post', 'page' ) as $post_type ) {
            register_post_meta( $post_type, $field, array(
                'show_in_rest'  => true,
                'single'        => true,
                'type'          => 'string',
                'auth_callback' => function() {
                    return current_user_can( 'edit_posts' );
                },
            ) );
        }
    }
} );

Activate it from WordPress Admin → Plugins. This approach auto-detects your SEO plugin and survives theme updates.

Verifying SEO Fields Are Exposed

After adding the code, verify the fields are accessible:

curl -s -u "username:application-password" \
  "https://yoursite.com/wp-json/wp/v2/posts?per_page=1" | python3 -m json.tool | grep -E "rank_math|yoast|seopress|aioseo"

If you see your SEO fields in the meta object, the configuration is working.

Troubleshooting SEO Fields

Symptom Cause Fix
wp_audit_seo returns empty SEO data Meta fields not exposed via REST API Add register_post_meta() code above
Unexpected token '<' on all MCP calls Stray character before <?php in functions.php Remove any characters before <?php
SEO fields visible but all null SEO plugin not yet configured on those posts Set titles/descriptions in RankMath/Yoast editor
No SEO plugin detected Plugin constant not matched Verify your SEO plugin is active
Fields lost after theme update Code was in functions.php Use the MCP SEO Bridge plugin instead

Structured Audit Log

Every tool invocation is recorded as a JSON event on stderr — ready for ingestion into Datadog, Splunk, CloudWatch, Langfuse, ELK, or any JSON-compatible pipeline.

{
  "timestamp": "2026-02-16T18:42:00.000Z",
  "tool": "wp_create_post",
  "target": 1234,
  "target_type": "post",
  "action": "create",
  "status": "success",
  "latency_ms": 245,
  "site": "production",
  "params": { "title": "New Post", "status": "draft" },
  "error": null
}

29 instrumentation points across all tools. Three status types: success, error, blocked.

Field Description
timestamp ISO 8601
tool Tool name invoked
target Resource ID when applicable
target_type Resource type (post, page, media, comment, category, tag)
action Operation: list, read, create, update, trash, permanent_delete, upload, search, switch_target, read_seo, update_seo, audit_seo
status success, error, or blocked
latency_ms Execution time
site Active target name
params Sanitized parameters (content fields truncated)
error Error detail or null

Multi-Target

Manage multiple WordPress sites from a single server instance. Designed for agencies and multi-brand organizations.

Inline configuration:

WP_TARGETS_JSON='{"production":{"url":"https://mysite.com","username":"admin","password":"xxxx"},"staging":{"url":"https://staging.mysite.com","username":"editor","password":"xxxx"}}'

File-based configuration:

WP_TARGETS_FILE=/path/to/targets.json
{
  "production": {
    "url": "https://mysite.com",
    "username": "admin",
    "password": "xxxx xxxx xxxx xxxx xxxx xxxx"
  },
  "staging": {
    "url": "https://staging.mysite.com",
    "username": "editor",
    "password": "xxxx xxxx xxxx xxxx xxxx xxxx"
  },
  "client-blog": {
    "url": "https://client.com",
    "username": "content-manager",
    "password": "xxxx xxxx xxxx xxxx xxxx xxxx"
  }
}

Switch targets during a session with wp_set_target. All available sites and the active target are visible in wp_site_info.


Health & Reliability

The server performs a health check on startup: REST API connectivity, user authentication, and role verification. During operation: automatic retry with exponential backoff (configurable, default 3 attempts), request timeout (default 30s), rate limit handling (respects 429 + retry-after), and contextual error messages with diagnosis guidance.

Setting Default Description
WP_MCP_VERBOSE false Debug-level logging
WP_MCP_TIMEOUT 30000 Request timeout (ms)
WP_MCP_MAX_RETRIES 3 Max retry attempts

Security

  • HTTPS required in production. HTTP only for localhost
  • Application Passwords only — never use WordPress login credentials
  • Credentials never logged — audit trail sanitizes all sensitive data
  • No credentials in code — .env or environment variables only
  • Instant revocation — Application Passwords can be revoked from WordPress admin
  • Traceable requests — custom User-Agent: WordPress-MCP-Server/2.2.0

Troubleshooting

Issue Solution
401 Unauthorized Verify username and Application Password
403 Forbidden Check WordPress user role and capabilities
404 Not Found Verify WP_API_URL and REST API availability
Unexpected token '<' Stray character before <?php in functions.php — see SEO Troubleshooting
Blocked: READ-ONLY mode Disable WP_READ_ONLY to allow writes
Blocked: DRAFT-ONLY mode Only draft/pending allowed. Check WP_DRAFT_ONLY
Rate limit exceeded Adjust WP_MAX_CALLS_PER_MINUTE
Timeout Increase WP_MCP_TIMEOUT or check server
Site not found Verify site key in WP_TARGETS_JSON or file
No SEO plugin detected Install Yoast, RankMath, SEOPress, or AIOSEO
SEO meta fields empty Add register_post_meta() code or install MCP SEO Bridge plugin — see Exposing SEO Meta Fields
Server not starting Check Node.js 18+ is installed: node --version

Development

# Clone the repository
git clone https://github.com/GeorgesAdSim/wordpress-mcp-server.git
cd wordpress-mcp-server

# Install dependencies
npm install

# Build
npm run build

# Run locally
WP_API_URL="https://your-site.com" \
WP_API_USERNAME="user" \
WP_API_PASSWORD="xxxx xxxx xxxx xxxx" \
node dist/index.js

Testing with MCP Inspector

npx @modelcontextprotocol/inspector node dist/index.js

Changelog

v2.2.0 (2026-02-17) — npm Distribution

  • Published on npm: npx -y @adsim/wordpress-mcp-server
  • Complete TypeScript rewrite with modular architecture (src/tools/, src/services/, src/schemas/)
  • Zod validation on all tool inputs
  • Modern MCP SDK with registerTool API
  • dist/ included for npx usage without build step

v2.1.0 (2026-02-16)

  • Enterprise governance controls (read-only, draft-only, type/status allowlists)
  • Structured JSON audit trail (29 instrumentation points)
  • Multi-target site management
  • 27 MCP tools including pages CRUD, media upload, taxonomy creation, custom post types
  • SEO auto-detection for 4 plugins (Yoast, RankMath, SEOPress, AIOSEO)
  • Health checks, retry with backoff, rate limiting

v1.0.0 (2025-10-17)

  • Initial release — JavaScript, 5 tools (list, get, create, update, search posts)

Roadmap

v2.3 — Governance Workflows

  • Approval workflow: draft → human review → publish
  • Confirmation step for destructive actions
  • Per-target enterprise controls

v2.4 — Extended Integrations

  • OAuth 2.0 / JWT authentication
  • WooCommerce support (products, orders)
  • Internal link analysis tool

Contributing

Contributions welcome. Open an issue or submit a pull request.

License

MIT — see LICENSE.

Credits

Built by AdSim — Digital Marketing & AI Agency, Liège, Belgium.

Building the governance layer for Claude-powered WordPress infrastructure in regulated environments.

About

Enterprise-grade MCP for WordPress — 27 tools, SEO metadata, auditable, controlled, plugin-free.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •