fix(qa): resolve A.1-A.7 code bugs and B.1-B.8 documentation issues
Code fixes: - Rename "Coolify Admin" to "MCP Hub Admin" in endpoints - Enrich list_projects with alias and endpoint URL - Downgrade OAuth InvalidTokenError log from WARNING to DEBUG - Add 7 missing health tools to /system/mcp endpoint (now 24 tools) - Remove "Phase 7.2" from health monitor log - Fix WP Advanced health check to pass with REST API only - Downgrade duplicate alias warnings to INFO Documentation fixes: - Document SSE transport and Bearer auth requirement - Recommend plugin-specific endpoints to save tokens - Document plugin vs project endpoint differences - WordPress plugin requirements for SEO/WP-CLI tools - Docker socket mounting guide for WP-CLI - Mark MASTER_API_KEY as recommended (auto-generates temp key) - Env var naming convention with prefix table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,16 @@ In Claude Desktop's `claude_desktop_config.json`:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
MCP Hub uses **Bearer token** authentication. Include the `Authorization` header in all requests:
|
||||||
|
|
||||||
|
```
|
||||||
|
Authorization: Bearer YOUR_MASTER_API_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
This applies to both MCP clients and API calls. Query parameter auth is not supported.
|
||||||
|
|
||||||
## Using Docker Compose
|
## Using Docker Compose
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -69,6 +79,8 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- mcphub-data:/app/data
|
- mcphub-data:/app/data
|
||||||
- mcphub-logs:/app/logs
|
- mcphub-logs:/app/logs
|
||||||
|
# Optional: mount Docker socket for WP-CLI tools
|
||||||
|
# - /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
@@ -76,6 +88,9 @@ volumes:
|
|||||||
mcphub-logs:
|
mcphub-logs:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **WP-CLI tools** (cache flush, database export, plugin updates via CLI) require Docker socket access.
|
||||||
|
> Add `WORDPRESS_SITE1_CONTAINER=your-wp-container-name` to your `.env` and uncomment the Docker socket volume above.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
@@ -92,7 +107,7 @@ docker compose up -d
|
|||||||
|
|
||||||
| Variable | Required | Description |
|
| Variable | Required | Description |
|
||||||
|----------|----------|-------------|
|
|----------|----------|-------------|
|
||||||
| `MASTER_API_KEY` | **Yes** | API key for authentication |
|
| `MASTER_API_KEY` | Recommended | API key for authentication. If omitted, a temporary key is auto-generated and printed to logs |
|
||||||
| `WORDPRESS_SITE1_URL` | For WP | WordPress site URL |
|
| `WORDPRESS_SITE1_URL` | For WP | WordPress site URL |
|
||||||
| `WORDPRESS_SITE1_USERNAME` | For WP | WordPress admin username |
|
| `WORDPRESS_SITE1_USERNAME` | For WP | WordPress admin username |
|
||||||
| `WORDPRESS_SITE1_APP_PASSWORD` | For WP | WordPress Application Password |
|
| `WORDPRESS_SITE1_APP_PASSWORD` | For WP | WordPress Application Password |
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ ENDPOINT_CONFIGS = {
|
|||||||
# Mounted at "/" → /mcp (FastMCP adds /mcp automatically)
|
# Mounted at "/" → /mcp (FastMCP adds /mcp automatically)
|
||||||
EndpointType.ADMIN: EndpointConfig(
|
EndpointType.ADMIN: EndpointConfig(
|
||||||
path="/",
|
path="/",
|
||||||
name="Coolify Admin",
|
name="MCP Hub Admin",
|
||||||
description="Full administrative access to all tools and plugins",
|
description="Full administrative access to all tools and plugins",
|
||||||
endpoint_type=EndpointType.ADMIN,
|
endpoint_type=EndpointType.ADMIN,
|
||||||
plugin_types=[], # Empty = all plugins
|
plugin_types=[], # Empty = all plugins
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Enhanced Health Monitoring System for MCP Server (Phase 7.2)
|
Enhanced Health Monitoring System for MCP Server
|
||||||
|
|
||||||
This module provides comprehensive health monitoring capabilities including:
|
This module provides comprehensive health monitoring capabilities including:
|
||||||
- Response time tracking
|
- Response time tracking
|
||||||
@@ -172,7 +172,7 @@ class HealthMonitor:
|
|||||||
# Request rate tracking (for requests per minute)
|
# Request rate tracking (for requests per minute)
|
||||||
self.request_timestamps: deque = deque(maxlen=1000)
|
self.request_timestamps: deque = deque(maxlen=1000)
|
||||||
|
|
||||||
logger.info("HealthMonitor initialized (Phase 7.2)")
|
logger.info("HealthMonitor initialized")
|
||||||
|
|
||||||
def _setup_default_thresholds(self):
|
def _setup_default_thresholds(self):
|
||||||
"""Setup default alert thresholds."""
|
"""Setup default alert thresholds."""
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ class TokenManager:
|
|||||||
logger.warning("Expired access token")
|
logger.warning("Expired access token")
|
||||||
raise
|
raise
|
||||||
except jwt.InvalidTokenError as e:
|
except jwt.InvalidTokenError as e:
|
||||||
logger.warning(f"Invalid access token: {e}")
|
logger.debug(f"Invalid access token: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def generate_refresh_token(self, client_id: str, access_token: str) -> str:
|
def generate_refresh_token(self, client_id: str, access_token: str) -> str:
|
||||||
|
|||||||
@@ -94,15 +94,13 @@ class SiteRegistry:
|
|||||||
|
|
||||||
# Log alias conflicts if any
|
# Log alias conflicts if any
|
||||||
if self.alias_conflicts:
|
if self.alias_conflicts:
|
||||||
self.logger.warning("=" * 50)
|
self.logger.info("Duplicate alias conflicts detected:")
|
||||||
self.logger.warning("DUPLICATE ALIAS CONFLICTS DETECTED:")
|
|
||||||
for alias, full_ids in self.alias_conflicts.items():
|
for alias, full_ids in self.alias_conflicts.items():
|
||||||
winner = self.aliases.get(alias)
|
winner = self.aliases.get(alias)
|
||||||
losers = [fid for fid in full_ids if fid != winner]
|
losers = [fid for fid in full_ids if fid != winner]
|
||||||
self.logger.warning(
|
self.logger.info(
|
||||||
f" Alias '{alias}': {winner} (winner), {losers} (using full_id)"
|
f" Alias '{alias}': {winner} (winner), {losers} (using full_id)"
|
||||||
)
|
)
|
||||||
self.logger.warning("=" * 50)
|
|
||||||
|
|
||||||
# Reserved words that should NOT be interpreted as site IDs
|
# Reserved words that should NOT be interpreted as site IDs
|
||||||
RESERVED_SITE_WORDS = {
|
RESERVED_SITE_WORDS = {
|
||||||
@@ -206,7 +204,7 @@ class SiteRegistry:
|
|||||||
if alias not in self.alias_conflicts:
|
if alias not in self.alias_conflicts:
|
||||||
self.alias_conflicts[alias] = [existing_full_id]
|
self.alias_conflicts[alias] = [existing_full_id]
|
||||||
self.alias_conflicts[alias].append(full_id)
|
self.alias_conflicts[alias].append(full_id)
|
||||||
self.logger.warning(
|
self.logger.info(
|
||||||
f"Duplicate alias '{alias}': {full_id} conflicts with {existing_full_id}. "
|
f"Duplicate alias '{alias}': {full_id} conflicts with {existing_full_id}. "
|
||||||
f"{full_id} will use full_id for endpoint path."
|
f"{full_id} will use full_id for endpoint path."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ Edit the `.env` file with your credentials:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# ============================================
|
# ============================================
|
||||||
# Required
|
# Authentication (recommended — auto-generates temp key if omitted)
|
||||||
# ============================================
|
# ============================================
|
||||||
MASTER_API_KEY=your-secure-key-here
|
MASTER_API_KEY=your-secure-key-here
|
||||||
|
|
||||||
@@ -171,6 +171,52 @@ RATE_LIMIT_PER_HOUR=1000
|
|||||||
RATE_LIMIT_PER_DAY=10000
|
RATE_LIMIT_PER_DAY=10000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### WordPress Plugin Requirements
|
||||||
|
|
||||||
|
Some WordPress tools require additional plugins on your WordPress site:
|
||||||
|
|
||||||
|
| MCP Tool Category | WordPress Plugin Required |
|
||||||
|
|-------------------|--------------------------|
|
||||||
|
| SEO tools (`get_post_seo`, `update_post_seo`) | **Rank Math** or **Yoast SEO** |
|
||||||
|
| WP-CLI tools (`wp_cache_flush`, `wp_db_export`, etc.) | Docker socket access + `CONTAINER` env var |
|
||||||
|
| WooCommerce tools | **WooCommerce** 3.0+ (separate `WOOCOMMERCE_` config) |
|
||||||
|
|
||||||
|
### Docker Socket for WP-CLI Tools
|
||||||
|
|
||||||
|
WP-CLI tools (cache management, database export, plugin updates via CLI) require Docker socket access:
|
||||||
|
|
||||||
|
1. Add the container name to your `.env`:
|
||||||
|
```bash
|
||||||
|
WORDPRESS_SITE1_CONTAINER=your-wp-container-name
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Mount the Docker socket in `docker-compose.yaml`:
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
Without Docker socket, WP-CLI tools will return a "not available" message but all REST API tools work normally.
|
||||||
|
|
||||||
|
### Environment Variable Naming Convention
|
||||||
|
|
||||||
|
All site configuration follows the pattern: `{PLUGIN_PREFIX}_{SITE_ID}_{CONFIG_KEY}`
|
||||||
|
|
||||||
|
| Plugin | Prefix | Example |
|
||||||
|
|--------|--------|---------|
|
||||||
|
| WordPress | `WORDPRESS_` | `WORDPRESS_SITE1_URL` |
|
||||||
|
| WooCommerce | `WOOCOMMERCE_` | `WOOCOMMERCE_STORE1_URL` |
|
||||||
|
| WordPress Advanced | `WORDPRESS_ADVANCED_` | `WORDPRESS_ADVANCED_SITE1_URL` |
|
||||||
|
| Gitea | `GITEA_` | `GITEA_REPO1_URL` |
|
||||||
|
| n8n | `N8N_` | `N8N_INSTANCE1_URL` |
|
||||||
|
| Supabase | `SUPABASE_` | `SUPABASE_PROJECT1_URL` |
|
||||||
|
| OpenPanel | `OPENPANEL_` | `OPENPANEL_INSTANCE1_URL` |
|
||||||
|
| Appwrite | `APPWRITE_` | `APPWRITE_PROJECT1_URL` |
|
||||||
|
| Directus | `DIRECTUS_` | `DIRECTUS_INSTANCE1_URL` |
|
||||||
|
|
||||||
|
- `SITE_ID` can be any alphanumeric identifier (e.g., `SITE1`, `PROD`, `MYBLOG`)
|
||||||
|
- Add `_ALIAS` for a friendly name used in tool calls (e.g., `WORDPRESS_SITE1_ALIAS=myblog`)
|
||||||
|
|
||||||
### Configuration Tips
|
### Configuration Tips
|
||||||
|
|
||||||
- **Site Aliases**: Use friendly names like `myblog`, `mystore`, or `mygitea`
|
- **Site Aliases**: Use friendly names like `myblog`, `mystore`, or `mygitea`
|
||||||
@@ -240,6 +286,8 @@ docker compose logs -f mcphub
|
|||||||
|
|
||||||
## Connect Your AI Client
|
## Connect Your AI Client
|
||||||
|
|
||||||
|
MCP Hub uses **SSE (Server-Sent Events)** transport over HTTP. All requests require **Bearer token** authentication via the `Authorization` header. Query parameter auth is not supported.
|
||||||
|
|
||||||
### Claude Desktop
|
### Claude Desktop
|
||||||
|
|
||||||
Add to `claude_desktop_config.json`:
|
Add to `claude_desktop_config.json`:
|
||||||
@@ -343,7 +391,7 @@ The `site` parameter accepts either a **site_id** (e.g., `site1`) or an **alias*
|
|||||||
|
|
||||||
### Multi-Endpoint Architecture
|
### Multi-Endpoint Architecture
|
||||||
|
|
||||||
Use specific endpoints to limit tool access:
|
Use specific endpoints to limit tool access and save tokens:
|
||||||
|
|
||||||
```
|
```
|
||||||
/mcp → All 596 tools (Master API Key)
|
/mcp → All 596 tools (Master API Key)
|
||||||
@@ -351,9 +399,24 @@ Use specific endpoints to limit tool access:
|
|||||||
/wordpress/mcp → WordPress tools (67 tools)
|
/wordpress/mcp → WordPress tools (67 tools)
|
||||||
/woocommerce/mcp → WooCommerce tools (28 tools)
|
/woocommerce/mcp → WooCommerce tools (28 tools)
|
||||||
/gitea/mcp → Gitea tools (56 tools)
|
/gitea/mcp → Gitea tools (56 tools)
|
||||||
|
/n8n/mcp → n8n tools (56 tools)
|
||||||
|
/supabase/mcp → Supabase tools (70 tools)
|
||||||
|
/openpanel/mcp → OpenPanel tools (73 tools)
|
||||||
|
/appwrite/mcp → Appwrite tools (100 tools)
|
||||||
|
/directus/mcp → Directus tools (100 tools)
|
||||||
/project/{alias}/mcp → Per-project (auto-injects site)
|
/project/{alias}/mcp → Per-project (auto-injects site)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Recommendation**: Use plugin-specific endpoints (e.g., `/wordpress/mcp`) instead of `/mcp` when possible. This reduces the number of tools your AI client loads, saving context tokens and improving response quality.
|
||||||
|
|
||||||
|
**Plugin endpoint vs Project endpoint:**
|
||||||
|
|
||||||
|
| Feature | Plugin endpoint (`/wordpress/mcp`) | Project endpoint (`/project/myblog/mcp`) |
|
||||||
|
|---------|-----------------------------------|----------------------------------------|
|
||||||
|
| Tools loaded | All tools for that plugin type | Same tools, but `site` parameter auto-injected |
|
||||||
|
| Site selection | Must pass `site` parameter manually | Site is auto-selected (no `site` param needed) |
|
||||||
|
| Best for | Managing multiple sites of same type | Dedicated access to a single site |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Docker Deployment
|
## Docker Deployment
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class WordPressAdvancedPlugin(BasePlugin):
|
|||||||
rest_api_available = False
|
rest_api_available = False
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"healthy": wp_cli_available, # Only WP-CLI is critical for wordpress_advanced
|
"healthy": wp_cli_available or rest_api_available,
|
||||||
"wp_cli_available": wp_cli_available,
|
"wp_cli_available": wp_cli_available,
|
||||||
"rest_api_available": rest_api_available,
|
"rest_api_available": rest_api_available,
|
||||||
"features": {
|
"features": {
|
||||||
|
|||||||
122
server.py
122
server.py
@@ -1183,6 +1183,19 @@ async def _list_projects_impl() -> str:
|
|||||||
"""Internal implementation for listing projects."""
|
"""Internal implementation for listing projects."""
|
||||||
try:
|
try:
|
||||||
projects = project_manager.list_projects()
|
projects = project_manager.list_projects()
|
||||||
|
|
||||||
|
# Enrich with alias and endpoint info from SiteManager
|
||||||
|
all_sites = site_manager.list_all_sites()
|
||||||
|
site_lookup = {s["full_id"]: s for s in all_sites}
|
||||||
|
|
||||||
|
for project in projects:
|
||||||
|
full_id = project.get("id", "")
|
||||||
|
site_info = site_lookup.get(full_id, {})
|
||||||
|
alias = site_info.get("alias")
|
||||||
|
path_suffix = alias if alias and alias != site_info.get("site_id") else full_id
|
||||||
|
project["alias"] = alias
|
||||||
|
project["endpoint"] = f"/project/{path_suffix}/mcp"
|
||||||
|
|
||||||
result = {"total": len(projects), "projects": projects}
|
result = {"total": len(projects), "projects": projects}
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@@ -2827,7 +2840,7 @@ async def oauth_get_client_info(client_id: str) -> dict:
|
|||||||
# === PHASE X.3: SYSTEM TOOLS ===
|
# === PHASE X.3: SYSTEM TOOLS ===
|
||||||
|
|
||||||
|
|
||||||
# Internal implementations for Phase X.3 system tools
|
# Internal implementations for system tools
|
||||||
async def _get_endpoints_impl() -> dict:
|
async def _get_endpoints_impl() -> dict:
|
||||||
"""Internal implementation for listing endpoints."""
|
"""Internal implementation for listing endpoints."""
|
||||||
try:
|
try:
|
||||||
@@ -2835,7 +2848,7 @@ async def _get_endpoints_impl() -> dict:
|
|||||||
endpoints = [
|
endpoints = [
|
||||||
{
|
{
|
||||||
"path": "/mcp",
|
"path": "/mcp",
|
||||||
"name": "Coolify Admin",
|
"name": "MCP Hub Admin",
|
||||||
"description": "Full administrative access to all tools",
|
"description": "Full administrative access to all tools",
|
||||||
"require_master_key": True,
|
"require_master_key": True,
|
||||||
"plugin_types": ["all"],
|
"plugin_types": ["all"],
|
||||||
@@ -2846,7 +2859,7 @@ async def _get_endpoints_impl() -> dict:
|
|||||||
"description": "System management tools (API keys, OAuth, health, rate limiting)",
|
"description": "System management tools (API keys, OAuth, health, rate limiting)",
|
||||||
"require_master_key": True,
|
"require_master_key": True,
|
||||||
"plugin_types": ["system"],
|
"plugin_types": ["system"],
|
||||||
"tool_count": 16,
|
"tool_count": 24,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/wordpress/mcp",
|
"path": "/wordpress/mcp",
|
||||||
@@ -3333,13 +3346,14 @@ async def manage_api_keys_rotate(project_id: str) -> dict:
|
|||||||
|
|
||||||
def create_system_mcp():
|
def create_system_mcp():
|
||||||
"""
|
"""
|
||||||
Create System-only MCP instance (Phase X.3).
|
Create System-only MCP instance.
|
||||||
|
|
||||||
Contains only 16 system management tools:
|
Contains 24 system management tools:
|
||||||
- API Key Management (6)
|
- API Key Management (6): create, list, get_info, revoke, delete, rotate
|
||||||
- OAuth Management (3)
|
- OAuth Management (4): register_client, list_clients, revoke_client, get_client_info
|
||||||
- Rate Limiting (3)
|
- Rate Limiting (3): get_stats, reset, set_config
|
||||||
- Health & Status (4)
|
- Health & Monitoring (7): check_all, get_project, get_metrics, get_uptime, get_project_metrics, export
|
||||||
|
- Status & Discovery (4): list_projects, get_endpoints, get_system_info, get_audit_log
|
||||||
"""
|
"""
|
||||||
from fastmcp import FastMCP
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
@@ -3349,7 +3363,9 @@ Available tools:
|
|||||||
• API Key Management: create, list, get_info, revoke, delete, rotate
|
• API Key Management: create, list, get_info, revoke, delete, rotate
|
||||||
• OAuth Management: register_client, list_clients, revoke_client, get_client_info
|
• OAuth Management: register_client, list_clients, revoke_client, get_client_info
|
||||||
• Rate Limiting: get_stats, reset, set_config
|
• Rate Limiting: get_stats, reset, set_config
|
||||||
• Health & Status: list_projects, get_endpoints, get_system_info, get_audit_log
|
• Health & Monitoring: check_all_projects_health, get_project_health, get_system_metrics,
|
||||||
|
get_system_uptime, get_project_metrics, export_health_metrics, get_project_info
|
||||||
|
• Status & Discovery: list_projects, get_endpoints, get_system_info, get_audit_log
|
||||||
|
|
||||||
Use list_projects() to see all configured sites across all plugin types.
|
Use list_projects() to see all configured sites across all plugin types.
|
||||||
Use get_endpoints() to see all available MCP endpoints."""
|
Use get_endpoints() to see all available MCP endpoints."""
|
||||||
@@ -3460,6 +3476,92 @@ Use get_endpoints() to see all available MCP endpoints."""
|
|||||||
requests_per_minute, requests_per_hour, requests_per_day
|
requests_per_minute, requests_per_hour, requests_per_day
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Health & Monitoring tools (7)
|
||||||
|
@system_mcp.tool()
|
||||||
|
async def get_project_info(project_id: str) -> str:
|
||||||
|
"""Get detailed information about a specific project."""
|
||||||
|
try:
|
||||||
|
info = project_manager.get_project_info(project_id)
|
||||||
|
if info is None:
|
||||||
|
return f"Project '{project_id}' not found. Use list_projects to see available projects."
|
||||||
|
import json
|
||||||
|
|
||||||
|
return json.dumps(info, indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting project info: {e}", exc_info=True)
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
|
@system_mcp.tool()
|
||||||
|
async def check_all_projects_health() -> str:
|
||||||
|
"""Check health status of all projects with enhanced metrics."""
|
||||||
|
try:
|
||||||
|
health_data = await health_monitor.check_all_projects_health(include_metrics=True)
|
||||||
|
import json
|
||||||
|
|
||||||
|
return json.dumps(health_data, indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error checking health: {e}", exc_info=True)
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
|
@system_mcp.tool()
|
||||||
|
async def get_project_health(project_id: str) -> str:
|
||||||
|
"""Get detailed health information for a specific project."""
|
||||||
|
try:
|
||||||
|
status = await health_monitor.check_project_health(project_id, include_metrics=True)
|
||||||
|
import json
|
||||||
|
|
||||||
|
return json.dumps(status.to_dict(), indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting project health: {e}", exc_info=True)
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
|
@system_mcp.tool()
|
||||||
|
async def get_system_metrics() -> str:
|
||||||
|
"""Get overall MCP server metrics and statistics."""
|
||||||
|
try:
|
||||||
|
metrics = health_monitor.get_system_metrics()
|
||||||
|
import json
|
||||||
|
|
||||||
|
return json.dumps(metrics.to_dict(), indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting system metrics: {e}", exc_info=True)
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
|
@system_mcp.tool()
|
||||||
|
async def get_system_uptime() -> str:
|
||||||
|
"""Get MCP server uptime information."""
|
||||||
|
try:
|
||||||
|
uptime = health_monitor.get_uptime()
|
||||||
|
import json
|
||||||
|
|
||||||
|
return json.dumps(uptime, indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting uptime: {e}", exc_info=True)
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
|
@system_mcp.tool()
|
||||||
|
async def get_project_metrics(project_id: str, hours: int = 1) -> str:
|
||||||
|
"""Get historical metrics for a specific project."""
|
||||||
|
try:
|
||||||
|
hours = min(hours, 24)
|
||||||
|
metrics = health_monitor.get_project_metrics(project_id, hours=hours)
|
||||||
|
import json
|
||||||
|
|
||||||
|
return json.dumps(metrics, indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting project metrics: {e}", exc_info=True)
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
|
@system_mcp.tool()
|
||||||
|
async def export_health_metrics(output_path: str = "logs/metrics_export.json") -> str:
|
||||||
|
"""Export all health metrics to a JSON file."""
|
||||||
|
try:
|
||||||
|
exported_path = health_monitor.export_metrics(output_path=output_path, format="json")
|
||||||
|
return f"Metrics exported successfully to: {exported_path}"
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error exporting metrics: {e}", exc_info=True)
|
||||||
|
return f"Error: {str(e)}"
|
||||||
|
|
||||||
logger.info("Created System endpoint with 24 tools")
|
logger.info("Created System endpoint with 24 tools")
|
||||||
return system_mcp
|
return system_mcp
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user