fix(qa): health monitor SiteManager, WP plugins, favicon, download ZIPs

- Health monitor now uses SiteManager for complete site discovery
- SEO Bridge: GPLv2+ license, Tested up to 6.9, fix escaping
- OpenPanel: fix wp_unslash, reduce tags, update branding
- Add logo.svg favicon, static file serving, plugin ZIPs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-18 21:11:49 +03:30
parent 91979ee18d
commit bb851e4be2
14 changed files with 97 additions and 25 deletions

View File

@@ -268,9 +268,10 @@ Some MCP Hub tools require companion WordPress plugins:
| Tools | Requirement | | Tools | Requirement |
|-------|-------------| |-------|-------------|
| SEO tools (`wordpress_get_post_seo`, etc.) | [SEO API Bridge](wordpress-plugin/seo-api-bridge/) + Rank Math or Yoast SEO | | SEO tools (`wordpress_get_post_seo`, etc.) | [SEO API Bridge](wordpress-plugin/seo-api-bridge/) ([Download ZIP](wordpress-plugin/seo-api-bridge.zip)) + Rank Math or Yoast SEO |
| WP-CLI tools (15 tools: `wp_cache_*`, `wp_db_*`, etc.) | Docker socket + `CONTAINER` env var | | WP-CLI tools (15 tools: `wp_cache_*`, `wp_db_*`, etc.) | Docker socket + `CONTAINER` env var |
| WordPress Advanced database/system tools | Docker socket + `CONTAINER` env var | | WordPress Advanced database/system tools | Docker socket + `CONTAINER` env var |
| OpenPanel analytics integration | [OpenPanel](wordpress-plugin/openpanel/) ([Download ZIP](wordpress-plugin/openpanel.zip)) |
| WooCommerce tools | WooCommerce plugin (separate `WOOCOMMERCE_` config) | | WooCommerce tools | WooCommerce plugin (separate `WOOCOMMERCE_` config) |
**Docker socket** is needed for WP-CLI and WordPress Advanced system tools. Add to your docker-compose: **Docker socket** is needed for WP-CLI and WordPress Advanced system tools. Add to your docker-compose:

View File

