user_login : ''; $app_pass_placeholder = esc_html__('your-application-password', 'siteseo'); $user_placeholder = $username ?: esc_html__('', 'siteseo'); ?>
setting_enabled['toggle-abilities']) ? $siteseo->setting_enabled['toggle-abilities'] : ''; $toggle_nonce = wp_create_nonce('siteseo_toggle_nonce'); ?>

esc_html__('Abilities', 'siteseo'), 'state' => $abilities_api_available ? 'done' : 'todo'], ['label' => esc_html__('MCP Adapter', 'siteseo'),'state' => $adapter_active ? 'done' : 'todo'], ['label' => esc_html__('Password', 'siteseo'), 'state' => $has_app_password ? 'done' : 'todo'], ['label' => esc_html__('Test', 'siteseo'), 'state' => !empty($test_status['ok']) ? 'done' : 'todo'], ]; foreach($step_states as $idx => $st){ echo '
'; echo '' . ($st === 'done' ? '✓' : esc_html((string)($idx + 1))) . ''; echo '' . esc_html($chips[$idx]['label']) . ''; echo '
'; } ?>
' . sprintf(esc_html__('Next up: %s', 'siteseo'), '' . esc_html($next_step) . '') . '

'; }else{ echo '

' . esc_html__('All steps complete. Your AI client can now connect.', 'siteseo') . '

'; } ?>
' . esc_html__('SiteSEO registers 12 SEO abilities across 5 categories. They are picked up automatically by any installed MCP adapter and surfaced to AI clients as tools.', 'siteseo') . '

'; echo wp_kses_post( self::render_abilities_list() ); }else{ echo '

' . esc_html__('The WordPress Abilities API ships in WordPress 6.9+. SiteSEO can still work today — installing the MCP Adapter backfills the API so your AI client can connect immediately. Consider updating WordPress for native abilities support.', 'siteseo') . '

'; } ?>
' . esc_html__('The MCP Adapter plugin is installed and active. Your site now exposes an MCP server that AI clients can connect to.', 'siteseo') . '

'; }elseif($adapter_installed){ echo '

' . esc_html__('The MCP Adapter is installed but not active. Activate it to enable the MCP server.', 'siteseo') . '

'; echo ''; }else{ echo '

' . esc_html__('Install the official WordPress MCP Adapter plugin with a single click. It bridges your site to any MCP-compatible AI client.', 'siteseo') . '

'; echo ''; } ?>

' . esc_html(($test_ok ? __('Success: ', 'siteseo') : __('Failed: ', 'siteseo')) . $test_message) . '
'; }else{ echo '
'; } ?>

' . wp_json_encode([ 'siteUrl' => $site_url, 'mcpServerUrl' => $mcp_server_url, 'endpointUrl' => $endpoint_url, 'username' => $username, 'passwordHolder' => $app_pass_placeholder, 'userPlaceholder'=> $user_placeholder, 'serverName' => 'siteseo-site', 'adapterActive' => $adapter_active, 'hasAppPassword' => $has_app_password, 'i18n' => [ 'copy' => esc_html__('Copy snippet', 'siteseo'), 'copied' => esc_html__('Copied!', 'siteseo'), 'installing' => esc_html__('Installing...', 'siteseo'), 'activating' => esc_html__('Activating...', 'siteseo'), 'generating' => esc_html__('Generating...', 'siteseo'), 'testing' => esc_html__('Testing...', 'siteseo'), 'genErrTitle' => esc_html__('Could not generate the password.', 'siteseo'), 'testOkPrefix' => esc_html__('Success:', 'siteseo'), 'testFailPrefix' => esc_html__('Failed:', 'siteseo'), ], ]) . ''; // Segmented tab bar $clients = [ 'claude-desktop' => ['label' => esc_html__('Claude Desktop', 'siteseo'), 'icon' => 'C'], 'claude-code' => ['label' => esc_html__('Claude Code CLI', 'siteseo'), 'icon' => '>_'], 'cursor' => ['label' => esc_html__('Cursor', 'siteseo'), 'icon' => 'Cu'], 'vscode' => ['label' => esc_html__('VS Code', 'siteseo'), 'icon' => 'VS'], 'antigravity' => ['label' => esc_html__('Antigravity', 'siteseo'), 'icon' => 'AG'], 'gemini-cli' => ['label' => esc_html__('Gemini CLI', 'siteseo'), 'icon' => 'G', 'deprecated' => true], ]; echo '
'; $first = true; foreach($clients as $id => $data){ $badge = ''; if(!empty($data['deprecated'])){ $badge = ' ' . esc_html__('Deprecated', 'siteseo') . ''; } echo ''; $first = false; } ?>

								
							

    ' . esc_html__('No SiteSEO abilities are currently exposed. Activate the MCP Adapter to surface them.', 'siteseo') . '

    '; } $html = '
    '; foreach($groups as $group => $items){ $html .= '
    '; $html .= '

    ' . esc_html($group) . '

    '; $html .= '
      '; foreach($items as $ability){ $html .= '
    • '; $html .= '' . esc_html($ability['label']) . ''; if(!empty($ability['description'])){ $html .= '' . esc_html($ability['description']) . ''; } $html .= '
    • '; } $html .= '
    '; $html .= '
    '; } $html .= '
    '; return $html; } // The catalogue of SiteSEO abilities exposed to MCP clients. Mirrors the // abilities registered in main/abilitiesregister.php so the UI listing // stays in sync with the real wp_register_ability() calls. Kept in one // place so future AI integrations can simply append a new entry here. static function get_registered_abilities(){ return [ esc_html__('Posts', 'siteseo') => [ [ 'label' => esc_html__('Get Post SEO Data', 'siteseo'), 'description' => esc_html__('Returns the SEO snapshot for a post: title, meta description, focus keyphrase, robots flags, canonical URL and social meta.', 'siteseo'), ], [ 'label' => esc_html__('Update Post SEO Data', 'siteseo'), 'description' => esc_html__('Updates SEO fields for a post: title, description, focus keyphrase, robots flags, canonical URL and social meta.', 'siteseo'), ], [ 'label' => esc_html__('List Posts Missing SEO Data', 'siteseo'), 'description' => esc_html__('Returns posts where one or more SEO fields are unset (title, description, or focus keyphrase).', 'siteseo'), ], [ 'label' => esc_html__('List Posts by SEO Score', 'siteseo'), 'description' => esc_html__('Returns posts with their cached SEO content-analysis score.', 'siteseo'), ], ], esc_html__('Settings', 'siteseo') => [ [ 'label' => esc_html__('Get SiteSEO Settings', 'siteseo'), 'description' => esc_html__('Returns the SiteSEO settings tree (titles, social, advanced, sitemap, analytics).', 'siteseo'), ], ], esc_html__('Sitemap', 'siteseo') => [ [ 'label' => esc_html__('Get Sitemap Status', 'siteseo'), 'description' => esc_html__('Returns whether the XML sitemap is enabled and the sitemap index URL.', 'siteseo'), ], ], esc_html__('Robots.txt', 'siteseo') => [ [ 'label' => esc_html__('Get Robots.txt Output', 'siteseo'), 'description' => esc_html__('Returns the active robots.txt content served for this site.', 'siteseo'), ], [ 'label' => esc_html__('List Robots.txt Rules', 'siteseo'), 'description' => esc_html__('Lists the parsed custom robots.txt rules.', 'siteseo'), ], [ 'label' => esc_html__('Add Robots.txt Rule', 'siteseo'), 'description' => esc_html__('Adds a new custom robots.txt rule for a given user agent.', 'siteseo'), ], [ 'label' => esc_html__('Delete Robots.txt Rule', 'siteseo'), 'description' => esc_html__('Deletes a custom robots.txt rule from the physical robots.txt file.', 'siteseo'), ], ], esc_html__('Audit', 'siteseo') => [ [ 'label' => esc_html__('Get Homepage SEO Audit', 'siteseo'), 'description' => esc_html__('Returns an SEO snapshot of the homepage: title, meta description, robots flags and a content-analysis score.', 'siteseo'), ], [ 'label' => esc_html__('Get Site SEO Audit', 'siteseo'), 'description' => esc_html__('Returns a site-wide SEO health snapshot: homepage score, sitemap state, robots rule count and public post types.', 'siteseo'), ], ], ]; } // Human readable label for the current adapter state. protected static function adapter_status_label($state){ switch($state){ case 'active': return esc_html__('Active', 'siteseo'); case 'inactive': return esc_html__('Inactive', 'siteseo'); default: return esc_html__('Not installed', 'siteseo'); } } // Scan installed plugins for the MCP Adapter (folder prefix: mcp-adapter/). static function get_installed_mcp_adapter_file(){ if(!function_exists('get_plugins')){ require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all_plugins = get_plugins(); $target_file = 'mcp-adapter/mcp-adapter.php'; if(isset($all_plugins[ $target_file ])){ return $target_file; } return ''; } // Does the current user already have an MCP app password created by SiteSEO? static function current_user_has_mcp_app_password(){ $user_id = get_current_user_id(); if(!$user_id || !class_exists('WP_Application_Passwords')){ return false; } foreach(\WP_Application_Passwords::get_user_application_passwords($user_id) as $app){ if(!empty($app['app_id']) && $app['app_id'] === self::$APP_PASSWORD_APP_ID){ return true; } } return false; } /** * Read the installed MCP Adapter version from the plugin headers. * * @return string Empty string when the adapter is not installed. */ static function get_installed_mcp_adapter_version(){ $file = self::get_installed_mcp_adapter_file(); if('' === $file){ return ''; } if(!function_exists('get_plugins')){ require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all_plugins = get_plugins(); if(!isset($all_plugins[ $file ]['Version'])){ return ''; } return (string)$all_plugins[ $file ]['Version']; } // Read the saved test-connection status for the current user. static function get_test_connection_status(){ $user_id = get_current_user_id(); if(!$user_id){ return ['ok' => false, 'message' => '']; } $stored = get_user_meta($user_id, self::$TEST_STATUS_META, true); if(!is_array($stored)){ return ['ok' => false, 'message' => '']; } return [ 'ok' => !empty($stored['ok']), 'message' => !empty($stored['message']) ? $stored['message'] : '', ]; } // Persist the test-connection status for the current user. static function save_test_connection_status($ok, $message){ $user_id = get_current_user_id(); if(!$user_id){ return; } update_user_meta($user_id, self::$TEST_STATUS_META, [ 'ok' => !empty($ok), 'message' => (string)$message, ]); } }