v2.8.0 · MCP Spec 2025-11-25 · OAuth 2.1 PKCE

WordPress meets
Claude.ai

Give Claude real-time read access to your WordPress site via the Model Context Protocol — so it can write plugins that actually fit your setup.

mcp · wp-json/mcp/v1/bridge
$ curl -X POST https://your-site.com/wp-json/mcp/v1/bridge \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{}}}'
 
# ✓ 200 OK · MCP-Protocol-Version: 2025-11-25
{ "jsonrpc": "2.0", "id": 1, "result": {
  "protocolVersion": "2025-11-25",
  "serverInfo": { "name": "wordpress-mcp-bridge", "version": "2.8.0" },
  "capabilities": { "tools": { "listChanged": false } }
}}
 
$
19
MCP Tools
44+
Bugs Fixed
2.1
OAuth Spec
8.0+
PHP Required

Three steps to connect

Install the plugin, grab your token or complete the OAuth flow, and Claude.ai can immediately read your site's internals.

1

Install & Activate

Upload the plugin to wp-content/plugins/ and activate it. A Bearer token is generated automatically on first activation.

Settings → Permalinks → Save
2

Connect Claude.ai

Add a custom connector in Claude.ai settings. Paste your MCP endpoint URL and choose Bearer Token or complete the OAuth 2.1 flow.

Profile → Connectors → Add custom
3

Ask Claude Anything

Claude can now inspect your plugins, database schema, ACF fields, source code, error logs, and more — in real time.

19 read-only tools available

Everything Claude needs to know

19 read-only tools give Claude a complete picture of your site. Nothing is written, deleted, or modified — ever.

INFO
wp_get_site_info
WP/PHP/MySQL versions, active theme, all plugins, debug settings, upload directories, memory limits.
INFO
wp_get_plugins
All installed plugins with name, version, author, description, and active/inactive status.
INFO
wp_get_themes
All themes with version, author, parent theme info, and active state.
SCHEMA
wp_get_post_types
All post types — labels, supports, taxonomies, REST base, and live post counts.
SCHEMA
wp_get_taxonomies
All taxonomies with object types, REST settings, and term counts.
CONFIG
wp_get_options
WordPress options reader. Credentials and API keys are always redacted.
DATA
wp_query_posts
Query any post type via WP_Query. Returns post data, all meta fields, and taxonomy terms.
DB
wp_get_db_schema
All database tables with column names, types, and live row counts.
DB
wp_db_query
Run SELECT queries. Credential columns and SELECT * on wp_users are permanently blocked.
FILES
wp_list_files
List files in any wp-content subdirectory, filtered by extension. Capped at 2000 results.
FILES
wp_read_file
Read source code of any file inside wp-content. Max 512 KB per read.
DEBUG
wp_get_logs
Read debug.log or the PHP error log. Returns the last N lines (up to 1000).
CODE
wp_get_hooks
Inspect $wp_filter — all registered actions and filters with priorities and callback names.
ACF
wp_get_acf_fields
All ACF field groups and field definitions. Requires ACF plugin to be active.
USERS
wp_get_users
List users with roles. Passwords are never returned under any circumstances.
UI
wp_get_menus
All navigation menus, assigned locations, and menu items with full hierarchy.
CRON
wp_get_cron_jobs
All scheduled WP cron events with next run times, schedules, intervals, and args.
UI
wp_get_active_widgets
Active widget areas and all widgets assigned to each area with their settings.
API
wp_get_rest_routes
All registered REST API routes with HTTP methods and namespaces.

Hardened at every layer

Read-only by design, with defense-in-depth across authentication, file access, database, and the OAuth flow.

🔐

Authentication

  • Tokens validated with hash_equals() — no timing attacks
  • API key stored with autoload = false
  • OAuth token lifetime configurable: 1h / 24h / 7d / 30d (admin setting)
  • Bearer Token auth never expires — only OAuth tokens are time-limited
  • Auth codes are single-use, expire in 10 minutes
  • PKCE S256 mandatory — plain challenges rejected
  • Bearer token and plugin config blocked from wp_get_options tool
  • "Revoke All OAuth Tokens" button for emergency invalidation
📁

File Access

  • All paths resolved with realpath()
  • Separator-aware containment check — no sibling-dir bypass
  • All reads contained within wp-content
  • Log paths validated against allowlist of safe roots
  • realpath() returning false treated as access denied
🗄️

Database

  • Only SELECT queries allowed — enforced by regex
  • user_pass and user_activation_key always blocked
  • SELECT * FROM wp_users permanently blocked
  • Query results capped at 200 rows
  • Rate limited to 120 tool calls/minute per IP
🌐

