OpenPanel product analytics plugin fully reviewed, tested, and published. Works with both self-hosted and cloud (openpanel.dev) instances. - 42 tools: event tracking, data export, analytics, project/client management - All tools use public REST APIs (Track, Export, Insights, Manage) - Client modes: write (tracking), read (analytics), root (full access) - Service page with description, setup notes, WordPress plugin download - Dynamic URL hints in Add Site form - 62 unit tests - ENABLED_PLUGINS default now includes openpanel Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
MCP Hub — a Python MCP (Model Context Protocol) server that manages multiple self-hosted services through a unified plugin architecture. Supports 9 plugin types (WordPress, WooCommerce, WordPress Advanced, Gitea, n8n, Supabase, OpenPanel, Appwrite, Directus) with 565 tools total. The tool count stays constant regardless of how many sites are configured.
Quick Setup
cp env.example .env # Copy and fill in credentials
pip install -e ".[dev]" # Install with dev deps
python server.py # Run (stdio) or:
python server.py --transport streamable-http --port 8000 # Run (HTTP)
Build & Development Commands
# Install with dev dependencies
pip install -e ".[dev]"
# Run server (stdio transport for Claude Desktop)
python server.py
# Run server (HTTP transport for testing)
python server.py --transport streamable-http --port 8000
# Run all tests
pytest
# Run single test file
pytest tests/test_api_keys.py
# Run by marker (unit, integration, security, slow)
pytest -m unit
pytest -m "not slow"
# Tests with coverage
pytest --cov --cov-report=html
# Format code
black .
# Lint
ruff check .
ruff check --fix .
# Type check
mypy .
# Docker build and run
docker build -t mcphub .
docker-compose up -d
Code Quality Configuration
All configured in pyproject.toml:
- Black: line-length=100, target py311
- Ruff: strict rules (E, W, F, I, N, D, UP, B, C4, SIM, TCH, PTH), Google-style docstrings
- mypy: Python 3.11, strict equality, check_untyped_defs=true
- pytest: asyncio_mode="auto", testpaths=["tests"], markers: slow, integration, unit, security
Architecture
Root Directory Overview
├── server.py # Primary entry point
├── server_multi.py # Alternative multi-endpoint server
├── core/ # Layer 1: Core system modules
├── plugins/ # Layer 2: Plugin system (9 plugins)
├── core/templates/ # Jinja2 templates (dashboard + OAuth)
├── tests/ # Organized test suite
├── scripts/ # Setup & deployment scripts
├── wordpress-plugin/ # Companion WP plugins (PHP)
├── docs/ # Extensive documentation
├── pyproject.toml # All tool configs (black, ruff, mypy, pytest)
├── docker-compose.yaml # Docker composition
└── env.example # Environment variable template
Three-Layer Clean Architecture ("Option B")
Layer 1: Core System (core/) — Auth, site discovery, tool registry, health, rate limiting
Layer 2: Plugin System (plugins/) — 9 plugin types, each with handlers + schemas
Layer 3: API & Web UI (server.py + core/dashboard/) — FastMCP server, Starlette routes, dashboard
Entry Points
server.py(~3500 lines) — Primary entry point. Handles FastMCP server, Starlette routes, middleware, plugins.server_multi.py— Alternative multi-endpoint server (legacy, predates unifiedserver.pyendpoints).
Multi-Endpoint Architecture
/mcp → Admin (all tools, Master API Key required)
/system/mcp → System tools only
/{plugin_type}/mcp → Plugin-specific tools (wordpress, gitea, n8n, etc.)
/project/{alias_or_id}/mcp → Per-project endpoint (auto-injects site parameter)
Implemented in core/endpoints/ — EndpointConfig, MCPEndpointFactory, EndpointRegistry.
Plugin System
All plugins extend BasePlugin (in plugins/base.py). Registration happens in plugins/__init__.py via PluginRegistry.
Each plugin follows this structure:
plugins/{name}/
├── plugin.py # Main class extending BasePlugin
├── client.py # REST API client for the service
├── handlers/ # Feature-specific handlers (posts.py, orders.py, etc.)
└── schemas/ # Pydantic models for validation
Registered plugins: wordpress, woocommerce, wordpress_advanced, gitea, n8n, supabase, openpanel, appwrite, directus
Plugin visibility (Track F.1): Public users only see plugins listed in ENABLED_PLUGINS env var (default: wordpress,woocommerce,supabase). Admin sees all. Controlled by core/plugin_visibility.py.
Tool Generation
Tools are dynamically generated at startup:
SiteManagerdiscovers sites from env vars ({PLUGIN_TYPE}_{SITE_ID}_{CONFIG_KEY})ToolGeneratorcreates unified tools with asiteparameter injected- Tools are registered in
ToolRegistryand exposed via FastMCP
Unified tool pattern: wordpress_create_post(site="myblog", title="Hello") — the site parameter accepts either a site_id or alias.
Site Configuration via Environment Variables
Pattern: {PLUGIN_TYPE}_{SITE_ID}_{CONFIG_KEY}
WORDPRESS_SITE1_URL=https://example.com
WORDPRESS_SITE1_USERNAME=admin
WORDPRESS_SITE1_APP_PASSWORD=xxxx xxxx xxxx xxxx
WORDPRESS_SITE1_ALIAS=myblog # optional friendly name
WORDPRESS_SITE1_CONTAINER=wp-docker # optional, for WP-CLI
Parsed by core/site_manager.py into SiteConfig (Pydantic model). Sites are auto-discovered on startup.
Key Core Modules
| Module | Purpose |
|---|---|
core/auth.py |
Master API key validation, request authentication |
core/api_keys.py |
Per-project API keys with scopes (read/write/admin) |
core/site_manager.py |
Type-safe site config discovery from env vars |
core/tool_registry.py |
Central tool definitions and lookup |
core/tool_generator.py |
Dynamic unified tool creation with site injection |
core/health.py |
Health monitoring, metrics, alerts |
core/rate_limiter.py |
Token bucket rate limiting (60/min, 1000/hr, 10k/day) |
core/audit_log.py |
GDPR-compliant JSON audit logging |
core/oauth/ |
OAuth 2.1 with PKCE (RFC 8414, 7591, 7636) |
core/dashboard/routes.py |
Web UI dashboard (login, projects, API keys, health, audit) |
core/endpoints/ |
Multi-endpoint architecture (factory, registry, config) |
core/plugin_visibility.py |
Plugin enable/disable for public vs admin users |
core/user_auth.py |
OAuth Social Login (GitHub + Google) |
core/user_endpoints.py |
Per-user MCP endpoints (/u/{user_id}/{alias}/mcp) |
core/site_api.py |
User site CRUD, connection testing, credential encryption |
core/user_keys.py |
User API key management (bcrypt, mhu_ prefix) |
core/database.py |
SQLite backend (aiosqlite, WAL mode, migrations) |
core/encryption.py |
AES-256-GCM credential encryption |
User System (Track E)
OAuth Social Login (GitHub + Google) via core/user_auth.py. Users register, add sites, get personal MCP endpoints at /u/{user_id}/{alias}/mcp. Per-user API keys with mhu_ prefix (bcrypt-hashed). Credentials encrypted with AES-256-GCM in SQLite.
Dashboard
Web UI at the server root, built with Starlette + Jinja2 + HTMX + Tailwind CSS. Supports EN/FA i18n (core/i18n.py).
Admin pages: Login, Home, Projects, API Keys, OAuth Clients, Audit Logs, Health, Settings. User pages: Login (OAuth), Home, My Sites (list/add/edit/test/delete), Connect (API keys + config snippets + OAuth clients), Profile.
Legacy Modules (Deprecated)
core/project_manager.py, core/site_registry.py, core/unified_tools.py — kept for backward compatibility. New code should use SiteManager, ToolRegistry, and ToolGenerator instead.
Commit Style
<type>(<scope>): <description>
Types: feat, fix, docs, style, refactor, test, chore
Live Instances
- Platform:
mcp.example.com— Live MCP Hub with OAuth login - Blog:
blog.example.com— WordPress test site + project blog - Deployment: Coolify / Docker Compose, port 8000
Current Development (Track F)
v4 development cycle with 15 phases. See docs/ROADMAP.md (Track F) and .claude/plans/structured-popping-adleman.md for full plan.
Active plugins for public users: WordPress, WooCommerce, Supabase, OpenPanel (configurable via ENABLED_PLUGINS env var)
Gotchas
- Test files exist in both
tests/(proper) and root directory (legacytest_*.py). Runpytest tests/for organized tests only. server_multi.pyis the alternative multi-endpoint entry point;server.pyis the primarywordpress-plugin/contains companion WP plugins (openpanel, airano-mcp-seo-bridge) — these are PHP, not Pythonenv.examplehas "FUTURE" labels for Supabase/Gitea but both are fully implemented- 4 plugins are tested for public use: WordPress, WooCommerce, Supabase, OpenPanel. Others are admin-only or disabled.
- OAuth Clients (Client ID/Secret) are for MCP endpoint auth, NOT the same as GitHub/Google dashboard login
- User sites stored in SQLite (
core/database.py), admin sites still from env vars - Dashboard templates live in
core/templates/(included in pip package aspackage_data) ruffconfig uses top-levelselectkey in pyproject.toml (not[tool.ruff.lint]nested format)- The
scripts/directory has platform-specific setup scripts:setup.sh(Linux/Mac),setup.ps1(Windows)
Deployment Notes
- Coolify: Docker Compose build pack, port 8000, health check
GET /health - Must listen on
0.0.0.0(not localhost) - Docker socket mount required for WP-CLI tools:
/var/run/docker.sock:/var/run/docker.sock:ro - Persistent volumes:
mcp-data(API keys, OAuth),mcp-logs(audit, health) - Required env vars:
MASTER_API_KEY,OAUTH_JWT_SECRET_KEY,OAUTH_BASE_URL - OAuth env vars:
GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET,GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,PUBLIC_URL - User limits:
MAX_SITES_PER_USER=10,USER_RATE_LIMIT_PER_MIN=30,USER_RATE_LIMIT_PER_HR=500 - Plugin visibility:
ENABLED_PLUGINS=wordpress,woocommerce,supabase,openpanel(default)