fix(qa): resolve remaining v3.0.1 issues — CI, code bugs, docs
CI: Fix black formatting in core/site_registry.py Code: list_projects uses SiteManager, WP Advanced health uses REST API Docs: Fix transport types, endpoint guide, env var reference for all plugins Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
### 1. Create a `.env` file
|
||||
|
||||
```bash
|
||||
# Required
|
||||
# Recommended (auto-generates temp key if omitted — check container logs)
|
||||
MASTER_API_KEY=your-secure-key-here
|
||||
|
||||
# Add at least one WordPress site
|
||||
@@ -41,13 +41,14 @@ curl http://localhost:8000/health
|
||||
|
||||
### 4. Connect your AI client
|
||||
|
||||
In Claude Desktop's `claude_desktop_config.json`:
|
||||
**Claude Desktop** (`claude_desktop_config.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"mcphub": {
|
||||
"url": "http://localhost:8000/mcp",
|
||||
"type": "streamableHttp",
|
||||
"url": "http://localhost:8000/wordpress/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer your-secure-key-here"
|
||||
}
|
||||
@@ -56,15 +57,45 @@ In Claude Desktop's `claude_desktop_config.json`:
|
||||
}
|
||||
```
|
||||
|
||||
**VS Code** (`.vscode/mcp.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"mcphub": {
|
||||
"type": "http",
|
||||
"url": "http://localhost:8000/wordpress/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer your-secure-key-here"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Use a plugin-specific endpoint (e.g., `/wordpress/mcp`) instead of `/mcp` to reduce tool count and save tokens. See [Endpoints](#endpoints) below.
|
||||
|
||||
## Authentication
|
||||
|
||||
MCP Hub uses **Bearer token** authentication. Include the `Authorization` header in all requests:
|
||||
MCP Hub uses **Bearer token** authentication:
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_MASTER_API_KEY
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
```
|
||||
|
||||
This applies to both MCP clients and API calls. Query parameter auth is not supported.
|
||||
> `X-API-Key` header and query parameter auth are **not** supported.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Use the most specific endpoint for your use case:
|
||||
|
||||
| Endpoint | Tools | `site` param? | Best for |
|
||||
|----------|------:|:-------------:|----------|
|
||||
| `/project/{alias}/mcp` | 22-100 | No (pre-scoped) | Single-site workflow |
|
||||
| `/{plugin}/mcp` | 23-101 | Yes | Multi-site management |
|
||||
| `/mcp` | 596 | Yes | Admin & discovery only |
|
||||
|
||||
Available plugin endpoints: `/wordpress/mcp`, `/woocommerce/mcp`, `/wordpress-advanced/mcp`, `/gitea/mcp`, `/n8n/mcp`, `/supabase/mcp`, `/openpanel/mcp`, `/appwrite/mcp`, `/directus/mcp`, `/system/mcp`
|
||||
|
||||
## Using Docker Compose
|
||||
|
||||
|
||||
@@ -91,9 +91,7 @@ class ProjectManager:
|
||||
if config:
|
||||
self._create_project_instance(plugin_type, project_id, config)
|
||||
except Exception as e:
|
||||
self.logger.error(
|
||||
f"Failed to create {plugin_type} project '{project_id}': {e}", exc_info=True
|
||||
)
|
||||
self.logger.debug(f"Legacy ProjectManager: skipped {plugin_type}/{project_id}: {e}")
|
||||
|
||||
def _load_project_config(self, plugin_type: str, project_id: str) -> dict[str, Any] | None:
|
||||
"""
|
||||
|
||||
@@ -98,9 +98,7 @@ class SiteRegistry:
|
||||
for alias, full_ids in self.alias_conflicts.items():
|
||||
winner = self.aliases.get(alias)
|
||||
losers = [fid for fid in full_ids if fid != winner]
|
||||
self.logger.info(
|
||||
f" Alias '{alias}': {winner} (winner), {losers} (using full_id)"
|
||||
)
|
||||
self.logger.info(f" Alias '{alias}': {winner} (winner), {losers} (using full_id)")
|
||||
|
||||
# Reserved words that should NOT be interpreted as site IDs
|
||||
RESERVED_SITE_WORDS = {
|
||||
|
||||
@@ -173,49 +173,123 @@ RATE_LIMIT_PER_DAY=10000
|
||||
|
||||
### WordPress Plugin Requirements
|
||||
|
||||
Some WordPress tools require additional plugins on your WordPress site:
|
||||
Some tools require specific WordPress plugins or infrastructure:
|
||||
|
||||
| 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) |
|
||||
| Tools | Requirement |
|
||||
|-------|-------------|
|
||||
| `wordpress_get_post_seo`, `wordpress_update_post_seo`, `wordpress_get_product_seo`, `wordpress_update_product_seo` | **Yoast SEO** or **RankMath** |
|
||||
| `wordpress_wp_cache_*`, `wordpress_wp_db_*`, `wordpress_wp_plugin_*`, `wordpress_wp_theme_*`, `wordpress_wp_core_*`, `wordpress_wp_search_replace_dry_run` (15 tools) | Docker socket + `CONTAINER` env var |
|
||||
| `wordpress_advanced_*` database/system tools | Docker socket + `CONTAINER` env var |
|
||||
| `woocommerce_*` | **WooCommerce** plugin (separate `WOOCOMMERCE_` config) |
|
||||
|
||||
### Docker Socket for WP-CLI Tools
|
||||
### Docker Socket for WP-CLI / WordPress Advanced
|
||||
|
||||
WP-CLI tools (cache management, database export, plugin updates via CLI) require Docker socket access:
|
||||
WP-CLI and WordPress Advanced system tools 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
|
||||
services:
|
||||
mcphub:
|
||||
image: airano/mcphub:latest
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
WORDPRESS_SITE1_URL: https://your-site.com
|
||||
WORDPRESS_SITE1_USERNAME: admin
|
||||
WORDPRESS_SITE1_APP_PASSWORD: xxxx xxxx xxxx xxxx
|
||||
WORDPRESS_SITE1_CONTAINER: wordpress-container-name
|
||||
WORDPRESS_SITE1_ALIAS: mysite
|
||||
WORDPRESS_ADVANCED_SITE1_URL: https://your-site.com
|
||||
WORDPRESS_ADVANCED_SITE1_USERNAME: admin
|
||||
WORDPRESS_ADVANCED_SITE1_APP_PASSWORD: xxxx xxxx xxxx xxxx
|
||||
WORDPRESS_ADVANCED_SITE1_CONTAINER: wordpress-container-name
|
||||
WORDPRESS_ADVANCED_SITE1_ALIAS: mysite-admin
|
||||
```
|
||||
|
||||
Without Docker socket, WP-CLI tools will return a "not available" message but all REST API tools work normally.
|
||||
Without Docker socket:
|
||||
- WP-CLI tools return "not available" errors
|
||||
- WordPress Advanced database/system tools are unavailable
|
||||
- All REST API tools (bulk operations, content management) work normally
|
||||
|
||||
### Environment Variable Naming Convention
|
||||
### Environment Variable Reference
|
||||
|
||||
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` |
|
||||
All site configuration follows the pattern: `{PLUGIN_TYPE}_{SITE_ID}_{CONFIG_KEY}=value`
|
||||
|
||||
- `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`)
|
||||
- Multiple sites: change `SITE1` to `SITE2`, `SITE3`, etc.
|
||||
|
||||
#### WordPress
|
||||
```env
|
||||
WORDPRESS_SITE1_URL=https://your-site.com # Required
|
||||
WORDPRESS_SITE1_USERNAME=admin # Required
|
||||
WORDPRESS_SITE1_APP_PASSWORD=xxxx xxxx xxxx xxxx # Required (WordPress Application Password)
|
||||
WORDPRESS_SITE1_ALIAS=my-site # Recommended
|
||||
WORDPRESS_SITE1_CONTAINER=wp-container # Optional (for WP-CLI)
|
||||
```
|
||||
|
||||
#### WooCommerce
|
||||
```env
|
||||
WOOCOMMERCE_SITE1_URL=https://your-site.com # Required
|
||||
WOOCOMMERCE_SITE1_CONSUMER_KEY=ck_xxx # Required
|
||||
WOOCOMMERCE_SITE1_CONSUMER_SECRET=cs_xxx # Required
|
||||
WOOCOMMERCE_SITE1_ALIAS=my-shop # Recommended
|
||||
```
|
||||
|
||||
#### WordPress Advanced
|
||||
```env
|
||||
WORDPRESS_ADVANCED_SITE1_URL=https://your-site.com # Required
|
||||
WORDPRESS_ADVANCED_SITE1_USERNAME=admin # Required
|
||||
WORDPRESS_ADVANCED_SITE1_APP_PASSWORD=xxxx xxxx xxxx xxxx # Required
|
||||
WORDPRESS_ADVANCED_SITE1_CONTAINER=wp-container # Required (for WP-CLI tools)
|
||||
WORDPRESS_ADVANCED_SITE1_ALIAS=my-site-admin # Recommended
|
||||
```
|
||||
|
||||
#### Gitea
|
||||
```env
|
||||
GITEA_SITE1_URL=https://gitea.example.com # Required
|
||||
GITEA_SITE1_TOKEN=your-access-token # Required
|
||||
GITEA_SITE1_ALIAS=my-gitea # Recommended
|
||||
```
|
||||
|
||||
#### n8n
|
||||
```env
|
||||
N8N_SITE1_URL=https://n8n.example.com # Required
|
||||
N8N_SITE1_API_KEY=your-api-key # Required
|
||||
N8N_SITE1_ALIAS=my-n8n # Recommended
|
||||
```
|
||||
|
||||
#### Supabase
|
||||
```env
|
||||
SUPABASE_SITE1_URL=https://your-project.supabase.co # Required
|
||||
SUPABASE_SITE1_API_KEY=your-service-role-key # Required
|
||||
SUPABASE_SITE1_ALIAS=my-supabase # Recommended
|
||||
```
|
||||
|
||||
#### OpenPanel
|
||||
```env
|
||||
OPENPANEL_SITE1_URL=https://openpanel.example.com # Required
|
||||
OPENPANEL_SITE1_CLIENT_ID=your-client-id # Required
|
||||
OPENPANEL_SITE1_CLIENT_SECRET=your-client-secret # Required
|
||||
OPENPANEL_SITE1_ALIAS=my-analytics # Recommended
|
||||
```
|
||||
|
||||
#### Appwrite
|
||||
```env
|
||||
APPWRITE_SITE1_URL=https://appwrite.example.com # Required
|
||||
APPWRITE_SITE1_API_KEY=your-api-key # Required
|
||||
APPWRITE_SITE1_PROJECT_ID=your-project-id # Required
|
||||
APPWRITE_SITE1_ALIAS=my-appwrite # Recommended
|
||||
```
|
||||
|
||||
#### Directus
|
||||
```env
|
||||
DIRECTUS_SITE1_URL=https://directus.example.com # Required
|
||||
DIRECTUS_SITE1_TOKEN=your-admin-token # Required
|
||||
DIRECTUS_SITE1_ALIAS=my-directus # Recommended
|
||||
```
|
||||
|
||||
> **Note:** Use `APP_PASSWORD` (WordPress Application Password), not `PASSWORD`.
|
||||
|
||||
### Configuration Tips
|
||||
|
||||
@@ -286,7 +360,13 @@ docker compose logs -f mcphub
|
||||
|
||||
## 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.
|
||||
All requests require **Bearer token** authentication via the `Authorization` header:
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_API_KEY
|
||||
```
|
||||
|
||||
> `X-API-Key` header and query parameter auth are **not** supported.
|
||||
|
||||
### Claude Desktop
|
||||
|
||||
@@ -295,8 +375,9 @@ Add to `claude_desktop_config.json`:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"mcphub": {
|
||||
"url": "https://your-server:8000/mcp",
|
||||
"mcphub-wordpress": {
|
||||
"type": "streamableHttp",
|
||||
"url": "http://your-server:8000/wordpress/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer YOUR_MASTER_API_KEY"
|
||||
}
|
||||
@@ -312,9 +393,9 @@ Add to `.mcp.json` in your project:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"mcphub": {
|
||||
"type": "sse",
|
||||
"url": "https://your-server:8000/mcp",
|
||||
"mcphub-wordpress": {
|
||||
"type": "http",
|
||||
"url": "http://your-server:8000/wordpress/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer YOUR_MASTER_API_KEY"
|
||||
}
|
||||
@@ -327,8 +408,8 @@ Add to `.mcp.json` in your project:
|
||||
|
||||
Go to **Settings > MCP Servers > Add Server**:
|
||||
|
||||
- **Name**: MCP Hub
|
||||
- **URL**: `https://your-server:8000/mcp`
|
||||
- **Name**: MCP Hub WordPress
|
||||
- **URL**: `http://your-server:8000/wordpress/mcp`
|
||||
- **Headers**: `Authorization: Bearer YOUR_MASTER_API_KEY`
|
||||
|
||||
### VS Code + Copilot
|
||||
@@ -338,9 +419,9 @@ Add to `.vscode/mcp.json`:
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"mcphub": {
|
||||
"type": "sse",
|
||||
"url": "https://your-server:8000/mcp",
|
||||
"mcphub-wordpress": {
|
||||
"type": "http",
|
||||
"url": "http://your-server:8000/wordpress/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer YOUR_MASTER_API_KEY"
|
||||
}
|
||||
@@ -357,6 +438,8 @@ MCP Hub supports **Open Dynamic Client Registration** (RFC 7591). ChatGPT can au
|
||||
2. In ChatGPT, add MCP server: `https://your-server:8000/mcp`
|
||||
3. ChatGPT auto-discovers OAuth metadata and registers
|
||||
|
||||
> **Important**: Use `"type": "streamableHttp"` for Claude Desktop and `"type": "http"` for VS Code/Claude Code. Using `"type": "sse"` will cause `400 Bad Request` errors.
|
||||
|
||||
---
|
||||
|
||||
## Using MCP Tools
|
||||
@@ -391,31 +474,40 @@ The `site` parameter accepts either a **site_id** (e.g., `site1`) or an **alias*
|
||||
|
||||
### Multi-Endpoint Architecture
|
||||
|
||||
Use specific endpoints to limit tool access and save tokens:
|
||||
Use the most specific endpoint for your use case to minimize token usage:
|
||||
|
||||
```
|
||||
/mcp → All 596 tools (Master API Key)
|
||||
/system/mcp → System tools only (24 tools)
|
||||
/wordpress/mcp → WordPress tools (67 tools)
|
||||
/woocommerce/mcp → WooCommerce tools (28 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)
|
||||
```
|
||||
| Endpoint | Use Case | Tools | `site` param? |
|
||||
|----------|----------|------:|:-------------:|
|
||||
| `/project/{alias}/mcp` | Single-site workflow | 22-100 | No (pre-scoped) |
|
||||
| `/{plugin}/mcp` | Multi-site management | 23-101 | Yes |
|
||||
| `/system/mcp` | System administration | 24 | N/A |
|
||||
| `/mcp` | Admin & discovery only | 596 | Yes |
|
||||
|
||||
> **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.
|
||||
> **Recommendation**: Always use the most specific endpoint. Using `/mcp` (596 tools) wastes context tokens and degrades AI response quality.
|
||||
|
||||
**Available plugin endpoints:**
|
||||
|
||||
| Endpoint | Plugin | Tools |
|
||||
|----------|--------|------:|
|
||||
| `/wordpress/mcp` | WordPress | 67 |
|
||||
| `/woocommerce/mcp` | WooCommerce | 28 |
|
||||
| `/wordpress-advanced/mcp` | WordPress Advanced | 22 |
|
||||
| `/gitea/mcp` | Gitea | 56 |
|
||||
| `/n8n/mcp` | n8n | 56 |
|
||||
| `/supabase/mcp` | Supabase | 70 |
|
||||
| `/openpanel/mcp` | OpenPanel | 73 |
|
||||
| `/appwrite/mcp` | Appwrite | 100 |
|
||||
| `/directus/mcp` | Directus | 100 |
|
||||
| `/system/mcp` | System Management | 24 |
|
||||
|
||||
**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 |
|
||||
| Feature | Plugin (`/wordpress/mcp`) | Project (`/project/{alias}/mcp`) |
|
||||
|---------|:------------------------:|:-------------------------------:|
|
||||
| `list_sites` tool | Yes | No |
|
||||
| `site` parameter needed | Yes | No (pre-scoped) |
|
||||
| Tool count | N + 1 (includes `list_sites`) | N |
|
||||
| Multi-site support | Yes | No (single site) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -112,22 +112,24 @@ class WordPressAdvancedPlugin(BasePlugin):
|
||||
Dict with health status and WP-CLI availability
|
||||
"""
|
||||
try:
|
||||
# Test WP-CLI access (primary requirement for wordpress_advanced)
|
||||
wp_cli_version = await self.system.wp_cli_version()
|
||||
wp_cli_available = bool(wp_cli_version.get("version"))
|
||||
|
||||
# Test REST API access with a public endpoint
|
||||
# Test REST API access (primary health indicator — most tools use REST)
|
||||
rest_api_available = False
|
||||
try:
|
||||
# Use a public endpoint that doesn't require authentication
|
||||
site_info = await self.client.get("/")
|
||||
rest_api_available = bool(site_info.get("name"))
|
||||
except Exception as e:
|
||||
self.logger.warning(f"REST API check failed (non-critical): {e}")
|
||||
rest_api_available = False
|
||||
self.logger.warning(f"REST API check failed: {e}")
|
||||
|
||||
# Test WP-CLI access (optional — only needed for database/system tools)
|
||||
wp_cli_available = False
|
||||
try:
|
||||
wp_cli_version = await self.system.wp_cli_version()
|
||||
wp_cli_available = bool(wp_cli_version.get("version"))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
"healthy": wp_cli_available or rest_api_available,
|
||||
"healthy": rest_api_available,
|
||||
"wp_cli_available": wp_cli_available,
|
||||
"rest_api_available": rest_api_available,
|
||||
"features": {
|
||||
|
||||
68
server.py
68
server.py
@@ -1180,25 +1180,29 @@ def add_endpoint_middleware(endpoint_mcp, endpoint_name: str = "unknown"):
|
||||
|
||||
# Internal implementation functions (not decorated) for system tools
|
||||
async def _list_projects_impl() -> str:
|
||||
"""Internal implementation for listing projects."""
|
||||
try:
|
||||
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}
|
||||
"""Internal implementation for listing projects (reads from SiteManager)."""
|
||||
import json
|
||||
|
||||
try:
|
||||
all_sites = site_manager.list_all_sites()
|
||||
projects = []
|
||||
|
||||
for site_info in all_sites:
|
||||
alias = site_info.get("alias")
|
||||
site_id = site_info.get("site_id")
|
||||
full_id = site_info.get("full_id", "")
|
||||
path_suffix = alias if alias and alias != site_id else full_id
|
||||
projects.append(
|
||||
{
|
||||
"id": full_id,
|
||||
"type": site_info.get("plugin_type", ""),
|
||||
"project_id": site_id,
|
||||
"alias": alias,
|
||||
"endpoint": f"/project/{path_suffix}/mcp",
|
||||
}
|
||||
)
|
||||
|
||||
result = {"total": len(projects), "projects": projects}
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
logger.error(f"Error listing projects: {e}", exc_info=True)
|
||||
@@ -1269,14 +1273,22 @@ async def get_project_info(project_id: str) -> str:
|
||||
Returns:
|
||||
JSON string with project information
|
||||
"""
|
||||
import json
|
||||
|
||||
try:
|
||||
# Try legacy ProjectManager first
|
||||
info = project_manager.get_project_info(project_id)
|
||||
|
||||
# Fallback to SiteManager if legacy finds nothing
|
||||
if info is None:
|
||||
for site_data in site_manager.list_all_sites():
|
||||
if site_data["full_id"] == project_id:
|
||||
info = site_data
|
||||
break
|
||||
|
||||
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)
|
||||
@@ -3111,7 +3123,6 @@ async def health_check(request: Request) -> JSONResponse:
|
||||
{
|
||||
"status": "healthy",
|
||||
"uptime": uptime_seconds,
|
||||
"projects": len(project_manager.projects),
|
||||
"sites": site_manager.get_count(),
|
||||
"tools": _total_tool_count, # Total tools (plugin + system)
|
||||
"timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
|
||||
@@ -3480,12 +3491,17 @@ Use get_endpoints() to see all available MCP endpoints."""
|
||||
@system_mcp.tool()
|
||||
async def get_project_info(project_id: str) -> str:
|
||||
"""Get detailed information about a specific project."""
|
||||
import json
|
||||
|
||||
try:
|
||||
info = project_manager.get_project_info(project_id)
|
||||
if info is None:
|
||||
for site_data in site_manager.list_all_sites():
|
||||
if site_data["full_id"] == project_id:
|
||||
info = site_data
|
||||
break
|
||||
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)
|
||||
@@ -4507,10 +4523,10 @@ def main():
|
||||
logger.info(f"Host: {args.host}")
|
||||
logger.info(f"Port: {args.port}")
|
||||
|
||||
# Check if any projects were discovered
|
||||
if len(project_manager.projects) == 0:
|
||||
logger.warning("No projects discovered! Check your environment variables.")
|
||||
logger.warning("Expected format: {PLUGIN_TYPE}_{PROJECT_ID}_{CONFIG_KEY}=value")
|
||||
# Check if any sites were discovered (SiteManager is the primary discovery system)
|
||||
if site_manager.get_count() == 0:
|
||||
logger.warning("No sites discovered! Check your environment variables.")
|
||||
logger.warning("Expected format: {PLUGIN_TYPE}_{SITE_ID}_{CONFIG_KEY}=value")
|
||||
logger.warning("Example: WORDPRESS_SITE1_URL=https://example.com")
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user