Network & OAuth

  • CORS scoped to claude.ai origins only
  • DNS rebinding protection via per-request origin check
  • Clickjacking blocked: X-Frame-Options: DENY
  • Content-Security-Policy: frame-ancestors 'none'
  • Apache/FastCGI auth header normalization built in
  • Two-nonce form prevents rest_cookie_invalid_nonce

Up and running in minutes

Two connection methods — pick the one that fits your workflow.

# 1. Find your endpoint and token in WordPress Admin
  Settings → MCP Bridge → copy the Bearer Token
 
# 2. Test the connection manually
curl -s -X POST https://your-site.com/wp-json/mcp/v1/bridge \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
 
# 3. In Claude.ai → Profile → Settings → Connectors → Add custom connector
  MCP URL: https://your-site.com/wp-json/mcp/v1/bridge
  Auth type: Bearer Token
  Token: <paste from Settings → MCP Bridge>
# Claude.ai auto-discovers OAuth via well-known endpoints.
# If you need to enter OAuth details manually:
 
Authorization URL: https://your-site.com/wp-json/mcp/v1/oauth/authorize
Token URL: https://your-site.com/wp-json/mcp/v1/oauth/token
Client ID: claude.ai # any value — NOT "Bearer Token"
Scopes: claudeai
 
# Auto-discovery URLs (Claude.ai fetches these automatically):
  https://your-site.com/.well-known/oauth-authorization-server
  https://your-site.com/.well-known/oauth-protected-resource
 
# After saving, Claude.ai opens a WP login → click "Allow Access"
# Clone into your plugins directory
cd /path/to/wp-content/plugins
git clone https://github.com/guramzhgamadze/WP-MCP-Bridge.git wp-mcp-bridge
 
# Or download the latest release zip and upload via WP Admin
  Plugins → Add New → Upload Plugin → activate
 
# After activation, flush rewrites
  Settings → Permalinks → Save Changes
 
# Add to wp-config.php for debug logging (optional)
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

Built with obsessive care

Every bug is documented with root cause, source reference, and precise fix. No handwaving.

v2.8.0 LATEST
Apr 2026
v2.7.0
Apr 2026
v2.6.0
Mar 2026
v2.5.0
Feb 2026
v2.4.0
Jan 2026
v2.3.0
Dec 2025
v2.1–2.2
Nov 2025

v2.8.0 — 6 bugs fixed + configurable token lifetime

HIGH
BUG-39: MCP protocol version 2025-11-25 (the latest spec) not in the supported list — Claude.ai's version request was always downgraded to 2025-06-18. Now the highest-priority supported version, with all 2025-11-25 additions implemented.
MEDIUM
BUG-40: Authorization Server Metadata missing client_id_metadata_document_supported field. MCP spec 2025-11-25 requires this so clients know to use DCR rather than Client ID Metadata Documents. Now explicitly set to false.
MEDIUM
BUG-41: WWW-Authenticate 401 header missing scope parameter. MCP spec 2025-11-25 §Protected Resource Metadata (SEP-835) requires it for incremental scope consent. Now sends scope="claudeai".
MEDIUM
BUG-42: Consent page expiry showed "1440 minutes" for a 24-hour token. New wp_mcp_bridge_format_ttl() helper formats correctly as "24 hours", "7 days", etc. across all configurable durations.
LOW
BUG-43: serverInfo in initialize response missing optional description field added by MCP spec 2025-11-25 to align with the MCP registry server.json format.
LOW
BUG-44: Token TTL option not written on fresh activation — admin radio buttons had no pre-selected value. Option also now blocked from wp_get_options tool to avoid exposing internal config.
FEATURE
Configurable OAuth Token Lifetime. Choose 1 hour, 24 hours, 7 days, or 30 days from Settings → MCP Bridge. Bearer Token auth is unaffected (never expires). Emergency "Revoke All OAuth Tokens" button added for instant invalidation.

v2.7.0 — 3 bugs fixed

HIGH
BUG-36: SQL injection in wp_mcp_tool_db_schema()$wpdb->esc_like() was used without $wpdb->prepare(). WordPress docs are explicit: "You must use prepare() in conjunction with esc_like()." A crafted table filter argument could escape the SQL string context.
MEDIUM
BUG-37: OAuth consent nonce sanitized with sanitize_text_field() instead of the canonical sanitize_key(). The wrong sanitizer strips HTML entities which can corrupt nonce values on certain hosting configs with magic quotes enabled.
LOW
BUG-38: Cut-paste artifact in v2.6.0 audit header left BUG-31 through BUG-34 described twice with a bare uncommented blank line between them, breaking PHP block-comment structure and flagging linters.

v2.6.0 — 5 bugs fixed

