# 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 8 active plugin types (WordPress, WooCommerce, WordPress Specialist, Gitea, n8n, Supabase, OpenPanel, Coolify). Total tool count varies with which plugins are enabled and grows as new plugins are added; per-plugin counts are surfaced in the dashboard. The exposed count for a given user/endpoint stays constant regardless of how many sites of the same type are configured.

## Quick Setup

```bash
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

```bash
# 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
```

## Frontend (web/) — Track G

The redesigned dashboard SPA lives in `web/` (Vite + React 18 + TypeScript).
Build artifacts land in `core/templates/static/dist/` and are served by
Starlette under `/static/dist/` and routed at `/dashboard-v2/*`. Legacy Jinja
dashboard at `/dashboard/*` is unchanged during the coexistence window.

```bash
# Install JS deps (one-time)
cd web && npm install

# Dev server (proxies /api, /auth, /dashboard, /static to localhost:8000)
cd web && npm run dev

# Type-check and run unit tests
cd web && npm run typecheck
cd web && npm test

# Production build (writes hashed bundles into core/templates/static/dist)
cd web && npm run build

# SPA-support backend tests
pytest -m frontend
```

Logo: `core/templates/static/logo.svg` is the single source of truth and is
copied verbatim into `web/public/` during build. The React `Logo` component
inlines the same path data and themes its fills via CSS vars.

## 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, frontend

## Architecture

### Root Directory Overview

```
├── server.py              # Primary entry point
├── core/                  # Layer 1: Core system modules
├── plugins/               # Layer 2: Plugin system (10 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/)   — 10 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.
### 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_specialist, gitea, n8n, supabase, openpanel, coolify

**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 from plugin specifications:
1. `ToolGenerator` creates unified tools with a `site` parameter injected
2. Tools are registered in `ToolRegistry` and 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

Sites are managed via the web dashboard and stored in SQLite (DB-based).
User sites are encrypted with AES-256-GCM and accessed via `core/site_api.py` + `core/database.py`.
`SiteManager` (`core/site_manager.py`) provides registration and lookup infrastructure for tool generation.

### 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 registration and lookup |
| `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.

## 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, Gitea (configurable via `ENABLED_PLUGINS` env var)

## Gotchas

- Test files exist in both `tests/` (proper) and root directory (legacy `test_*.py`). Run `pytest tests/` for organized tests only.
- `wordpress-plugin/` contains companion WP plugins (openpanel, airano-mcp-bridge) — these are PHP, not Python
- `env.example` has "FUTURE" labels for Supabase/Gitea but both are fully implemented
- 8 active plugins: WordPress, WooCommerce, WordPress Specialist, Supabase, OpenPanel, Gitea, n8n, Coolify. Appwrite/Directus code is retained in plugins/ but not registered (need review before re-enabling).
- OAuth Clients (Client ID/Secret) are for MCP endpoint auth, NOT the same as GitHub/Google dashboard login
- All sites stored in SQLite (`core/database.py`), managed via web dashboard
- Dashboard templates live in `core/templates/` (included in pip package as `package_data`)
- `ruff` config uses top-level `select` key 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,gitea` (default)