@@ -9,8 +9,7 @@ This module provides comprehensive health monitoring capabilities including:
- Dependency health checks - Dependency health checks
- System uptime tracking - System uptime tracking
Author: Coolify MCP Team Author: MCP Hub Team
Version: 7.2
""" """
import json import json
@@ -24,6 +23,7 @@ from typing import Any
from core.audit_log import AuditLogger from core.audit_log import AuditLogger
from core.project_manager import ProjectManager from core.project_manager import ProjectManager
from core.site_manager import SiteManager
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -133,6 +133,7 @@ class HealthMonitor:
audit_logger: AuditLogger | None = None, audit_logger: AuditLogger | None = None,
metrics_retention_hours: int = 24, metrics_retention_hours: int = 24,
max_metrics_per_project: int = 1000, max_metrics_per_project: int = 1000,
site_manager: SiteManager | None = None,
): ):
""" """
Initialize health monitor. Initialize health monitor.
@@ -142,8 +143,10 @@ class HealthMonitor:
audit_logger: Optional audit logger for logging health events audit_logger: Optional audit logger for logging health events
metrics_retention_hours: Hours to retain historical metrics metrics_retention_hours: Hours to retain historical metrics
max_metrics_per_project: Maximum metrics to store per project max_metrics_per_project: Maximum metrics to store per project
site_manager: Optional SiteManager for comprehensive site discovery
""" """
self.project_manager = project_manager self.project_manager = project_manager
self.site_manager = site_manager
self.audit_logger = audit_logger self.audit_logger = audit_logger
self.metrics_retention_hours = metrics_retention_hours self.metrics_retention_hours = metrics_retention_hours
self.max_metrics_per_project = max_metrics_per_project self.max_metrics_per_project = max_metrics_per_project
@@ -401,9 +404,17 @@ class HealthMonitor:
start_time = time.time() start_time = time.time()
try: try:
# Get plugin instance # Get plugin instance from ProjectManager
plugin = self.project_manager.projects.get(project_id) plugin = self.project_manager.projects.get(project_id)
if not plugin:
if plugin:
# Perform health check via plugin instance
health_result = await plugin.health_check()
elif self.site_manager:
# Fallback: site exists in SiteManager but not ProjectManager
# (e.g. WooCommerce uses CONSUMER_KEY, not USERNAME/APP_PASSWORD)
health_result = await self._basic_site_health_check(project_id)
else:
return ProjectHealthStatus( return ProjectHealthStatus(
project_id=project_id, project_id=project_id,
healthy=False, healthy=False,
@@ -413,9 +424,6 @@ class HealthMonitor:
recent_errors=["Project not found"], recent_errors=["Project not found"],
alerts=["CRITICAL: Project not found"], alerts=["CRITICAL: Project not found"],
) )
# Perform health check
health_result = await plugin.health_check()
response_time_ms = (time.time() - start_time) * 1000 response_time_ms = (time.time() - start_time) * 1000
# Handle both dict and string (JSON) responses # Handle both dict and string (JSON) responses
@@ -495,6 +503,43 @@ class HealthMonitor:
alerts=[f"CRITICAL: Health check failed - {error_msg}"], alerts=[f"CRITICAL: Health check failed - {error_msg}"],
) )
async def _basic_site_health_check(self, project_id: str) -> dict[str, Any]:
"""Basic HTTP health check for sites not in ProjectManager."""
import aiohttp
if not self.site_manager:
return {"healthy": False, "message": "SiteManager not available"}
# Parse plugin_type and site_id from full_id (e.g. "woocommerce_site1")
parts = project_id.split("_", 1)
if len(parts) < 2:
return {"healthy": False, "message": f"Invalid project_id format: {project_id}"}
plugin_type = parts[0]
site_id = parts[1]
try:
config = self.site_manager.get_site_config(plugin_type, site_id)
except (KeyError, ValueError):
return {"healthy": False, "message": f"Site not found: {project_id}"}
url = config.url
if not url:
return {"healthy": False, "message": "No URL configured for site"}
try:
async with aiohttp.ClientSession() as session:
async with session.get(
url, timeout=aiohttp.ClientTimeout(total=10), ssl=False
) as resp:
return {
"healthy": resp.status < 500,
"status_code": resp.status,
"message": f"HTTP {resp.status} from {url}",
}
except Exception as e:
return {"healthy": False, "message": f"Connection failed: {e}"}
async def check_all_projects_health(self, include_metrics: bool = True) -> dict[str, Any]: async def check_all_projects_health(self, include_metrics: bool = True) -> dict[str, Any]:
""" """
Check health of all projects. Check health of all projects.
@@ -507,8 +552,14 @@ class HealthMonitor:
""" """
health_statuses = {} health_statuses = {}
# Collect all known project/site IDs from both sources
all_project_ids = set(self.project_manager.projects.keys())
if self.site_manager:
for site_info in self.site_manager.list_all_sites():
all_project_ids.add(site_info["full_id"])
# Check each project # Check each project
for project_id in self.project_manager.projects.keys(): for project_id in sorted(all_project_ids):
status = await self.check_project_health(project_id, include_metrics) status = await self.check_project_health(project_id, include_metrics)
health_statuses[project_id] = status.to_dict() health_statuses[project_id] = status.to_dict()
@@ -673,7 +724,10 @@ def get_health_monitor() -> HealthMonitor | None:
def initialize_health_monitor( def initialize_health_monitor(
project_manager: ProjectManager, audit_logger: AuditLogger | None = None, **kwargs project_manager: ProjectManager,
audit_logger: AuditLogger | None = None,
site_manager: SiteManager | None = None,
**kwargs,
) -> HealthMonitor: ) -> HealthMonitor:
""" """
Initialize the global health monitor. Initialize the global health monitor.
@@ -681,11 +735,14 @@ def initialize_health_monitor(
Args: Args:
project_manager: Project manager instance project_manager: Project manager instance
audit_logger: Optional audit logger audit_logger: Optional audit logger
site_manager: Optional SiteManager for comprehensive site discovery
**kwargs: Additional configuration options **kwargs: Additional configuration options
Returns: Returns:
HealthMonitor instance HealthMonitor instance
""" """
global _health_monitor global _health_monitor
_health_monitor = HealthMonitor(project_manager, audit_logger, **kwargs) _health_monitor = HealthMonitor(
project_manager, audit_logger, site_manager=site_manager, **kwargs
)
return _health_monitor return _health_monitor

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}MCP Hub{% endblock %}</title> <title>{% block title %}MCP Hub{% endblock %}</title>
<link rel="icon" type="image/svg+xml" href="/static/logo.svg">
<!-- Tailwind CSS from CDN --> <!-- Tailwind CSS from CDN -->
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{{ t.dashboard }}{% endblock %} - MCP Hub</title> <title>{% block title %}{{ t.dashboard }}{% endblock %} - MCP Hub</title>
<link rel="icon" type="image/svg+xml" href="/static/logo.svg">
<!-- Vazirmatn Font for Persian --> <!-- Vazirmatn Font for Persian -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ t.login_title }} - MCP Hub</title> <title>{{ t.login_title }} - MCP Hub</title>
<link rel="icon" type="image/svg+xml" href="/static/logo.svg">
<!-- Tailwind CSS --> <!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>