CRITICAL
BUG-35: rest_cookie_invalid_nonce 403 when clicking "Allow Access". WordPress REST cookie auth validated _wpnonce as a wp_rest nonce before the handler ran — our consent nonce failed this check. Fixed with a two-nonce form: _wpnonce satisfies WP REST auth, _mcp_nonce is our CSRF check.
CRITICAL
BUG-31: wp_get_options could expose the plugin's own Bearer token. An authenticated Claude session could call the tool with keys:['wp_mcp_bridge_api_key'] and receive the credential it used to authenticate. Plugin's own options are now permanently blocked.
HIGH
BUG-32: _get_cron_array() returns false on corrupt cron data. foreach(false) emits E_WARNING on PHP 8.0/8.1 and TypeError on PHP 8.2+, crashing the cron tool. Fixed with _get_cron_array() ?: [].
MEDIUM
BUG-33: Unused $resp_types variable in the DCR handler caused PHP notices and created a latent risk of accidentally relying on an unchecked user-supplied value.
LOW
BUG-34: OAuth transients (wpmcp_code_*, wpmcp_token_*, wpmcp_rate_*) were not cleaned up on plugin deactivation, leaving orphaned rows in wp_options.

v2.5.0 — 5 bugs fixed

CRITICAL
BUG-26: Missing registration_endpoint in Authorization Server Metadata. Claude Code fails with "does not support dynamic client registration" without it. Full RFC 7591 DCR endpoint added.
CRITICAL
BUG-27: Plugin declared Requires PHP: 7.4 but used union types and str_starts_with() — PHP 8.0+ only features. Fatal parse error on PHP 7.4. Header corrected to 8.0.
HIGH
BUG-28: realpath() returning false bypassed the entire path containment guard. str_starts_with($full, false . '/') is always true, allowing any file on the filesystem to be read.
MEDIUM
BUG-29: register_setting() only hooked to admin_init. WordPress docs require both admin_init and rest_api_init for the sanitize callback to apply on REST API updates.
MEDIUM
BUG-30: array_filter($params) stripped legitimate empty-string OAuth params. State and scope can be empty strings; removing them breaks CSRF protection and scope handling.

v2.4.0 — 6 bugs fixed

CRITICAL
BUG-20: do_action('rest_api_init') inside a tool callback double-fired every hook in the system, causing duplicate route warnings and third-party side effects.
HIGH
BUG-21: strncmp prefix check bypassed by sibling directories. /wp-content-evil/ matches /wp-content prefix. Fixed with separator-aware str_starts_with($full, $base . DIRECTORY_SEPARATOR).
HIGH
BUG-22: N+1 DB queries in wp_get_usersget_userdata() called per user in a loop. On 200 users = 200 extra queries. Fixed by removing 'fields' restriction to get full WP_User objects.
HIGH
BUG-23: $client_display mixing raw HTML and escaped text in one variable, echoed without escaping — flagged by all WP security scanners as an XSS risk.
MEDIUM
BUG-24: OAuth consent page missing X-Frame-Options. RFC 6749 §10.13 requires clickjacking prevention on authorization endpoints. Headers X-Frame-Options: DENY and frame-ancestors 'none' added.
MEDIUM
BUG-25: Authorization header stripped on Apache + FastCGI — auth always failed even with valid tokens. Normalization from REDIRECT_HTTP_AUTHORIZATION and apache_request_headers() now runs at init priority 1.

v2.3.0 — 8 bugs fixed

CRITICAL
BUG-12: wp_safe_redirect() silently blocked delivery to claude.ai — not on WordPress's allowed redirect hosts list. OAuth flow could never complete.
CRITICAL
BUG-13: get_json_params() returns null on missing/malformed body. Accessing $body['id'] on null = PHP 8 TypeError, crashing the MCP handler on any malformed request.
HIGH
BUG-14/15/16: Inline JS handle not queued · log path traversal via ini_get('error_log') · token fallback always HTTP 200 even on errors (RFC 6749 §5.2 violation).
MEDIUM
BUG-17/18/19: strtok() global state corruption · missing Cache-Control/Pragma on token fallback · target="_blank" link without rel="noopener".

v2.1.0–v2.2.0 — OAuth + 10 core fixes

CRITICAL
BUG-11: Full OAuth 2.1 authorization server implemented. Claude.ai requires /.well-known/oauth-authorization-server (RFC 8414) and the old-spec fallback /authorize and /token paths. Without these, every connection attempt 404'd.
CRITICAL
BUG-01–03: Missing GET handler (generic 404) · OPTIONS preflight blocked by auth callback · missing CORS headers blocked all Claude.ai responses.
HIGH
BUG-04–05: post_count always cast to 1 via stdClass · missing wp_reset_postdata() after WP_Query.
MEDIUM
BUG-06–08: API key stored with autoload = true · date() instead of gmdate() · MCP-Protocol-Version header only on initialize responses.