diff --git a/DOCKER_README.md b/DOCKER_README.md new file mode 100644 index 0000000..6b8f4a5 --- /dev/null +++ b/DOCKER_README.md @@ -0,0 +1,124 @@ +# MCP Hub + +**The AI-native management hub for WordPress, WooCommerce, and self-hosted services.** + +589 tools across 9 plugins. Connect your sites, stores, repos, and databases — manage them all through Claude, ChatGPT, Cursor, or any MCP client. + +## Quick Start + +### 1. Create a `.env` file + +```bash +# Required +MASTER_API_KEY=your-secure-key-here + +# Add at least one WordPress site +WORDPRESS_SITE1_URL=https://your-site.com +WORDPRESS_SITE1_USERNAME=admin +WORDPRESS_SITE1_APP_PASSWORD=xxxx xxxx xxxx xxxx +WORDPRESS_SITE1_ALIAS=mysite +``` + +### 2. Run the container + +```bash +docker run -d \ + --name mcphub \ + -p 8000:8000 \ + --env-file .env \ + airano/mcphub:latest +``` + +### 3. Verify it works + +```bash +# Health check (wait ~30 seconds for startup) +curl http://localhost:8000/health + +# Open the web dashboard +# http://localhost:8000/dashboard +``` + +### 4. Connect your AI client + +In Claude Desktop's `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "mcphub": { + "url": "http://localhost:8000/mcp", + "headers": { + "Authorization": "Bearer your-secure-key-here" + } + } + } +} +``` + +## Using Docker Compose + +```yaml +services: + mcphub: + image: airano/mcphub:latest + ports: + - "8000:8000" + env_file: + - .env + volumes: + - mcphub-data:/app/data + - mcphub-logs:/app/logs + restart: unless-stopped + +volumes: + mcphub-data: + mcphub-logs: +``` + +```bash +docker compose up -d +``` + +## After Starting + +| URL | Description | +|-----|-------------| +| `http://localhost:8000/health` | Health check & status | +| `http://localhost:8000/dashboard` | Web dashboard (manage API keys, view sites, health) | +| `http://localhost:8000/mcp` | MCP endpoint (connect AI clients here) | + +## Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `MASTER_API_KEY` | **Yes** | API key for authentication | +| `WORDPRESS_SITE1_URL` | For WP | WordPress site URL | +| `WORDPRESS_SITE1_USERNAME` | For WP | WordPress admin username | +| `WORDPRESS_SITE1_APP_PASSWORD` | For WP | WordPress Application Password | +| `WORDPRESS_SITE1_ALIAS` | Recommended | Friendly name (e.g., `myblog`) | +| `OAUTH_JWT_SECRET_KEY` | For OAuth | JWT secret for ChatGPT/Claude auto-registration | +| `OAUTH_BASE_URL` | For OAuth | Public URL of your server | + +Add more sites with `SITE2`, `SITE3`, etc. See [full configuration guide](https://github.com/airano-ir/mcphub/blob/main/docs/getting-started.md). + +## Supported Plugins + +| Plugin | Tools | Env Prefix | +|--------|-------|------------| +| WordPress | 67 | `WORDPRESS_` | +| WooCommerce | 28 | `WOOCOMMERCE_` | +| WordPress Advanced | 22 | `WORDPRESS_` | +| Gitea | 56 | `GITEA_` | +| n8n | 56 | `N8N_` | +| Supabase | 70 | `SUPABASE_` | +| OpenPanel | 73 | `OPENPANEL_` | +| Appwrite | 100 | `APPWRITE_` | +| Directus | 100 | `DIRECTUS_` | + +## Links + +- **GitHub**: [github.com/airano-ir/mcphub](https://github.com/airano-ir/mcphub) +- **PyPI**: [pypi.org/project/mcphub-server](https://pypi.org/project/mcphub-server/) +- **Documentation**: [Getting Started Guide](https://github.com/airano-ir/mcphub/blob/main/docs/getting-started.md) +- **License**: MIT diff --git a/Dockerfile b/Dockerfile index b123ca3..f2854eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # =================================== -# Coolify Projects MCP Server - Dockerfile +# MCP Hub — Dockerfile # =================================== # Multi-stage build for optimized image size # Production-ready with security best practices diff --git a/README.md b/README.md index eb7aa28..019d891 100644 --- a/README.md +++ b/README.md @@ -72,14 +72,15 @@ MCP Hub is the first MCP server that lets you manage WordPress, WooCommerce, and git clone https://github.com/airano-ir/mcphub.git cd mcphub cp env.example .env -# Edit .env with your site credentials +# Edit .env — set MASTER_API_KEY and add your site credentials docker compose up -d ``` -### Option 2: PyPI +### Option 2: Docker Hub (No Clone) ```bash -pip install mcphub-server +# Create a .env file with your credentials (see "Configure Your Sites" below) +docker run -d --name mcphub -p 8000:8000 --env-file .env airano/mcphub:latest ``` ### Option 3: From Source @@ -93,6 +94,19 @@ cp env.example .env python server.py --transport sse --port 8000 ``` +### Verify It Works + +After starting the server, wait ~30 seconds then: + +```bash +# Check server health +curl http://localhost:8000/health +``` + +Open the **web dashboard** in your browser: **http://localhost:8000/dashboard** + +You should see the login page. Use your `MASTER_API_KEY` to log in. + ### Configure Your Sites Add site credentials to `.env`: diff --git a/docker-compose.yaml b/docker-compose.yaml index d4854b0..fe1dc9a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,17 +1,16 @@ # =================================== # MCP Hub — Docker Compose Configuration # =================================== -# -# Build Pack: Docker Compose -# ⚠️ CRITICAL RULES FOR COOLIFY: -# 1. NO host port mappings: Use "8000" NOT "8000:8000" -# 2. Listen on 0.0.0.0 (NOT localhost) -# 3. Health checks for ALL services -# 4. Use environment variables for ALL configs +# +# After starting: +# docker compose up -d +# curl http://localhost:8000/health # verify server is running +# open http://localhost:8000/dashboard # web dashboard +# +# For Coolify deployments: +# Change ports to "8000" (no host mapping) — Coolify handles routing. # =================================== -version: '3.8' - services: mcp-server: build: @@ -19,11 +18,9 @@ services: dockerfile: Dockerfile container_name: mcphub restart: unless-stopped - - # ⚠️ CRITICAL: Only container port (NO host port mapping) - # Coolify will handle routing via domain + ports: - - "8000" + - "8000:8000" # Environment variables environment: diff --git a/docs/getting-started.md b/docs/getting-started.md index 458f9ab..a6a0f89 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -48,14 +48,18 @@ For each WordPress site you want to manage: git clone https://github.com/airano-ir/mcphub.git cd mcphub cp env.example .env -# Edit .env with your site credentials +# Edit .env — set MASTER_API_KEY and add your site credentials (see Configuration below) docker compose up -d ``` -### Option 2: PyPI +After starting, see [Verify Installation](#verify-installation) below. + +### Option 2: Docker Hub (No Clone) ```bash -pip install mcphub-server +# 1. Create a .env file (see Configuration section below) +# 2. Run: +docker run -d --name mcphub -p 8000:8000 --env-file .env airano/mcphub:latest ``` ### Option 3: From Source @@ -190,22 +194,48 @@ python server.py --transport sse --port 8000 python server.py ``` -### Verify Server is Running +### Verify Installation -Check logs for: +After starting (via Docker or locally), wait ~30 seconds for the server to initialize, then: -``` -INFO: MCP Hub initialized -INFO: Registered 589 tools -INFO: Server ready -``` - -Or test the health endpoint: +**1. Check health:** ```bash curl http://localhost:8000/health +# Expected: {"status": "ok", "tools_loaded": 589, ...} ``` +**2. Open the web dashboard:** + +Open **http://localhost:8000/dashboard** in your browser. Log in with your `MASTER_API_KEY`. + +The dashboard lets you: +- View all connected sites and their health status +- Create and manage per-project API keys +- View audit logs +- Monitor rate limits + +**3. Check container status (Docker only):** + +```bash +docker compose ps +# Look for Status: "Up (healthy)" +# Note: Health check starts after 40 seconds — "starting" is normal initially + +# View logs if something is wrong: +docker compose logs -f mcphub +``` + +**4. Troubleshooting:** + +| Problem | Solution | +|---------|----------| +| Container exits immediately | Check logs: `docker compose logs mcphub` | +| Port 8000 already in use | Change port in docker-compose.yaml: `"8001:8000"` | +| Health check shows "unhealthy" | Wait 60 seconds, then check logs for startup errors | +| Dashboard login fails | Make sure you're using the `MASTER_API_KEY` value from your `.env` | +| Sites not showing up | Restart after adding new env vars: `docker compose restart` | + --- ## Connect Your AI Client @@ -334,34 +364,40 @@ Use specific endpoints to limit tool access: docker compose up -d ``` +After starting, verify the installation: + +```bash +curl http://localhost:8000/health # server health +open http://localhost:8000/dashboard # web dashboard +``` + +See [Verify Installation](#verify-installation) for detailed steps. + ### Docker Commands ```bash # View logs -docker compose logs -f +docker compose logs -f mcphub -# Check status +# Check status (look for "healthy") docker compose ps -# Restart +# Restart (needed after .env changes) docker compose restart # Stop docker compose down -# Rebuild +# Rebuild (after code changes) docker compose up --build -d ``` -### Health Check +### Adding Sites After Startup -```bash -# Check container health -docker compose ps - -# Test API endpoint -curl http://localhost:8000/health -``` +1. Edit your `.env` file to add new site credentials +2. Restart the container: `docker compose restart` +3. Verify: `curl http://localhost:8000/health` — check that tools are loaded +4. The dashboard at http://localhost:8000/dashboard will show the new sites --- diff --git a/server.py b/server.py index 2a7f65c..092828c 100644 --- a/server.py +++ b/server.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """ -Coolify Projects MCP Server +MCP Hub Server -Universal MCP server for managing Coolify projects through plugins. -Supports WordPress, Supabase, Gitea, and custom project types. +Universal MCP server for managing self-hosted services through plugins. +Supports WordPress, WooCommerce, Gitea, n8n, Supabase, OpenPanel, Appwrite, and Directus. Usage: # With stdio transport (Claude Desktop) @@ -228,7 +228,7 @@ if OAUTH_AUTH_MODE == "trusted_domains": logger.info(f"OAuth Trusted Domains: {', '.join(OAUTH_TRUSTED_DOMAINS)}") # Initialize MCP server -mcp = FastMCP("Coolify Projects Manager") +mcp = FastMCP("MCP Hub") # Initialize Jinja2 templates (Phase E - OAuth Authorization Page) templates = Jinja2Templates(directory="templates") @@ -261,7 +261,7 @@ tool_registry = get_tool_registry() tool_generator = ToolGenerator(site_manager) logger.info("=" * 60) -logger.info("Coolify Projects MCP Server - Option B Clean Architecture") +logger.info("MCP Hub Server - Initialized") logger.info("=" * 60) _mk = auth_manager.get_master_key() logger.info(f"Master API Key: {_mk[:8]}***{_mk[-4:]}") @@ -471,10 +471,11 @@ def extract_plugin_type_from_tool(tool_name: str) -> str | None: Returns: Plugin type string or None for system tools """ - # Remove MCP namespace prefix if present + # Remove MCP namespace prefix if present (e.g., "mcp__mcp-hub__wordpress_...") clean_name = tool_name - if tool_name.startswith("mcp__coolify-projects__"): - clean_name = tool_name.replace("mcp__coolify-projects__", "") + if tool_name.startswith("mcp__") and "__" in tool_name[5:]: + # Strip "mcp__{server-name}__" prefix + clean_name = tool_name.split("__", 2)[-1] # Check for plugin types (order matters - check more specific first) # wordpress_advanced must be checked before wordpress @@ -683,15 +684,15 @@ class UserAuthMiddleware(Middleware): else: # All plugin tools that have a 'site' parameter are unified tools # They defer project access check to execution time + # Clean any MCP namespace prefix before checking + check_name = tool_name + if tool_name.startswith("mcp__") and "__" in tool_name[5:]: + check_name = tool_name.split("__", 2)[-1] is_unified_tool = ( - tool_name.startswith("wordpress_") - or tool_name.startswith("wordpress_advanced_") - or tool_name.startswith("woocommerce_") - or tool_name.startswith("gitea_") - or tool_name.startswith("mcp__coolify-projects__wordpress_") - or tool_name.startswith("mcp__coolify-projects__wordpress_advanced_") - or tool_name.startswith("mcp__coolify-projects__woocommerce_") - or tool_name.startswith("mcp__coolify-projects__gitea_") + check_name.startswith("wordpress_") + or check_name.startswith("wordpress_advanced_") + or check_name.startswith("woocommerce_") + or check_name.startswith("gitea_") ) logger.debug( @@ -992,9 +993,11 @@ class RateLimitMiddleware(Middleware): tool_name = params.name if hasattr(params, "name") else "unknown" # Determine plugin type from tool name - if tool_name.startswith("wordpress_") or tool_name.startswith( - "mcp__coolify-projects__wordpress_" - ): + # Clean any MCP namespace prefix + tn = tool_name + if tool_name.startswith("mcp__") and "__" in tool_name[5:]: + tn = tool_name.split("__", 2)[-1] + if tn.startswith("wordpress_"): plugin_type = "wordpress" elif tool_name.startswith("woocommerce_"): plugin_type = "woocommerce" @@ -1540,6 +1543,12 @@ def create_dynamic_tool(name: str, description: str, handler, input_schema: dict ) ) + # Sort params: required (no default) first, then optional (with default). + # Python/inspect.Signature requires non-default args before default args. + required_first = [p for p in params if p.default is inspect.Parameter.empty] + optional_after = [p for p in params if p.default is not inspect.Parameter.empty] + params = required_first + optional_after + # Create signature sig = inspect.Signature(params) @@ -4313,7 +4322,7 @@ def main(): import uvicorn # Parse command line arguments for transport configuration - parser = argparse.ArgumentParser(description="Coolify Projects MCP Server") + parser = argparse.ArgumentParser(description="MCP Hub Server") parser.add_argument( "--transport", type=str,