View File

@@ -0,0 +1 @@
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024"><style>.a{fill:#51b9f4}.b{fill:#fec13d}</style><path class="a" d="m496.4 11.5q5.4-0.4 10.9-0.5 5.4-0.1 10.9 0.1 5.4 0.2 10.8 0.7 5.5 0.4 10.9 1.2c56.2 7.7 99.4 36 133.3 81.3 31 41.4 41 94.3 34.8 144.5-6.9 54.7-31.6 89.2-71.8 124.8-9.7 8.6-19.4 21-29.9 28.7 8 7.2 17.5 17 25.1 24.8 8.6-11.9 22.8-24.8 33.4-35.4 10.6-10.1 20.7-22.2 32.2-31.2 44.5-34.8 109.1-43.7 163.2-31.3 97.6 22.4 162.8 115.9 151.4 215.3-12.8 111.6-114.9 189.3-225.7 173.7-56.9-8-89.1-29.9-126.7-72.5-8.4-9.5-20.3-19-28.1-29.2-3 4.4-20.1 21-24.7 25.2q0.8 0.7 1.6 1.4c10.3 8.9 20.5 20.3 30 29.2 31.4 29.2 55.6 55.7 65.1 98.8 3 13.7 4.9 22.1 6.1 36.6 4.1 52.5-11.2 105-46 144.9-28.1 32.3-64.1 56.5-106.4 64.9-9.6 2-17.3 3.9-27.3 4.6-11.3 1.4-27.3 0.9-38.7-0.3-55.1-6.2-100.6-31.3-135.2-74.7-6.8-8.5-12.8-17.6-18.1-27.2-5.2-9.5-9.7-19.4-13.3-29.7-3.6-10.3-6.3-20.8-8.1-31.6-1.9-10.7-2.8-21.5-2.8-32.4-0.3-41.9 11.1-88.4 38.8-121 5-5.8 10.5-11.8 16.2-17.1 15.8-14.8 31.2-33.3 48-46.7-7.7-8-17.1-16.5-24.2-24.6-8.4 9.8-18.5 19-27.4 28.3-10.5 10.8-20.7 21.7-31.8 31.9-36.3 32.8-85.8 44.5-133.6 42.7-12.8-0.5-25.5-2.3-38-5.3-12.5-3-24.6-7.3-36.2-12.7-11.7-5.4-22.7-11.9-33.1-19.4-10.3-7.6-19.9-16.2-28.6-25.6-46.8-50.7-62.6-116.6-46.9-183.1 8.7-41.1 30.3-72.1 60.7-100.4 64.9-60.6 180.9-67.1 250.4-10.7 10.9 8.8 22.5 21.5 32.4 31.7 10.5 10.9 21.9 21.7 32.1 33 8.1-11 14.4-15.9 23.9-24.9-37.7-41.9-77.8-63.2-94-122.1-17.9-65.2-8.7-131.9 34.5-185.2 37.9-46.8 81.1-66.8 139.9-73.5zm-44.5 380.8q-5.1 5.1-10.1 10.1-5.1 5-10.2 10.1-5 5-10.1 10.1-5 5-10 10.1c-3.7 3.7-16.3 16.8-20.1 19.1-12.9-14.3-28.9-27.9-41.3-41.9-35.7-40-68.4-67.4-124.3-70.7-53.9-3.2-94.4 9.6-135 46-8.2 7.5-15.6 15.7-22.2 24.7-6.6 8.9-12.3 18.5-16.9 28.5-4.7 10.1-8.4 20.6-11.1 31.4-2.6 10.7-4.2 21.7-4.6 32.8-6.6 133.7 129.3 221.3 249 166.3 3.7-2.1 7.3-4.3 10.8-6.5 10.9-6.8 20.8-15.5 30.3-24.1 8.2-7.5 14.8-16.4 22.4-24.5 13.8-14.7 29-28.3 43.3-42.4 4.5 5.4 11.9 12.5 17.1 17.6l28.4 28.4c3.5 3.5 11.1 10.7 14.1 14.3-7.4 9.1-18.4 18.4-26.7 27.1-18.8 19.6-40.3 36.2-56.9 57.9-24.9 32.5-31.7 75.7-28.5 115.7 4 47.7 27.3 87 63.8 117.4 6.4 5.3 13.3 10.2 20.5 14.5 7.2 4.3 14.8 8 22.5 11.2 7.8 3.2 15.8 5.8 23.9 7.8 8.2 2 16.5 3.4 24.8 4.2 7.8 0.6 17.8 0.5 25.6 0.3 35.2-1.1 68.9-14.1 95.9-36.2 8.1-6.7 12.7-9.6 20.1-17.7 33.7-36.9 49.6-74.3 48.1-124.8-1.2-38-9.3-69-34.2-98.8-10.3-12.4-23-23.8-34.6-34.8-14.7-14-29.1-29.5-43.8-43.4l-0.3-0.3c5.7-7.1 11.7-13.2 18.2-19.6 13.7-13.5 26.9-27.5 41.1-40.5l0.6 0.6c9.5 7.9 18.7 19.1 27.8 27.7 18.6 17.9 34.5 38.1 54.9 54.1 33.2 26.3 76.5 33.7 117.8 30.3 52.5-4.3 98.3-36.2 127.8-78.8 18.8-27.3 28.5-62 27.5-95.2-0.2-5 0-9.8-0.4-14.8-1-11.4-3.1-22.6-6.3-33.5-3.2-10.9-7.6-21.5-12.9-31.6-5.4-10-11.7-19.5-19-28.3-7.2-8.8-15.4-16.8-24.2-24-40.2-32.9-81.7-43.3-132.6-38.3-50.9 4.9-82 28.5-114.5 65.8-6.4 7.3-15 14.7-21.6 22-5.5 5.4-19.7 20.8-25 24.3-19.2-20.2-40.1-39.5-59.3-59.7 12.5-15 27.9-29.5 41.9-43.3 5.9-5.8 13.8-11.6 20.3-18.1 32.1-32.4 48.9-63.7 50.5-110 0.4-10.8 0.6-19.1-0.4-30.1-1.1-11.2-3.3-22.3-6.6-33.1-3.3-10.7-7.6-21.2-13-31.1-5.3-9.9-11.7-19.3-18.9-27.9-7.2-8.7-15.3-16.6-24-23.7-29.7-24.3-61.1-36.9-99.7-39-4.5-0.2-14.1-0.4-18.5 0.1-49.6 3.5-86.3 20.5-120.3 57.4-33.6 36.6-46.8 80.9-43.8 130 2.7 45.3 18.5 77.4 51.2 108.8 20 19.3 42.8 39.8 61.7 60z"/><path class="b" d="m210.1 432.1c43.8 0.6 78.9 36.4 78.7 80.1-0.3 43.8-35.9 79.2-79.6 79.2-44.3 0-80-35.9-79.8-80.1 0.3-44.2 36.5-79.8 80.7-79.2zm1.1 134.1c30-1.2 53.3-26.3 52.3-56.3-1-29.9-26-53.4-55.9-52.6-30.3 0.8-54.1 26-53.1 56.3 1 30.2 26.5 53.9 56.7 52.6z"/><path class="b" d="m503.9 129.7c43.8-4.5 82.9 27.4 87.3 71.2 4.4 43.8-27.5 82.9-71.3 87.3-43.8 4.3-82.8-27.6-87.2-71.3-4.4-43.8 27.5-82.8 71.2-87.2zm12.9 133.5c29.9-2.7 52-29.1 49.4-59.1-2.7-30-29.1-52.1-59.1-49.5-30 2.6-52.3 29.1-49.6 59.1 2.7 30.1 29.2 52.3 59.3 49.5z"/><path class="b" d="m503.3 735.5c43.8-4.8 83.1 26.9 87.9 70.7 4.7 43.7-27 83-70.8 87.7-43.7 4.6-82.9-27-87.6-70.7-4.7-43.7 26.9-82.9 70.5-87.7zm12.5 133.5c29.9-2.1 52.5-28 50.5-58-2-30-27.8-52.7-57.8-50.8-30.2 1.8-53.1 27.9-51.1 58 2 30.2 28.2 53 58.4 50.8z"/><path class="b" d="m805.8 432.7c43.7-5 83.3 26.4 88.2 70.2 4.9 43.7-26.6 83.2-70.3 88-43.7 4.9-83-26.5-87.9-70.2-5-43.6 26.4-83 70-88zm11.6 133.6c29.9-1.5 53-26.9 51.8-56.8-1.3-30-26.5-53.3-56.4-52.2-30.3 1-53.9 26.5-52.7 56.8 1.3 30.3 27 53.7 57.3 52.2z"/><path class="a" d="m511 451.9c3.1 0.9 33.1 32.8 37.8 36.9 2.7 2.4 19.9 19.4 22.3 22.7-2.6 4.7-53.2 55.7-59.3 60.2-0.2 0-0.4 0-0.5-0.1-2.1-0.8-56.2-54.4-59.3-59.3 1.4-4.2 52.5-54.7 59-60.4zm-24.9 59.5c3.3 4.6 19.8 20.5 24.8 25.4 0.5 0 1 0.1 1.4 0 8-8.8 16-16.6 24.6-24.8-3-3-24.1-24.6-25.8-25.3-3.7 4.3-21 21.9-25 24.7z"/></svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -1055,6 +1055,7 @@ from core import initialize_health_monitor
health_monitor = initialize_health_monitor( health_monitor = initialize_health_monitor(
project_manager=project_manager, project_manager=project_manager,
audit_logger=audit_logger, audit_logger=audit_logger,
site_manager=site_manager,
metrics_retention_hours=24, metrics_retention_hours=24,
max_metrics_per_project=1000, max_metrics_per_project=1000,
) )
@@ -4441,6 +4442,13 @@ def create_multi_endpoint_app(transport: str = "streamable-http"):
safe_name = project_id.replace("-", "_").replace(".", "_") safe_name = project_id.replace("-", "_").replace(".", "_")
routes.append(Mount(mount_path, app=project_app, name=f"mcp_project_{safe_name}")) routes.append(Mount(mount_path, app=project_app, name=f"mcp_project_{safe_name}"))
# Static files (favicon, logo)
_static_dir = os.path.join(os.path.dirname(__file__), "core", "templates", "static")
if os.path.isdir(_static_dir):
from starlette.staticfiles import StaticFiles
routes.append(Mount("/static", app=StaticFiles(directory=_static_dir), name="static"))
# Main admin endpoint (must be last - catches all remaining routes) # Main admin endpoint (must be last - catches all remaining routes)
routes.append(Mount("/", app=main_app, name="mcp_admin")) routes.append(Mount("/", app=main_app, name="mcp_admin"))

