| 1 |
#!/usr/bin/env php |
| 2 |
<?php |
| 3 |
/** |
| 4 |
* MCP stdio Bridge for WordPress (PHP Implementation) |
| 5 |
* |
| 6 |
* This bridge allows MCP clients (Claude Desktop, Claude Code) to communicate |
| 7 |
* with WordPress MCP tools via stdio (standard input/output). |
| 8 |
* |
| 9 |
* Usage: |
| 10 |
* php mcp-stdio-bridge.php |
| 11 |
* |
| 12 |
* Configuration (Claude Desktop): |
| 13 |
* { |
| 14 |
* "mcpServers": { |
| 15 |
* "wordpress-metasync": { |
| 16 |
* "command": "php", |
| 17 |
* "args": ["/path/to/mcp-stdio-bridge.php"] |
| 18 |
* } |
| 19 |
* } |
| 20 |
* } |
| 21 |
* |
| 22 |
* @package Metasync |
| 23 |
* @subpackage Metasync/wp-mcp-server |
| 24 |
* @since 2.0.0 |
| 25 |
*/ |
| 26 |
|
| 27 |
// Ensure we're running in CLI mode |
| 28 |
if (php_sapi_name() !== 'cli') { |
| 29 |
fwrite(STDERR, "Error: This script must be run from the command line\n"); |
| 30 |
exit(1); |
| 31 |
} |
| 32 |
|
| 33 |
// Suppress unnecessary WordPress output |
| 34 |
// NOTE: Don't define WP_CLI constant - it causes Elementor to crash expecting WP_CLI class |
| 35 |
define('DOING_AJAX', true); |
| 36 |
define('WP_USE_THEMES', false); |
| 37 |
define('DISABLE_WP_CRON', true); |
| 38 |
define('METASYNC_MCP_BRIDGE', true); // Custom flag for our bridge |
| 39 |
|
| 40 |
// Determine WordPress root path |
| 41 |
// Try multiple possible locations for WordPress installation |
| 42 |
|
| 43 |
$possible_paths = [ |
| 44 |
// Docker container path |
| 45 |
'/var/www/html/wp-load.php', |
| 46 |
// Standard installation (5 levels up from script) |
| 47 |
dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/wp-load.php', |
| 48 |
// Alternative: 4 levels up |
| 49 |
dirname(dirname(dirname(dirname(__FILE__)))) . '/wp-load.php', |
| 50 |
// Bedrock/custom structure |
| 51 |
dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))) . '/wp-load.php', |
| 52 |
]; |
| 53 |
|
| 54 |
$wp_load_path = null; |
| 55 |
foreach ($possible_paths as $path) { |
| 56 |
if (file_exists($path)) { |
| 57 |
$wp_load_path = $path; |
| 58 |
break; |
| 59 |
} |
| 60 |
} |
| 61 |
|
| 62 |
if (!$wp_load_path) { |
| 63 |
fwrite(STDERR, "Error: Cannot find WordPress wp-load.php\n"); |
| 64 |
fwrite(STDERR, "Tried the following paths:\n"); |
| 65 |
foreach ($possible_paths as $path) { |
| 66 |
fwrite(STDERR, " - $path\n"); |
| 67 |
} |
| 68 |
fwrite(STDERR, "\nIf using Docker, run via mcp-stdio-bridge-docker.sh wrapper\n"); |
| 69 |
exit(1); |
| 70 |
} |
| 71 |
|
| 72 |
// Bootstrap WordPress |
| 73 |
require_once $wp_load_path; |
| 74 |
|
| 75 |
// Verify MCP server is available |
| 76 |
global $metasync_mcp_server; |
| 77 |
if (!isset($metasync_mcp_server) || !$metasync_mcp_server) { |
| 78 |
fwrite(STDERR, "Error: Metasync MCP server not initialized\n"); |
| 79 |
exit(1); |
| 80 |
} |
| 81 |
|
| 82 |
$auth_result = $metasync_mcp_server->authenticate_bridge_request(getenv('WP_MCP_API_KEY')); |
| 83 |
if (is_wp_error($auth_result)) { |
| 84 |
fwrite(STDERR, 'Error: ' . $auth_result->get_error_message() . "\n"); |
| 85 |
exit(1); |
| 86 |
} |
| 87 |
|
| 88 |
// Disable output buffering for real-time communication |
| 89 |
ob_implicit_flush(true); |
| 90 |
while (ob_get_level()) { |
| 91 |
ob_end_clean(); |
| 92 |
} |
| 93 |
|
| 94 |
// Log startup to stderr (for debugging) |
| 95 |
fwrite(STDERR, "MCP stdio bridge started (PHP)\n"); |
| 96 |
fwrite(STDERR, "WordPress version: " . get_bloginfo('version') . "\n"); |
| 97 |
fwrite(STDERR, "Metasync version: " . (defined('METASYNC_VERSION') ? METASYNC_VERSION : 'unknown') . "\n"); |
| 98 |
fwrite(STDERR, "Listening on stdin...\n"); |
| 99 |
|
| 100 |
// Main event loop - read from stdin, process, write to stdout |
| 101 |
while (!feof(STDIN)) { |
| 102 |
$line = fgets(STDIN); |
| 103 |
|
| 104 |
// Skip empty lines |
| 105 |
if ($line === false || trim($line) === '') { |
| 106 |
continue; |
| 107 |
} |
| 108 |
|
| 109 |
$result = $metasync_mcp_server->process_json_rpc_request($line); |
| 110 |
if ($result['body'] !== null) { |
| 111 |
fwrite(STDOUT, json_encode($result['body']) . "\n"); |
| 112 |
fflush(STDOUT); |
| 113 |
} |
| 114 |
} |
| 115 |
|
| 116 |
fwrite(STDERR, "MCP stdio bridge terminated\n"); |
| 117 |
|