Binary file not shown.

View File

@@ -3,11 +3,13 @@
* Plugin Name: OpenPanel * Plugin Name: OpenPanel
* Description: Activate OpenPanel to start tracking your website. Supports both Cloud and Self-Hosted instances. * Description: Activate OpenPanel to start tracking your website. Supports both Cloud and Self-Hosted instances.
* Version: 1.1.1 * Version: 1.1.1
* Author: OpenPanel / Airano * Plugin URI: https://github.com/airano-ir/mcphub
* Author: OpenPanel / MCP Hub
* Author URI: https://github.com/airano-ir
* License: GPLv2 or later * License: GPLv2 or later
* Requires at least: 5.8 * Requires at least: 5.8
* Requires PHP: 7.4 * Requires PHP: 7.4
* Tested up to: 6.8 * Tested up to: 6.9
* Text Domain: openpanel * Text Domain: openpanel
*/ */
@@ -479,7 +481,7 @@ final class OP_WP_Proxy {
// Content-Type is a special header NOT prefixed with HTTP_ in PHP // Content-Type is a special header NOT prefixed with HTTP_ in PHP
// This is critical for OpenPanel API which requires application/json // This is critical for OpenPanel API which requires application/json
if (!empty($_SERVER['CONTENT_TYPE'])) { if (!empty($_SERVER['CONTENT_TYPE'])) {
$headers['Content-Type'] = sanitize_text_field($_SERVER['CONTENT_TYPE']); $headers['Content-Type'] = sanitize_text_field(wp_unslash($_SERVER['CONTENT_TYPE']));
} }
foreach ($_SERVER as $name => $value) { foreach ($_SERVER as $name => $value) {

View File

@@ -1,14 +1,14 @@
=== OpenPanel === === OpenPanel ===
Contributors: openpanel, airano Contributors: openpanel, airano
Tags: analytics, web analytics, privacy-friendly, tracking, proxy, self-hosted Tags: analytics, privacy-friendly, tracking, self-hosted, proxy
Requires at least: 5.8 Requires at least: 5.8
Tested up to: 6.8 Tested up to: 6.9
Requires PHP: 7.4 Requires PHP: 7.4
Stable tag: 1.1.1 Stable tag: 1.1.1
License: GPLv2 or later License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html License URI: https://www.gnu.org/licenses/gpl-2.0.html
OpenPanel WordPress plugin - Privacy-friendly analytics with ad-blocker resistance. Supports both OpenPanel Cloud and Self-Hosted instances. Inline tracking scripts and proxy API calls through your domain. Privacy-friendly analytics with ad-blocker resistance. Supports OpenPanel Cloud and Self-Hosted instances.
== Description == == Description ==

Binary file not shown.

View File

@@ -2,7 +2,7 @@
**Version:** 1.3.0 **Version:** 1.3.0
**Requires:** WordPress 5.0+, PHP 7.4+ **Requires:** WordPress 5.0+, PHP 7.4+
**License:** MIT **License:** GPLv2 or later
## Description ## Description
@@ -421,7 +421,7 @@ For issues related to:
## License ## License
MIT License - See LICENSE file for details GPLv2 or later - See [LICENSE](https://www.gnu.org/licenses/gpl-2.0.html) for details
## Credits ## Credits

View File

@@ -2,11 +2,11 @@
Contributors: airano Contributors: airano
Tags: seo, rest-api, rank-math, yoast, mcp Tags: seo, rest-api, rank-math, yoast, mcp
Requires at least: 5.0 Requires at least: 5.0
Tested up to: 6.7 Tested up to: 6.9
Requires PHP: 7.4 Requires PHP: 7.4
Stable tag: 1.3.0 Stable tag: 1.3.0
License: MIT License: GPLv2 or later
License URI: https://opensource.org/licenses/MIT License URI: https://www.gnu.org/licenses/gpl-2.0.html
Exposes Rank Math SEO and Yoast SEO meta fields via WordPress REST API for use with MCP servers and AI agents. Exposes Rank Math SEO and Yoast SEO meta fields via WordPress REST API for use with MCP servers and AI agents.

View File

@@ -6,7 +6,7 @@
* Version: 1.3.0 * Version: 1.3.0
* Author: MCP Hub * Author: MCP Hub
* Author URI: https://github.com/airano-ir * Author URI: https://github.com/airano-ir
* License: MIT * License: GPL-2.0-or-later
* Requires at least: 5.0 * Requires at least: 5.0
* Requires PHP: 7.4 * Requires PHP: 7.4
* Text Domain: seo-api-bridge * Text Domain: seo-api-bridge
@@ -690,8 +690,8 @@ class SEO_API_Bridge {
$supported_types = implode(', ', $this->supported_post_types); $supported_types = implode(', ', $this->supported_post_types);
echo '<div class="notice notice-success is-dismissible">'; echo '<div class="notice notice-success is-dismissible">';
echo '<p><strong>SEO API Bridge v' . self::VERSION . ':</strong> Successfully registered meta fields for ' . implode(' and ', $active_plugins) . '.</p>'; echo '<p><strong>SEO API Bridge v' . esc_html( self::VERSION ) . ':</strong> ' . esc_html( sprintf( 'Successfully registered meta fields for %s.', implode( ' and ', $active_plugins ) ) ) . '</p>';
echo '<p><strong>Supported post types:</strong> ' . $supported_types . '</p>'; echo '<p><strong>Supported post types:</strong> ' . esc_html( $supported_types ) . '</p>';
if ($woocommerce_active) { if ($woocommerce_active) {
echo '<p><strong>WooCommerce:</strong> Detected and supported. Product SEO fields are available via REST API.</p>'; echo '<p><strong>WooCommerce:</strong> Detected and supported. Product SEO fields are available via REST API.</p>';