diff --git a/.env.example b/.env.example index bed470343..f17cddd4c 100644 --- a/.env.example +++ b/.env.example @@ -13,7 +13,10 @@ HOST=0.0.0.0 # Port number for the HTTP server PORT=4444 -# Runtime environment (development/production) - affects CORS, cookies, and security defaults +# Runtime environment - affects CORS, cookies, and security defaults +# Options: development, production +# - development: Relaxed CORS (localhost:3000/8080), debug info, insecure cookies +# - production: Strict CORS (APP_DOMAIN only), secure cookies, no debug info ENVIRONMENT=development # Domain name for CORS origins and cookie settings (use your actual domain in production) @@ -24,7 +27,10 @@ APP_DOMAIN=localhost # See FastAPI docs: https://fastapi.tiangolo.com/advanced/behind-a-proxy/ APP_ROOT_PATH= -# Enable basic auth for docs endpoints +# Enable HTTP Basic Auth for OpenAPI docs endpoints (/docs, /redoc) +# Options: true, false (default: false) +# When true: Allows accessing docs with BASIC_AUTH_USER/BASIC_AUTH_PASSWORD +# When false: Only JWT Bearer token authentication is accepted DOCS_ALLOW_BASIC_AUTH=false # Database Configuration @@ -59,11 +65,29 @@ DB_MAX_RETRIES=5 # Retry interval in milliseconds (default: 2000) DB_RETRY_INTERVAL_MS=2000 -# Cache Configuration +# Cache Backend Configuration +# Options: database (default), memory (in-process), redis (distributed) +# - database: Uses SQLite/PostgreSQL for persistence (good for single-node) +# - memory: Fast in-process caching (lost on restart, not shared between workers) +# - redis: Distributed caching for multi-node deployments CACHE_TYPE=database -# CACHE_TYPE=redis + +# Redis connection URL (only used when CACHE_TYPE=redis) +# Format: redis://[username:password@]host:port/database +# Example: redis://localhost:6379/0 (local), redis://redis:6379/0 (container) # REDIS_URL=redis://localhost:6379/0 +# Cache key prefix for Redis (used to namespace keys in shared Redis instances) +# Default: "mcpgw:" +CACHE_PREFIX=mcpgw: + +# Session time-to-live in seconds (how long sessions remain valid) +# Default: 3600 (1 hour) +SESSION_TTL=3600 + +# Message time-to-live in seconds (how long messages are retained) +# Default: 600 (10 minutes) +MESSAGE_TTL=600 # Maximum number of times to boot redis connection for cold start REDIS_MAX_RETRIES=3 @@ -82,13 +106,19 @@ PROTOCOL_VERSION=2025-03-26 # Authentication ##################################### -# Admin UI basic-auth credentials +# Admin UI HTTP Basic Auth credentials +# Used for: Admin UI login, /docs endpoint (if DOCS_ALLOW_BASIC_AUTH=true) # PRODUCTION: Change these to strong, unique values! -# Authentication Configuration BASIC_AUTH_USER=admin BASIC_AUTH_PASSWORD=changeme + +# Global authentication requirement +# Options: true (default), false +# When true: All endpoints require authentication (Basic or JWT) +# When false: Endpoints are publicly accessible (NOT RECOMMENDED) AUTH_REQUIRED=true -# Content type for outgoing requests to Forge +# Content type for outgoing HTTP requests to upstream services +# Options: application/json (default), application/x-www-form-urlencoded, multipart/form-data FORGE_CONTENT_TYPE=application/json # JWT Algorithm Selection @@ -184,9 +214,13 @@ OAUTH_MAX_RETRIES=3 # ============================================================================== # Master SSO switch - enable Single Sign-On authentication +# Options: true, false (default) +# When true: Enables SSO login options alongside local auth SSO_ENABLED=false # GitHub OAuth Configuration +# Options: true, false (default) +# Requires: GitHub OAuth App (Settings > Developer settings > OAuth Apps) SSO_GITHUB_ENABLED=false # SSO_GITHUB_CLIENT_ID=your-github-client-id # SSO_GITHUB_CLIENT_SECRET=your-github-client-secret @@ -247,12 +281,19 @@ REQUIRE_EMAIL_VERIFICATION_FOR_INVITES=true # Admin UI and API Toggles ##################################### -# Enable the visual Admin UI (true/false) -# PRODUCTION: Set to false for security - -# UI/Admin Feature Flags +# Enable the web-based Admin UI at /admin +# Options: true (default), false +# PRODUCTION: Set to false for security unless needed MCPGATEWAY_UI_ENABLED=true + +# Enable Admin REST API endpoints (/tools, /servers, /resources, etc.) +# Options: true (default), false +# Required for: Admin UI functionality, programmatic management MCPGATEWAY_ADMIN_API_ENABLED=true + +# Enable bulk import feature for mass tool/resource registration +# Options: true (default), false +# Allows importing multiple tools/resources in a single API call MCPGATEWAY_BULK_IMPORT_ENABLED=true # Maximum number of tools allowed per bulk import request @@ -300,15 +341,22 @@ MCPGATEWAY_A2A_METRICS_ENABLED=true # Security and CORS ##################################### -# Skip TLS certificate checks for upstream requests (not recommended in prod) +# Skip SSL/TLS certificate verification for upstream requests +# Options: true, false (default) +# WARNING: Only use in development or with self-signed certificates! +# PRODUCTION: Must be false for security SKIP_SSL_VERIFY=false -# CORS origin allowlist (use JSON array of URLs) -# Example: ["http://localhost:3000"] -# Do not quote this value. Start with [] to ensure it's valid JSON. +# CORS allowed origins (JSON array of URLs) +# Controls which domains can make cross-origin requests to the gateway +# Format: JSON array starting with [ and ending with ] +# Example: ["http://localhost:3000", "https://app.example.com"] +# Use ["*"] to allow all origins (NOT RECOMMENDED) ALLOWED_ORIGINS='["http://localhost", "http://localhost:4444"]' -# Enable CORS handling in the gateway +# Enable CORS (Cross-Origin Resource Sharing) handling +# Options: true (default), false +# Required for: Web browser clients, cross-domain API access CORS_ENABLED=true # CORS allow credentials (true/false) @@ -382,12 +430,27 @@ RETRY_JITTER_MAX=0.5 # Logging ##################################### -# Logging verbosity level: DEBUG, INFO, WARNING, ERROR, CRITICAL - -# Logging Configuration +# Logging verbosity level +# Options: DEBUG, INFO (default), WARNING, ERROR, CRITICAL +# DEBUG: Detailed diagnostic info (verbose) +# INFO: General operational messages +# WARNING: Warning messages for potential issues +# ERROR: Error messages for failures +# CRITICAL: Only critical failures LOG_LEVEL=INFO + +# Log output format +# Options: json (default), text +# json: Structured JSON logs (good for log aggregation) +# text: Human-readable plain text LOG_FORMAT=json + +# Enable file logging (in addition to console output) +# Options: true, false (default) LOG_TO_FILE=false + +# File write mode when LOG_TO_FILE=true +# Options: a+ (append, default), w (overwrite on startup) LOG_FILEMODE=a+ LOG_FILE=mcpgateway.log LOG_FOLDER=logs @@ -396,22 +459,59 @@ LOG_MAX_SIZE_MB=1 LOG_BACKUP_COUNT=5 LOG_BUFFER_SIZE_MB=1.0 -# Transport Configuration +# Transport Protocol Configuration +# Options: all (default), sse, streamablehttp, http +# - all: Enable all transport protocols +# - sse: Server-Sent Events only +# - streamablehttp: Streaming HTTP only +# - http: Standard HTTP JSON-RPC only TRANSPORT_TYPE=all + +# WebSocket keepalive ping interval in seconds +# Prevents connection timeout for idle WebSocket connections WEBSOCKET_PING_INTERVAL=30 + +# SSE client retry timeout in milliseconds +# Time client waits before reconnecting after SSE connection loss SSE_RETRY_TIMEOUT=5000 + +# Enable SSE keepalive events to prevent proxy/firewall timeouts +# Options: true (default), false SSE_KEEPALIVE_ENABLED=true + +# SSE keepalive event interval in seconds +# How often to send keepalive events when SSE_KEEPALIVE_ENABLED=true SSE_KEEPALIVE_INTERVAL=30 # Streaming HTTP Configuration +# Enable stateful sessions (stores session state server-side) +# Options: true, false (default) +# false: Stateless mode (better for scaling) USE_STATEFUL_SESSIONS=false + +# Enable JSON response format for streaming HTTP +# Options: true (default), false +# true: Return JSON responses, false: Return SSE stream JSON_RESPONSE_ENABLED=true # Federation Configuration +# Enable gateway federation (connect to other MCP gateways) +# Options: true (default), false FEDERATION_ENABLED=true + +# Enable automatic peer discovery via mDNS/Zeroconf +# Options: true, false (default) +# Requires: python-zeroconf package FEDERATION_DISCOVERY=false + +# Static list of peer gateway URLs (JSON array) +# Example: ["http://gateway1:4444", "https://gateway2.example.com"] FEDERATION_PEERS=[] + +# Timeout for federation requests in seconds FEDERATION_TIMEOUT=30 + +# Interval between federation sync operations in seconds FEDERATION_SYNC_INTERVAL=300 # Resource Configuration @@ -419,6 +519,13 @@ RESOURCE_CACHE_SIZE=1000 RESOURCE_CACHE_TTL=3600 MAX_RESOURCE_SIZE=10485760 +# Allowed MIME types for resources (JSON array) +# Controls which content types are allowed for resource handling +# Default includes common text, image, and data formats +# Example: ["text/plain", "text/markdown", "application/json", "image/png"] +# To add custom types: ["text/plain", "application/pdf", "video/mp4"] +# ALLOWED_MIME_TYPES=["text/plain", "text/markdown", "text/html", "application/json", "application/xml", "image/png", "image/jpeg", "image/gif"] + # Tool Configuration TOOL_TIMEOUT=60 MAX_TOOL_RETRIES=3 @@ -437,11 +544,44 @@ HEALTH_CHECK_TIMEOUT=15 UNHEALTHY_THRESHOLD=5 GATEWAY_VALIDATION_TIMEOUT=10 -# OpenTelemetry Configuration +# File lock name for gateway service leader election +# Used to coordinate multiple gateway instances when running in cluster mode +# Default: "gateway_service_leader.lock" +FILELOCK_NAME=gateway_service_leader.lock + +# Default root paths (JSON array) +# List of default root paths for resource resolution +# Example: ["/api/v1", "/mcp"] +# Default: [] +DEFAULT_ROOTS=[] + +# OpenTelemetry Observability Configuration +# Enable distributed tracing and metrics collection +# Options: true (default), false OTEL_ENABLE_OBSERVABILITY=true + +# Traces exporter backend +# Options: otlp (default), jaeger, zipkin, console, none +# - otlp: OpenTelemetry Protocol (works with many backends) +# - jaeger: Direct Jaeger integration +# - zipkin: Direct Zipkin integration +# - console: Print to stdout (debugging) +# - none: Disable tracing OTEL_TRACES_EXPORTER=otlp + +# OTLP endpoint for traces and metrics +# Examples: +# - Phoenix: http://localhost:4317 +# - Jaeger: http://localhost:4317 +# - Tempo: http://localhost:4317 OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 + +# OTLP protocol +# Options: grpc (default), http OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +# Use insecure connection (no TLS) for OTLP +# Options: true (default for localhost), false (use TLS) OTEL_EXPORTER_OTLP_INSECURE=true # OTEL_EXPORTER_OTLP_HEADERS=key1=value1,key2=value2 # OTEL_EXPORTER_JAEGER_ENDPOINT=http://localhost:14268/api/traces @@ -452,8 +592,15 @@ OTEL_BSP_MAX_QUEUE_SIZE=2048 OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512 OTEL_BSP_SCHEDULE_DELAY=5000 -# Plugin Configuration +# Plugin Framework Configuration +# Enable the plugin system for extending gateway functionality +# Options: true, false (default) +# When true: Loads and executes plugins from PLUGIN_CONFIG_FILE PLUGINS_ENABLED=false + +# Path to the plugin configuration file +# Contains plugin definitions, hooks, and settings +# Default: plugins/config.yaml PLUGIN_CONFIG_FILE=plugins/config.yaml ##################################### @@ -497,14 +644,68 @@ WELL_KNOWN_CACHE_MAX_AGE=3600 # Example 4: Multiple custom files # WELL_KNOWN_CUSTOM_FILES={"ai.txt": "# AI Usage Policy\n\nThis MCP Gateway uses AI for:\n- Tool orchestration\n- Response generation\n- Error handling\n\nWe do not use AI for:\n- User data analysis\n- Behavioral tracking\n- Decision making without human oversight", "dnt-policy.txt": "# Do Not Track Policy\n\nWe respect the DNT header.\nNo tracking cookies are used.\nOnly essential session data is stored.", "change-password": "https://mycompany.com/account/password"} +##################################### +# Validation Settings +##################################### + +# These settings control input validation and security patterns +# Most users won't need to change these defaults + +# HTML/JavaScript injection patterns (regex) +# Used to detect potentially dangerous HTML/JS content +# VALIDATION_DANGEROUS_HTML_PATTERN - Pattern to detect dangerous HTML tags +# VALIDATION_DANGEROUS_JS_PATTERN - Pattern to detect JavaScript injection attempts + +# Allowed URL schemes for external requests +# Controls which URL schemes are permitted for gateway operations +# Default: ["http://", "https://", "ws://", "wss://"] +# VALIDATION_ALLOWED_URL_SCHEMES=["http://", "https://", "ws://", "wss://"] + +# Character validation patterns (regex) +# Used to validate various input fields +# VALIDATION_NAME_PATTERN - Pattern for validating names (allows spaces) +# VALIDATION_IDENTIFIER_PATTERN - Pattern for validating IDs (no spaces) +# VALIDATION_SAFE_URI_PATTERN - Pattern for safe URI characters +# VALIDATION_UNSAFE_URI_PATTERN - Pattern to detect unsafe URI characters +# VALIDATION_TOOL_NAME_PATTERN - MCP tool naming pattern +# VALIDATION_TOOL_METHOD_PATTERN - MCP tool method naming pattern + +# Size limits for various inputs (in characters or bytes) +# VALIDATION_MAX_NAME_LENGTH=255 +# VALIDATION_MAX_DESCRIPTION_LENGTH=8192 +# VALIDATION_MAX_TEMPLATE_LENGTH=65536 +# VALIDATION_MAX_CONTENT_LENGTH=1048576 +# VALIDATION_MAX_JSON_DEPTH=10 +# VALIDATION_MAX_URL_LENGTH=2048 +# VALIDATION_MAX_RPC_PARAM_SIZE=262144 +# VALIDATION_MAX_METHOD_LENGTH=128 + +# Rate limiting for validation operations +# Maximum requests per minute for validation endpoints +# VALIDATION_MAX_REQUESTS_PER_MINUTE=60 + +# Allowed MIME types for validation (JSON array) +# Controls which content types pass validation checks +# VALIDATION_ALLOWED_MIME_TYPES=["text/plain", "text/html", "text/css", "text/markdown", "text/javascript", "application/json", "application/xml", "application/pdf", "image/png", "image/jpeg", "image/gif", "image/svg+xml", "application/octet-stream"] + ##################################### # Development Configuration ##################################### +# Enable development mode (relaxed security, verbose logging) +# Options: true, false (default) +# WARNING: Never use in production! DEV_MODE=false + +# Enable auto-reload on code changes (for development) +# Options: true, false (default) +# Requires: Running with uvicorn directly (not gunicorn) RELOAD=false + +# Enable debug mode (verbose error messages, stack traces) +# Options: true, false (default) +# WARNING: May expose sensitive information! DEBUG=false -# SKIP_SSL_VERIFY is already defined in Security and CORS section # Header Passthrough (WARNING: Security implications) ENABLE_HEADER_PASSTHROUGH=false @@ -547,5 +748,3 @@ REQUIRE_STRONG_SECRETS=false # Set to false to allow startup with security warnings # NOT RECOMMENDED for production! # REQUIRE_STRONG_SECRETS=false - -MCPCONTEXT_UI_ENABLED=true diff --git a/DEVELOPING.md b/DEVELOPING.md index 2e1ea39ed..a6d0211a2 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -1,56 +1,813 @@ -# Development Quick-Start +# MCP Gateway Development Guide -## πŸ§ͺ Development Testing with **MCP Inspector** +This guide provides comprehensive information for developers working on the MCP Gateway (ContextForge) project. + +## Table of Contents +- [Quick Start](#quick-start) +- [Development Setup](#development-setup) +- [Project Architecture](#project-architecture) +- [Development Workflow](#development-workflow) +- [Code Quality](#code-quality) +- [Database Management](#database-management) +- [API Development](#api-development) +- [Plugin Development](#plugin-development) +- [Testing MCP Servers](#testing-mcp-servers) +- [Debugging](#debugging) +- [Performance Optimization](#performance-optimization) +- [Contributing](#contributing) + +## Quick Start + +```bash +# Clone and setup +git clone https://github.com/IBM/mcp-context-forge.git +cd mcp-context-forge + +# Complete setup with uv (recommended) +cp .env.example .env && make venv install-dev check-env + +# Start development server with hot-reload +make dev + +# Run quality checks before committing +make autoflake isort black pre-commit +make doctest test htmlcov flake8 pylint verify +``` + +## Development Setup + +### Prerequisites + +- **Python 3.11+** (3.10 minimum) +- **uv** (recommended) or pip/virtualenv +- **Make** for automation +- **Docker/Podman** (optional, for container development) +- **Node.js 18+** (for UI development and MCP Inspector) +- **PostgreSQL/MySQL** (optional, for production database testing) + +### Environment Setup + +#### Using uv (Recommended) + +```bash +# Install uv +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Create virtual environment and install dependencies +make venv install-dev + +# Verify environment +make check-env +``` + +#### Traditional Setup + +```bash +# Create virtual environment +python3 -m venv .venv +source .venv/bin/activate + +# Install in editable mode with all extras +pip install -e ".[dev,test,docs,otel,redis]" +``` + +### Configuration + +```bash +# Copy example configuration +cp .env.example .env + +# Edit configuration +vim .env + +# Key development settings +ENVIRONMENT=development # Enables debug features +DEV_MODE=true # Additional development helpers +DEBUG=true # Verbose error messages +RELOAD=true # Auto-reload on code changes +LOG_LEVEL=DEBUG # Maximum logging verbosity +MCPGATEWAY_UI_ENABLED=true # Enable Admin UI +MCPGATEWAY_ADMIN_API_ENABLED=true # Enable Admin API +``` + +## Project Architecture + +### Directory Structure + +``` +mcp-context-forge/ +β”œβ”€β”€ mcpgateway/ # Main application package +β”‚ β”œβ”€β”€ main.py # FastAPI application entry +β”‚ β”œβ”€β”€ cli.py # CLI commands +β”‚ β”œβ”€β”€ config.py # Settings management +β”‚ β”œβ”€β”€ models.py # SQLAlchemy models +β”‚ β”œβ”€β”€ schemas.py # Pydantic schemas +β”‚ β”œβ”€β”€ admin.py # Admin UI routes +β”‚ β”œβ”€β”€ auth.py # Authentication logic +β”‚ β”œβ”€β”€ services/ # Business logic layer +β”‚ β”‚ β”œβ”€β”€ gateway_service.py # Federation management +β”‚ β”‚ β”œβ”€β”€ server_service.py # Virtual server composition +β”‚ β”‚ β”œβ”€β”€ tool_service.py # Tool registry +β”‚ β”‚ β”œβ”€β”€ a2a_service.py # Agent-to-Agent +β”‚ β”‚ └── export_service.py # Bulk operations +β”‚ β”œβ”€β”€ transports/ # Protocol implementations +β”‚ β”‚ β”œβ”€β”€ sse_transport.py # Server-Sent Events +β”‚ β”‚ β”œβ”€β”€ websocket_transport.py # WebSocket +β”‚ β”‚ └── stdio_transport.py # Standard I/O wrapper +β”‚ β”œβ”€β”€ plugins/ # Plugin framework +β”‚ β”‚ β”œβ”€β”€ framework/ # Core plugin system +β”‚ β”‚ └── [plugin_dirs]/ # Individual plugins +β”‚ β”œβ”€β”€ validation/ # Input validation +β”‚ β”œβ”€β”€ utils/ # Utility modules +β”‚ β”œβ”€β”€ templates/ # Jinja2 templates (Admin UI) +β”‚ └── static/ # Static assets +β”œβ”€β”€ tests/ # Test suites +β”‚ β”œβ”€β”€ unit/ # Unit tests +β”‚ β”œβ”€β”€ integration/ # Integration tests +β”‚ β”œβ”€β”€ e2e/ # End-to-end tests +β”‚ β”œβ”€β”€ playwright/ # UI tests +β”‚ └── conftest.py # Pytest fixtures +β”œβ”€β”€ alembic/ # Database migrations +β”œβ”€β”€ docs/ # Documentation +β”œβ”€β”€ plugins/ # Plugin configurations +└── mcp-servers/ # Example MCP servers +``` + +### Technology Stack + +- **Web Framework**: FastAPI 0.115+ +- **Database ORM**: SQLAlchemy 2.0+ +- **Validation**: Pydantic 2.0+ +- **Admin UI**: HTMX + Alpine.js +- **Testing**: Pytest + Playwright +- **Package Management**: uv (or pip) +- **Database**: SQLite (dev), PostgreSQL/MySQL (production) +- **Caching**: Redis (optional) +- **Observability**: OpenTelemetry + +### Key Components + +#### 1. Core Services +- **GatewayService**: Manages federation and peer discovery +- **ServerService**: Handles virtual server composition +- **ToolService**: Tool registry and invocation +- **A2AService**: Agent-to-Agent integration +- **AuthService**: JWT authentication and authorization + +#### 2. Transport Layers +- **SSE Transport**: Server-Sent Events for streaming +- **WebSocket Transport**: Bidirectional real-time communication +- **HTTP Transport**: Standard JSON-RPC over HTTP +- **Stdio Wrapper**: Bridge for stdio-based MCP clients + +#### 3. Plugin System +- **Hook-based**: Pre/post request/response hooks +- **Filters**: PII, deny-list, regex, resource filtering +- **Custom plugins**: Extensible framework for custom logic + +## Development Workflow + +### Running the Development Server + +```bash +# Development server with hot-reload (port 8000) +make dev + +# Production-like server (port 4444) +make serve + +# With SSL/TLS +make certs serve-ssl + +# Custom host/port +python3 -m mcpgateway --host 0.0.0.0 --port 8080 +``` + +### Code Formatting and Linting + +```bash +# Auto-format code (run before committing) +make autoflake isort black pre-commit + +# Comprehensive linting +make flake8 bandit interrogate pylint verify + +# Quick lint for changed files only +make lint-changed + +# Watch mode for auto-linting +make lint-watch + +# Fix common issues automatically +make lint-fix +``` + +### Pre-commit Workflow + +```bash +# Install git hooks +make pre-commit-install + +# Run pre-commit checks manually +make pre-commit + +# Complete quality pipeline (recommended before commits) +make autoflake isort black pre-commit +make doctest test htmlcov smoketest +make flake8 bandit interrogate pylint verify +``` + +## Code Quality + +### Style Guidelines + +- **Python**: PEP 8 with Black formatting (line length 200) +- **Type hints**: Required for all public APIs +- **Docstrings**: Google style, required for all public functions +- **Imports**: Organized with isort (black profile) +- **Naming**: + - Functions/variables: `snake_case` + - Classes: `PascalCase` + - Constants: `UPPER_SNAKE_CASE` + +### Quality Tools + +```bash +# Format code +make black # Python formatter +make isort # Import sorter +make autoflake # Remove unused imports + +# Lint code +make flake8 # Style checker +make pylint # Advanced linting +make mypy # Type checking +make bandit # Security analysis + +# Documentation +make interrogate # Docstring coverage +make doctest # Test code examples + +# All checks +make verify # Run all quality checks +``` + +## Database Management + +### Migrations with Alembic + +```bash +# Create a new migration +alembic revision --autogenerate -m "Add new feature" + +# Apply migrations +alembic upgrade head + +# Rollback one revision +alembic downgrade -1 + +# Show migration history +alembic history + +# Reset database (CAUTION: destroys data) +alembic downgrade base && alembic upgrade head +``` + +### Database Operations ```bash -# Gateway & auth +# Different database backends +DATABASE_URL=sqlite:///./dev.db make dev # SQLite +DATABASE_URL=postgresql://localhost/mcp make dev # PostgreSQL +DATABASE_URL=mysql+pymysql://localhost/mcp make dev # MySQL + +# Database utilities +python3 -m mcpgateway.cli db upgrade # Apply migrations +python3 -m mcpgateway.cli db reset # Reset database +python3 -m mcpgateway.cli db seed # Seed test data +``` + +## API Development + +### Adding New Endpoints + +```python +# mcpgateway/main.py or separate router file +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from mcpgateway.database import get_db +from mcpgateway.schemas import MySchema + +router = APIRouter(prefix="/api/v1") + +@router.post("/my-endpoint", response_model=MySchema) +async def my_endpoint( + data: MySchema, + db: Session = Depends(get_db), + current_user = Depends(get_current_user) +): + """ + Endpoint description. + + Args: + data: Input data + db: Database session + current_user: Authenticated user + + Returns: + MySchema: Response data + """ + # Implementation + return result + +# Register router in main.py +app.include_router(router, tags=["my-feature"]) +``` + +### Schema Validation + +```python +# mcpgateway/schemas.py +from pydantic import BaseModel, Field, validator + +class MySchema(BaseModel): + """Schema for my feature.""" + + name: str = Field(..., min_length=1, max_length=255) + value: int = Field(..., gt=0, le=100) + + @validator('name') + def validate_name(cls, v): + """Custom validation logic.""" + if not v.isalnum(): + raise ValueError('Name must be alphanumeric') + return v + + class Config: + """Pydantic config.""" + str_strip_whitespace = True + use_enum_values = True +``` + +### Testing APIs + +```python +# tests/integration/test_my_endpoint.py +import pytest +from fastapi.testclient import TestClient + +def test_my_endpoint(test_client: TestClient, auth_headers): + """Test my endpoint.""" + response = test_client.post( + "/api/v1/my-endpoint", + json={"name": "test", "value": 50}, + headers=auth_headers + ) + assert response.status_code == 200 + assert response.json()["name"] == "test" +``` + +## Plugin Development + +### Creating a Plugin + +```yaml +# plugins/my_plugin/plugin-manifest.yaml +name: my_plugin +version: 1.0.0 +description: Custom plugin for X functionality +enabled: true +hooks: + - type: pre_request + handler: my_plugin.hooks:pre_request_hook + - type: post_response + handler: my_plugin.hooks:post_response_hook +config: + setting1: value1 + setting2: value2 +``` + +```python +# plugins/my_plugin/hooks.py +from typing import Dict, Any +import logging + +logger = logging.getLogger(__name__) + +async def pre_request_hook(request: Dict[str, Any], config: Dict[str, Any]) -> Dict[str, Any]: + """Process request before handling.""" + logger.info(f"Pre-request hook: {request.get('method')}") + # Modify request if needed + return request + +async def post_response_hook(response: Dict[str, Any], config: Dict[str, Any]) -> Dict[str, Any]: + """Process response before sending.""" + logger.info(f"Post-response hook: {response.get('result')}") + # Modify response if needed + return response +``` + +### Registering Plugins + +```yaml +# plugins/config.yaml +plugins: + - path: plugins/my_plugin + enabled: true + config: + custom_setting: value +``` + +```bash +# Enable plugin system +export PLUGINS_ENABLED=true +export PLUGIN_CONFIG_FILE=plugins/config.yaml + +# Test plugin +make dev +``` + +## Testing MCP Servers + +### Using MCP Inspector + +```bash +# Setup environment export MCP_GATEWAY_BASE_URL=http://localhost:4444 -export MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp -export MCP_AUTH="Bearer " +export MCP_SERVER_URL=http://localhost:4444/servers/UUID/mcp +export MCP_AUTH="Bearer $(python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 0 --secret my-test-key)" + +# Launch Inspector with SSE (direct) +npx @modelcontextprotocol/inspector + +# Launch with stdio wrapper +npx @modelcontextprotocol/inspector python3 -m mcpgateway.wrapper + +# Open browser to http://localhost:5173 +# Add server: http://localhost:4444/servers/UUID/sse +# Add header: Authorization: Bearer +``` + +### Using mcpgateway.translate + +```bash +# Expose stdio server over HTTP/SSE +python3 -m mcpgateway.translate \ + --stdio "uvx mcp-server-git" \ + --expose-sse \ + --port 9000 + +# Test with curl +curl http://localhost:9000/sse + +# Register with gateway +curl -X POST http://localhost:4444/gateways \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"git_server","url":"http://localhost:9000/sse"}' ``` -| Mode | Command | Notes | -| ----------------------------------------------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| **SSE (direct)** | `npx @modelcontextprotocol/inspector` | Connects straight to the Gateway's SSE endpoint. | -| **Stdio wrapper**
*(for clients that can't speak SSE)* | `npx @modelcontextprotocol/inspector python3 -m mcpgateway.wrapper` | Spins up the wrapper **in-process** and points Inspector to its stdio stream. | -| **Stdio wrapper via uv / uvx** | `npx @modelcontextprotocol/inspector uvx python3 -m mcpgateway.wrapper` | Uses the lightning-fast `uv` virtual-env if installed. | +### Using SuperGateway Bridge + +```bash +# Install and run SuperGateway +npm install -g supergateway +npx supergateway --stdio "uvx mcp-server-git" + +# Register with MCP Gateway +curl -X POST http://localhost:4444/gateways \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"supergateway","url":"http://localhost:8000/sse"}' +``` + +## Debugging + +### Debug Mode + +```bash +# Enable debug mode +export DEBUG=true +export LOG_LEVEL=DEBUG +export DEV_MODE=true -πŸ” MCP Inspector boots at **[http://localhost:5173](http://localhost:5173)** - open it in a browser and add: +# Run with debugger +python3 -m debugpy --listen 5678 --wait-for-client -m mcpgateway -```text -Server URL: http://localhost:4444/servers/UUID_OF_SERVER_1/sse -Headers: Authorization: Bearer +# Or use IDE debugger with launch.json (VS Code) ``` ---- +### VS Code Configuration + +```json +// .vscode/launch.json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug MCP Gateway", + "type": "python", + "request": "launch", + "module": "mcpgateway", + "args": ["--host", "0.0.0.0", "--port", "8000"], + "env": { + "DEBUG": "true", + "LOG_LEVEL": "DEBUG", + "ENVIRONMENT": "development" + }, + "console": "integratedTerminal" + } + ] +} +``` -## πŸŒ‰ SuperGateway (stdio-in β‡’ SSE-out bridge) +### Logging -SuperGateway lets you expose *any* MCP **stdio** server over **SSE** with a single command - perfect for -remote debugging or for clients that only understand SSE. +```python +# Add debug logging in code +import logging +logger = logging.getLogger(__name__) + +def my_function(): + logger.debug(f"Debug info: {variable}") + logger.info("Operation started") + logger.warning("Potential issue") + logger.error("Error occurred", exc_info=True) +``` ```bash -# Using uvx (ships with uv) -pip install uv -npx -y supergateway --stdio "uvx mcp-server-git" +# View logs +tail -f mcpgateway.log # If LOG_TO_FILE=true +journalctl -u mcpgateway -f # Systemd service +docker logs -f mcpgateway # Docker container ``` -| Endpoint | Method | URL | -| ------------------------ | ------ | -------------------------------------------------------------- | -| **SSE stream** | `GET` | [http://localhost:8000/sse](http://localhost:8000/sse) | -| **Message back-channel** | `POST` | [http://localhost:8000/message](http://localhost:8000/message) | +### Request Tracing + +```bash +# Enable OpenTelemetry tracing +export OTEL_ENABLE_OBSERVABILITY=true +export OTEL_TRACES_EXPORTER=console # Or otlp, jaeger + +# Run with tracing +make dev + +# View traces in console or tracing backend +``` -Combine this with the Gateway: +### Database Debugging ```bash -# Register the SuperGateway SSE endpoint as a peer -curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{"name":"local-supergateway","url":"http://localhost:8000/sse"}' \ - http://localhost:4444/gateways +# Enable SQL echo +export DATABASE_ECHO=true + +# Query database directly +sqlite3 mcp.db "SELECT * FROM tools LIMIT 10;" +psql mcp -c "SELECT * FROM servers;" + +# Database profiling +python3 -m mcpgateway.utils.db_profiler +``` + +## Performance Optimization + +### Profiling + +```python +# Profile code execution +import cProfile +import pstats + +def profile_function(): + profiler = cProfile.Profile() + profiler.enable() + + # Code to profile + expensive_operation() + + profiler.disable() + stats = pstats.Stats(profiler) + stats.sort_stats('cumulative') + stats.print_stats(10) +``` + +### Caching Strategies + +```python +# Use Redis caching +from mcpgateway.cache import cache_get, cache_set + +async def get_expensive_data(key: str): + # Try cache first + cached = await cache_get(f"data:{key}") + if cached: + return cached + + # Compute if not cached + result = expensive_computation() + await cache_set(f"data:{key}", result, ttl=3600) + return result ``` -The tools hosted by **`mcp-server-git`** are now available in the Gateway catalog, and therefore -also visible through `mcpgateway.wrapper` or any other MCP client. +### Database Optimization + +```python +# Use eager loading to avoid N+1 queries +from sqlalchemy.orm import joinedload +def get_servers_with_tools(db: Session): + return db.query(Server)\ + .options(joinedload(Server.tools))\ + .all() + +# Use bulk operations +def bulk_insert_tools(db: Session, tools: List[Dict]): + db.bulk_insert_mappings(Tool, tools) + db.commit() ``` + +### Async Best Practices + +```python +# Use async/await properly +import asyncio +from typing import List + +async def process_items(items: List[str]): + # Process concurrently + tasks = [process_item(item) for item in items] + results = await asyncio.gather(*tasks) + return results + +# Use connection pooling +from aiohttp import ClientSession + +async def make_requests(): + async with ClientSession() as session: + # Reuse session for multiple requests + async with session.get(url1) as resp1: + data1 = await resp1.json() + async with session.get(url2) as resp2: + data2 = await resp2.json() +``` + +## Contributing + +### Development Process + +1. **Fork and clone** the repository +2. **Create a feature branch**: `git checkout -b feature/my-feature` +3. **Set up environment**: `make venv install-dev` +4. **Make changes** and write tests +5. **Run quality checks**: `make verify` +6. **Commit with sign-off**: `git commit -s -m "feat: add new feature"` +7. **Push and create PR**: `git push origin feature/my-feature` + +### Commit Guidelines + +Follow [Conventional Commits](https://www.conventionalcommits.org/): + +- `feat:` New feature +- `fix:` Bug fix +- `docs:` Documentation changes +- `style:` Code style changes (formatting, etc.) +- `refactor:` Code refactoring +- `test:` Test additions or changes +- `chore:` Build process or auxiliary tool changes + +### Code Review Process + +1. **Self-review** your changes +2. **Run all tests**: `make test` +3. **Update documentation** if needed +4. **Ensure CI passes** +5. **Address review feedback** +6. **Squash commits** if requested + +### Getting Help + +- **Documentation**: [docs/](docs/) +- **Issues**: [GitHub Issues](https://github.com/IBM/mcp-context-forge/issues) +- **Discussions**: [GitHub Discussions](https://github.com/IBM/mcp-context-forge/discussions) +- **Contributing Guide**: [CONTRIBUTING.md](CONTRIBUTING.md) + +## Advanced Topics + +### Multi-tenancy Development + +```python +# Implement tenant isolation +from mcpgateway.auth import get_current_tenant + +@router.get("/tenant-data") +async def get_tenant_data( + tenant = Depends(get_current_tenant), + db: Session = Depends(get_db) +): + # Filter by tenant + return db.query(Model).filter(Model.tenant_id == tenant.id).all() +``` + +### Custom Transport Implementation + +```python +# mcpgateway/transports/custom_transport.py +from mcpgateway.transports.base import BaseTransport + +class CustomTransport(BaseTransport): + """Custom transport implementation.""" + + async def connect(self, url: str): + """Establish connection.""" + # Implementation + + async def send(self, message: dict): + """Send message.""" + # Implementation + + async def receive(self) -> dict: + """Receive message.""" + # Implementation +``` + +### Federation Development + +```python +# Test federation locally +# Start multiple instances +PORT=4444 make dev # Instance 1 +PORT=4445 make dev # Instance 2 + +# Register peers +curl -X POST http://localhost:4444/gateways \ + -H "Authorization: Bearer $TOKEN" \ + -d '{"name":"peer2","url":"http://localhost:4445/sse"}' +``` + +## Security Considerations + +### Authentication Testing + +```bash +# Generate test tokens +python3 -m mcpgateway.utils.create_jwt_token \ + --username test@example.com \ + --exp 60 \ + --secret test-key + +# Test with different auth methods +curl -H "Authorization: Bearer $TOKEN" http://localhost:4444/api/test +curl -u admin:changeme http://localhost:4444/api/test +``` + +### Security Scanning + +```bash +# Static analysis +make bandit + +# Dependency scanning +make security-scan + +# OWASP checks +pip install safety +safety check +``` + +## Troubleshooting + +### Common Issues + +1. **Import errors**: Ensure package installed with `pip install -e .` +2. **Database locked**: Use PostgreSQL for concurrent access +3. **Port in use**: Change with `PORT=8001 make dev` +4. **Missing dependencies**: Run `make install-dev` +5. **Permission errors**: Check file permissions and user context + +### Debug Commands + +```bash +# Check environment +make check-env + +# Verify installation +python3 -c "import mcpgateway; print(mcpgateway.__version__)" + +# Test configuration +python3 -m mcpgateway.config + +# Database status +alembic current + +# Clear caches +redis-cli FLUSHDB +``` + +## Resources + +- [MCP Specification](https://modelcontextprotocol.io/) +- [FastAPI Documentation](https://fastapi.tiangolo.com/) +- [SQLAlchemy Documentation](https://docs.sqlalchemy.org/) +- [Pydantic Documentation](https://docs.pydantic.dev/) +- [HTMX Documentation](https://htmx.org/) +- [Alpine.js Documentation](https://alpinejs.dev/) diff --git a/README.md b/README.md index d006e036c..753aa2a0e 100644 --- a/README.md +++ b/README.md @@ -356,7 +356,7 @@ Copy [.env.example](https://github.com/IBM/mcp-context-forge/blob/main/.env.exam ```bash # 1️⃣ Spin up the sample GO MCP time server using mcpgateway.translate & docker python3 -m mcpgateway.translate \ - --stdio "docker run --rm -i -p 8888:8080 ghcr.io/ibm/fast-time-server:latest -transport=stdio" \ + --stdio "docker run --rm -i ghcr.io/ibm/fast-time-server:latest -transport=stdio" \ --expose-sse \ --port 8003 @@ -1494,6 +1494,8 @@ mcpgateway | `UNHEALTHY_THRESHOLD` | Fail-count before peer deactivation, | `3` | int > 0 | | | Set to -1 if deactivation is not needed. | | | | `GATEWAY_VALIDATION_TIMEOUT` | Gateway URL validation timeout (secs) | `5` | int > 0 | +| `FILELOCK_NAME` | File lock for leader election | `gateway_service_leader.lock` | string | +| `DEFAULT_ROOTS` | Default root paths for resources | `[]` | JSON array | ### Database @@ -1513,6 +1515,8 @@ mcpgateway | `CACHE_TYPE` | Backend type | `database` | `none`, `memory`, `database`, `redis` | | `REDIS_URL` | Redis connection URL | (none) | string or empty | | `CACHE_PREFIX` | Key prefix | `mcpgw:` | string | +| `SESSION_TTL` | Session validity (secs) | `3600` | int > 0 | +| `MESSAGE_TTL` | Message retention (secs) | `600` | int > 0 | | `REDIS_MAX_RETRIES` | Max Retry Attempts | `3` | int > 0 | | `REDIS_RETRY_INTERVAL_MS` | Retry Interval (ms) | `2000` | int > 0 | diff --git a/TESTING.md b/TESTING.md index 61c0d1a7b..ccf64cfa0 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1,103 +1,545 @@ -# Testing Guide for MCP Context Forge +# Testing Guide for MCP Gateway (ContextForge) -This guide explains how to set up and run tests for the MCP Context Forge project. +This comprehensive guide covers all aspects of testing the MCP Gateway, from unit tests to end-to-end integration testing. + +## Table of Contents +- [Quick Start](#quick-start) +- [Prerequisites](#prerequisites) +- [Test Categories](#test-categories) +- [Running Tests](#running-tests) +- [Coverage Reports](#coverage-reports) +- [Writing Tests](#writing-tests) +- [Continuous Integration](#continuous-integration) +- [Troubleshooting](#troubleshooting) + +## Quick Start + +```bash +# Complete test suite with coverage +make doctest test htmlcov + +# Quick smoke test +make smoketest + +# Full quality check pipeline +make doctest test htmlcov smoketest lint-web flake8 bandit interrogate pylint verify +``` ## Prerequisites -- Python 3.10 or higher -- virtualenv or venv (for virtual environment management) -- Make (for running Makefile commands) +- **Python 3.11+** (3.10 minimum) +- **uv** (recommended) or pip/virtualenv +- **Docker/Podman** (for container tests) +- **Make** (for automation) +- **Node.js 18+** (for Playwright UI tests) + +### Initial Setup + +```bash +# Setup with uv (recommended) +make venv install-dev + +# Alternative: traditional pip +python3 -m venv .venv +source .venv/bin/activate +pip install -e ".[dev,test]" +``` -## Setting Up the Test Environment +## Test Categories -First, create a virtual environment and install the project's development dependencies: +### 1. Unit Tests (`tests/unit/`) +Fast, isolated tests for individual components. ```bash -make venv # Create a virtual environment -make install # Install the project with development dependencies +# Run all unit tests +make test + +# Run specific module tests +pytest tests/unit/mcpgateway/test_config.py -v + +# Run with coverage +pytest --cov=mcpgateway --cov-report=term-missing tests/unit/ ``` -## Running Tests +### 2. Integration Tests (`tests/integration/`) +Tests for API endpoints and service interactions. + +```bash +# Run integration tests +pytest tests/integration/ -v + +# Test specific endpoints +pytest tests/integration/test_api.py::test_tools_endpoint -v +``` -### Running All Tests +### 3. End-to-End Tests (`tests/e2e/`) +Complete workflow tests with real services. + +```bash +# Run E2E tests +pytest tests/e2e/ -v + +# Container-based smoke test +make smoketest +``` + +### 4. Security Tests (`tests/security/`) +Security validation and vulnerability testing. + +```bash +# Security test suite +pytest tests/security/ -v + +# Static security analysis +make bandit + +# Dependency vulnerability scan +make security-scan +``` + +### 5. UI Tests (`tests/playwright/`) +Browser-based Admin UI testing with Playwright. + +```bash +# Install Playwright browsers +make playwright-install + +# Run UI tests +make test-ui # With browser UI +make test-ui-headless # Headless mode +make test-ui-debug # Debug mode with inspector +make test-ui-parallel # Parallel execution + +# Generate test report +make test-ui-report +``` + +### 6. Async Tests (`tests/async/`) +Asynchronous operation and WebSocket testing. + +```bash +# Run async tests +pytest tests/async/ -v --async-mode=auto +``` -To run all tests, simply use: +### 7. Fuzz Tests (`tests/fuzz/`) +Property-based and fuzz testing for robustness. ```bash +# Run fuzz tests +pytest tests/fuzz/ -v + +# With hypothesis settings +pytest tests/fuzz/ --hypothesis-show-statistics +``` + +### 8. Migration Tests (`tests/migration/`) +Database migration and upgrade testing. + +```bash +# Test migrations +pytest tests/migration/ -v + +# Test specific migration +pytest tests/migration/test_v0_7_0_migration.py -v +``` + +## Running Tests + +### Complete Test Pipeline + +```bash +# Full test suite (recommended before commits) +make doctest test htmlcov smoketest + +# Quick validation make test + +# With code quality checks +make test flake8 pylint ``` -This will: -1. Create a virtual environment if it doesn't exist -2. Install required testing dependencies (pytest, pytest-asyncio, pytest-cov) -3. Run the pytest suite with verbose output +### Doctest Testing + +```bash +# Run all doctests +make doctest + +# Verbose output +make doctest-verbose -### Running Specific Tests +# With coverage +make doctest-coverage -You can run specific tests by specifying the file or directory: +# Check docstring coverage +make interrogate +``` + +### Specific Test Patterns ```bash -# Activate the virtual environment +# Activate virtual environment first source ~/.venv/mcpgateway/bin/activate -# Run a specific test file -python3 -m pytest tests/unit/mcpgateway/test_config.py -v +# Run tests matching a pattern +pytest -k "test_auth" -v + +# Run tests with specific markers +pytest -m "asyncio" -v +pytest -m "not slow" -v -# Run a specific test class -python3 -m pytest tests/unit/mcpgateway/validation/test_jsonrpc.py::TestJSONRPCValidation -v +# Run failed tests from last run +pytest --lf -v -# Run a specific test method -python3 -m pytest tests/unit/mcpgateway/validation/test_jsonrpc.py::TestJSONRPCValidation::test_validate_valid_request -v +# Run tests in parallel +pytest -n auto tests/unit/ ``` -### Testing README Examples +### Testing Individual Files -To test code examples from the README: +```bash +# Test a specific file with coverage +. /home/cmihai/.venv/mcpgateway/bin/activate +pytest --cov-report=annotate tests/unit/mcpgateway/test_translate.py + +# Test with detailed output +pytest -vvs tests/unit/mcpgateway/services/test_gateway_service.py + +# Test specific class or method +pytest tests/unit/mcpgateway/test_config.py::TestSettings -v +pytest tests/unit/mcpgateway/test_auth.py::test_jwt_creation -v +``` + +## Coverage Reports + +### HTML Coverage Report ```bash -make pytest-examples +# Generate HTML coverage report +make htmlcov + +# View report (opens in browser) +open docs/docs/coverage/index.html # macOS +xdg-open docs/docs/coverage/index.html # Linux ``` -## Test Coverage +### Terminal Coverage Report -To run tests with coverage reporting: +```bash +# Simple coverage summary +make coverage + +# Detailed line-by-line coverage +pytest --cov=mcpgateway --cov-report=term-missing tests/ + +# Coverage for specific modules +pytest --cov=mcpgateway.services --cov-report=term tests/unit/mcpgateway/services/ +``` + +### Coverage Thresholds ```bash -# Activate the virtual environment -source ~/.venv/mcpgateway/bin/activate +# Enforce minimum coverage (fails if below 80%) +pytest --cov=mcpgateway --cov-fail-under=80 tests/ -# Run tests with coverage -python3 -m pytest --cov=mcpgateway tests/ +# Check coverage trends +coverage report --show-missing +coverage html --directory=htmlcov +``` -# Generate a coverage report -python3 -m pytest --cov=mcpgateway --cov-report=html tests/ +## Writing Tests + +### Test Structure + +```python +# tests/unit/mcpgateway/test_example.py +import pytest +from unittest.mock import Mock, patch +from mcpgateway.services import ExampleService + +class TestExampleService: + """Test suite for ExampleService.""" + + @pytest.fixture + def service(self, db_session): + """Create service instance with mocked dependencies.""" + return ExampleService(db=db_session) + + def test_basic_operation(self, service): + """Test basic service operation.""" + result = service.do_something("test") + assert result.status == "success" + + @pytest.mark.asyncio + async def test_async_operation(self, service): + """Test async service operation.""" + result = await service.async_operation() + assert result is not None + + @patch('mcpgateway.services.external_api') + def test_with_mock(self, mock_api, service): + """Test with mocked external dependency.""" + mock_api.return_value = {"status": "ok"} + result = service.call_external() + mock_api.assert_called_once() ``` -The HTML coverage report will be available in the `htmlcov` directory. +### Using Fixtures + +```python +# Import common fixtures from conftest.py +def test_with_database(db_session): + """Test using database session fixture.""" + # db_session is automatically provided by conftest.py + from mcpgateway.models import Tool + tool = Tool(name="test_tool") + db_session.add(tool) + db_session.commit() + assert tool.id is not None + +def test_with_client(test_client): + """Test using FastAPI test client.""" + response = test_client.get("/health") + assert response.status_code == 200 +``` -## Creating New Tests +### Testing Async Code -When creating new tests, follow these guidelines: +```python +import pytest +import asyncio -1. Place test files in the appropriate directory under `tests/unit/` -2. Use the naming convention `test_*.py` for test files -3. Use pytest fixtures from `conftest.py` where applicable -4. Use `@pytest.mark.asyncio` decorator for asynchronous tests +@pytest.mark.asyncio +async def test_websocket_connection(): + """Test WebSocket connection handling.""" + from mcpgateway.transports import WebSocketTransport + transport = WebSocketTransport() -## Continuous Integration + async with transport.connect("ws://localhost:4444/ws") as conn: + await conn.send_json({"method": "ping"}) + response = await conn.receive_json() + assert response["result"] == "pong" +``` + +### Property-Based Testing + +```python +from hypothesis import given, strategies as st -The project is configured to run tests automatically in CI/CD pipelines. -When committing changes, ensure all tests pass locally first: +@given(st.text(min_size=1, max_size=255)) +def test_name_validation(name): + """Test name validation with random inputs.""" + from mcpgateway.validation import validate_name + if validate_name(name): + assert len(name) <= 255 + assert not name.startswith(" ") +``` + +## Environment-Specific Testing + +### Testing with Different Databases ```bash +# SQLite (default) make test + +# PostgreSQL +DATABASE_URL=postgresql://user:pass@localhost/test_mcp make test + +# MySQL/MariaDB +DATABASE_URL=mysql+pymysql://user:pass@localhost/test_mcp make test +``` + +### Testing with Different Configurations + +```bash +# Test with production settings +ENVIRONMENT=production AUTH_REQUIRED=true make test + +# Test with Redis caching +CACHE_TYPE=redis REDIS_URL=redis://localhost:6379 make test + +# Test with federation enabled +FEDERATION_ENABLED=true FEDERATION_PEERS='["http://peer1:4444"]' make test +``` + +## Performance Testing + +### Load Testing + +```bash +# Using hey (HTTP load generator) +make test-hey + +# Custom load test +hey -n 1000 -c 10 -H "Authorization: Bearer $TOKEN" http://localhost:4444/health +``` + +### Profiling Tests + +```bash +# Run tests with profiling +pytest --profile tests/unit/ + +# Generate profile report +python -m cProfile -o profile.stats $(which pytest) tests/ +python -m pstats profile.stats +``` + +## Continuous Integration + +### GitHub Actions Workflow + +Tests run automatically on: +- Pull requests +- Push to main branch +- Nightly schedule + +```yaml +# .github/workflows/test.yml example +- name: Run test suite + run: | + make venv install-dev + make doctest test htmlcov + make smoketest +``` + +### Pre-commit Hooks + +```bash +# Install pre-commit hooks +make pre-commit-install + +# Run manually +make pre-commit + +# Skip hooks (emergency only) +git commit --no-verify +``` + +## Debugging Tests + +### Verbose Output + +```bash +# Maximum verbosity +pytest -vvs tests/unit/ + +# Show print statements +pytest -s tests/unit/ + +# Show local variables on failure +pytest -l tests/unit/ +``` + +### Interactive Debugging + +```python +# Add breakpoint in test +def test_complex_logic(): + result = complex_function() + import pdb; pdb.set_trace() # Debugger breakpoint + assert result == expected +``` + +```bash +# Run with pdb on failure +pytest --pdb tests/unit/ + +# Run with ipdb (if installed) +pytest --pdbcls=IPython.terminal.debugger:TerminalPdb tests/unit/ +``` + +### Test Logs + +```bash +# Capture logs during tests +pytest --log-cli-level=DEBUG tests/unit/ + +# Save logs to file +pytest --log-file=test.log --log-file-level=DEBUG tests/unit/ ``` ## Troubleshooting -If you encounter issues with running tests: +### Common Issues + +#### 1. Import Errors +```bash +# Ensure package is installed in editable mode +pip install -e . + +# Verify Python path +python -c "import sys; print(sys.path)" +``` + +#### 2. Database Errors +```bash +# Reset test database +rm -f test_mcp.db +alembic upgrade head + +# Use in-memory database for tests +DATABASE_URL=sqlite:///:memory: pytest tests/unit/ +``` + +#### 3. Async Test Issues +```bash +# Install async test dependencies +pip install pytest-asyncio pytest-aiohttp + +# Use proper event loop scope +pytest --asyncio-mode=auto tests/async/ +``` + +#### 4. Coverage Not Updating +```bash +# Clear coverage data +coverage erase + +# Regenerate coverage +make htmlcov +``` + +#### 5. Playwright Browser Issues +```bash +# Reinstall browsers +npx playwright install --with-deps + +# Use specific browser +BROWSER=firefox make test-ui +``` + +### Test Isolation + +```bash +# Run tests in random order to detect dependencies +pytest --random-order tests/unit/ + +# Run each test in a subprocess +pytest --forked tests/unit/ + +# Clear cache between runs +pytest --cache-clear tests/ +``` -1. Check that you're using the virtual environment with the correct dependencies -2. Verify that your Python version is compatible (Python 3.10+) -3. Try recreating the virtual environment: `make clean && make venv && make install` -4. Check for any error messages during dependency installation +## Best Practices + +1. **Keep tests fast**: Unit tests should run in < 1 second +2. **Use fixtures**: Leverage conftest.py for common setup +3. **Mock external dependencies**: Don't rely on network services +4. **Test edge cases**: Include boundary and error conditions +5. **Maintain test coverage**: Aim for > 80% coverage +6. **Write descriptive test names**: `test_auth_fails_with_invalid_token` +7. **Group related tests**: Use test classes for organization +8. **Clean up resources**: Use fixtures with proper teardown +9. **Document complex tests**: Add docstrings explaining the test purpose +10. **Run tests before committing**: Use pre-commit hooks + +## Additional Resources + +- [Pytest Documentation](https://docs.pytest.org/) +- [Coverage.py Documentation](https://coverage.readthedocs.io/) +- [Playwright Documentation](https://playwright.dev/python/) +- [Hypothesis Documentation](https://hypothesis.readthedocs.io/) +- [MCP Gateway Contributing Guide](CONTRIBUTING.md) diff --git a/docs/docs/architecture/adr/005-vscode-devcontainer-support.md b/docs/docs/architecture/adr/005-vscode-devcontainer-support.md index f95b3403f..11976c097 100644 --- a/docs/docs/architecture/adr/005-vscode-devcontainer-support.md +++ b/docs/docs/architecture/adr/005-vscode-devcontainer-support.md @@ -91,7 +91,7 @@ The devcontainer uses: - **Python 3.11**: As specified in the project requirements - **PDM and UV**: For package management (matching the project's tooling) - **Make targets**: Leverages existing `make install-dev` and `make test` workflows -- **Environment variables**: Sets `MCPGATEWAY_DEV_MODE=true` for development +- **Environment variables**: Sets `DEV_MODE=true` for development - **VS Code extensions**: Includes Python and Docker extensions for optimal development experience ## Verification diff --git a/docs/docs/development/developer-workstation.md b/docs/docs/development/developer-workstation.md index 38eee6e38..8873a14fa 100644 --- a/docs/docs/development/developer-workstation.md +++ b/docs/docs/development/developer-workstation.md @@ -4,7 +4,7 @@ This guide helps you to set up your local environment for contributing to the Mo ## Tooling Requirements -- **Python** (>= 3.10) +- **Python** (>= 3.11) - Download from [python.org](https://www.python.org/downloads/) or use your package manager (e.g., `brew install python` on macOS, `sudo apt-get install python3` on Ubuntu). - Verify: `python3 --version`. - **Docker or Podman** diff --git a/docs/docs/development/index.md b/docs/docs/development/index.md index 262f0b571..86cec2f8d 100644 --- a/docs/docs/development/index.md +++ b/docs/docs/development/index.md @@ -19,7 +19,7 @@ Welcome! This guide is for developers contributing to MCP Gateway. Whether you'r MCP Gateway is built with: -* **Python 3.10+** +* **Python 3.11+** * **FastAPI** + **SQLAlchemy (async)** + **Pydantic Settings** * **HTMX**, **Alpine.js**, **TailwindCSS** for the Admin UI @@ -52,9 +52,9 @@ Test coverage includes: Use: ```bash -make test # run all tests -make test-unit # run only unit tests -make test-e2e # run end-to-end +make test # run full suite +python3 -m pytest tests/unit # run only unit tests +python3 -m pytest tests/e2e # run end-to-end scenarios ``` --- diff --git a/docs/docs/index.md b/docs/docs/index.md index b42d2ace6..379b4c88d 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -151,7 +151,7 @@ uvx --from mcp-contextforge-gateway mcpgateway --host 0.0.0.0 --port 4444
πŸ“‹ Prerequisites -* **Python β‰₯ 3.10** (3.11 recommended) +* **Python β‰₯ 3.11** (3.11 recommended) * **curl + jq** - only for the last smoke-test step
@@ -787,7 +787,7 @@ Common tasks inside the container: ```bash # Start dev server (hot reload) -make dev # http://localhost:4444 +make dev # http://localhost:8000 # Run tests & linters make test @@ -819,7 +819,7 @@ No local Docker? Use Codespaces: ### Prerequisites -* **Python β‰₯ 3.10** +* **Python β‰₯ 3.11** * **GNU Make** (optional, but all common workflows are available as Make targets) * Optional: **Docker / Podman** for containerized runs diff --git a/docs/docs/manage/configuration.md b/docs/docs/manage/configuration.md index 215f38a4a..6df6e76b2 100644 --- a/docs/docs/manage/configuration.md +++ b/docs/docs/manage/configuration.md @@ -159,22 +159,26 @@ DATABASE_URL=postgresql://postgres:changeme@localhost:5432/mcp # Postgr DATABASE_URL=mongodb://admin:changeme@localhost:27017/mcp # MongoDB # Connection pool settings (optional) -DATABASE_POOL_SIZE=10 -DATABASE_MAX_OVERFLOW=20 -DATABASE_POOL_TIMEOUT=30 +DB_POOL_SIZE=200 +DB_MAX_OVERFLOW=5 +DB_POOL_TIMEOUT=60 +DB_POOL_RECYCLE=3600 +DB_MAX_RETRIES=5 +DB_RETRY_INTERVAL_MS=2000 ``` ### Server Configuration ```bash -# Network binding +# Network binding & runtime HOST=0.0.0.0 PORT=4444 +ENVIRONMENT=development +APP_DOMAIN=localhost +APP_ROOT_PATH= -# SSL/TLS (optional) -SSL=false -CERT_FILE=/app/certs/cert.pem -KEY_FILE=/app/certs/key.pem +# TLS helper (run-gunicorn.sh) +# SSL=true CERT_FILE=certs/cert.pem KEY_FILE=certs/key.pem ./run-gunicorn.sh ``` ### Authentication & Security @@ -206,8 +210,12 @@ PLATFORM_ADMIN_EMAIL=admin@example.com PLATFORM_ADMIN_PASSWORD=changeme # Security Features +AUTH_REQUIRED=true SECURITY_HEADERS_ENABLED=true +CORS_ENABLED=true CORS_ALLOW_CREDENTIALS=true +ALLOWED_ORIGINS="https://admin.example.com,https://api.example.com" +AUTH_ENCRYPTION_SECRET=$(openssl rand -hex 32) ``` ### Feature Flags @@ -223,11 +231,13 @@ MCPGATEWAY_BULK_IMPORT_MAX_TOOLS=200 MCPGATEWAY_A2A_ENABLED=true MCPGATEWAY_A2A_MAX_AGENTS=100 MCPGATEWAY_A2A_DEFAULT_TIMEOUT=30 +MCPGATEWAY_A2A_MAX_RETRIES=3 MCPGATEWAY_A2A_METRICS_ENABLED=true # Federation & Discovery -MCPGATEWAY_ENABLE_FEDERATION=true -MCPGATEWAY_ENABLE_MDNS_DISCOVERY=true +FEDERATION_ENABLED=true +FEDERATION_DISCOVERY=true +FEDERATION_PEERS=["https://gateway-1.internal", "https://gateway-2.internal"] ``` ### Caching Configuration @@ -236,11 +246,12 @@ MCPGATEWAY_ENABLE_MDNS_DISCOVERY=true # Cache Backend CACHE_TYPE=redis # Options: memory, redis, database, none REDIS_URL=redis://localhost:6379/0 +CACHE_PREFIX=mcpgateway # Cache TTL (seconds) -CACHE_DEFAULT_TTL=300 -CACHE_TOOL_TTL=600 -CACHE_RESOURCE_TTL=180 +SESSION_TTL=3600 +MESSAGE_TTL=600 +RESOURCE_CACHE_TTL=1800 ``` ### Logging Settings @@ -257,7 +268,6 @@ LOG_FOLDER=logs # Structured Logging LOG_FORMAT=json # json, plain -LOG_INCLUDE_TIMESTAMPS=true ``` ### Development & Debug @@ -269,9 +279,10 @@ DEV_MODE=true RELOAD=true DEBUG=true -# Metrics & Observability -METRICS_ENABLED=true -HEALTH_CHECK_ENABLED=true +# Observability +OTEL_ENABLE_OBSERVABILITY=true +OTEL_TRACES_EXPORTER=otlp +OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 ``` --- @@ -516,20 +527,16 @@ spec: ### Performance Tuning ```bash -# Database Connection Pool -DATABASE_POOL_SIZE=20 -DATABASE_MAX_OVERFLOW=30 -DATABASE_POOL_TIMEOUT=60 -DATABASE_POOL_RECYCLE=3600 - -# HTTP Settings -HTTP_WORKERS=4 -HTTP_KEEPALIVE=2 -HTTP_TIMEOUT=30 - -# Tool Execution -TOOL_EXECUTION_TIMEOUT=300 -MAX_CONCURRENT_TOOLS=10 +# Database connection pool +DB_POOL_SIZE=200 +DB_MAX_OVERFLOW=5 +DB_POOL_TIMEOUT=60 +DB_POOL_RECYCLE=3600 + +# Tool execution +TOOL_TIMEOUT=120 +MAX_TOOL_RETRIES=5 +TOOL_CONCURRENT_LIMIT=10 ``` ### Security Hardening @@ -538,29 +545,20 @@ MAX_CONCURRENT_TOOLS=10 # Enable all security features SECURITY_HEADERS_ENABLED=true CORS_ALLOW_CREDENTIALS=false +AUTH_REQUIRED=true REQUIRE_TOKEN_EXPIRATION=true -JWT_ACCESS_TOKEN_EXPIRE_MINUTES=15 -JWT_REFRESH_TOKEN_EXPIRE_DAYS=7 - -# Rate limiting -RATE_LIMIT_ENABLED=true -RATE_LIMIT_REQUESTS_PER_MINUTE=60 -RATE_LIMIT_BURST=10 +TOKEN_EXPIRY=60 ``` ### Observability Integration ```bash # OpenTelemetry (Phoenix, Jaeger, etc.) +OTEL_ENABLE_OBSERVABILITY=true +OTEL_TRACES_EXPORTER=otlp OTEL_EXPORTER_OTLP_ENDPOINT=http://phoenix:4317 +OTEL_EXPORTER_OTLP_PROTOCOL=grpc OTEL_SERVICE_NAME=mcp-gateway -OTEL_TRACES_EXPORTER=otlp -OTEL_METRICS_EXPORTER=otlp - -# Prometheus Metrics -METRICS_ENABLED=true -METRICS_PORT=9090 -METRICS_PATH=/metrics ``` --- diff --git a/docs/docs/manage/securing.md b/docs/docs/manage/securing.md index 25916003c..0a0bffc83 100644 --- a/docs/docs/manage/securing.md +++ b/docs/docs/manage/securing.md @@ -21,19 +21,22 @@ This guide provides essential security configurations and best practices for dep MCPGATEWAY_UI_ENABLED=false MCPGATEWAY_ADMIN_API_ENABLED=false -# Disable unused features -MCPGATEWAY_ENABLE_ROOTS=false # If not using roots -MCPGATEWAY_ENABLE_PROMPTS=false # If not using prompts -MCPGATEWAY_ENABLE_RESOURCES=false # If not using resources +# Optional: turn off auxiliary systems you do not need +MCPGATEWAY_BULK_IMPORT_ENABLED=false +MCPGATEWAY_A2A_ENABLED=false ``` +Use RBAC policies to revoke access to prompts, resources, or tools you do not +intend to exposeβ€”these surfaces are always mounted but can be hidden from end +users by removing the corresponding permissions. + ### 2. Enable Authentication & Security ```bash # Configure strong authentication -MCPGATEWAY_AUTH_ENABLED=true -MCPGATEWAY_AUTH_USERNAME=custom-username # Change from default -MCPGATEWAY_AUTH_PASSWORD=strong-password-here # Use secrets manager +AUTH_REQUIRED=true +BASIC_AUTH_USER=custom-username # Change from default +BASIC_AUTH_PASSWORD=strong-password-here # Use secrets manager # Platform admin user (auto-created during bootstrap) PLATFORM_ADMIN_EMAIL=admin@yourcompany.com # Change from default @@ -310,25 +313,29 @@ Applications consuming MCP Gateway data must: # Core Security MCPGATEWAY_UI_ENABLED=false # Must be false in production MCPGATEWAY_ADMIN_API_ENABLED=false # Must be false in production -MCPGATEWAY_AUTH_ENABLED=true # Enable authentication -MCPGATEWAY_AUTH_USERNAME=custom-user # Change from default -MCPGATEWAY_AUTH_PASSWORD= # Use secrets manager +AUTH_REQUIRED=true # Enforce auth for every request +BASIC_AUTH_USER=custom-user # Change from default +BASIC_AUTH_PASSWORD= # Use secrets manager or secret store # Feature Flags (disable unused features) -MCPGATEWAY_ENABLE_ROOTS=false -MCPGATEWAY_ENABLE_PROMPTS=false -MCPGATEWAY_ENABLE_RESOURCES=false +MCPGATEWAY_BULK_IMPORT_ENABLED=false +MCPGATEWAY_A2A_ENABLED=false # Network Security -MCPGATEWAY_CORS_ALLOWED_ORIGINS=https://your-domain.com -MCPGATEWAY_RATE_LIMIT_ENABLED=true -MCPGATEWAY_RATE_LIMIT_PER_MINUTE=100 +CORS_ENABLED=true +ALLOWED_ORIGINS=https://your-domain.com +SECURITY_HEADERS_ENABLED=true # Logging (no sensitive data) -MCPGATEWAY_LOG_LEVEL=INFO # Not DEBUG in production -MCPGATEWAY_LOG_SENSITIVE_DATA=false # Never log sensitive data +LOG_LEVEL=INFO # Avoid DEBUG in production +LOG_TO_FILE=false # Disable file logging unless required +LOG_ROTATION_ENABLED=false # Enable only when log files are needed ``` +> **Rate limiting:** MCP Gateway does not ship a built-in global rate limiter. Enforce +> request throttling at an upstream ingress (NGINX, Envoy, API gateway) before traffic +> reaches the service. + ## πŸš€ Deployment Architecture ### Recommended Production Architecture diff --git a/docs/docs/manage/ui-customization.md b/docs/docs/manage/ui-customization.md index ec1eee336..eeb19123e 100644 --- a/docs/docs/manage/ui-customization.md +++ b/docs/docs/manage/ui-customization.md @@ -1,1522 +1,179 @@ # Customizing the Admin UI -The MCP Gateway Admin UI provides extensive customization options to tailor the interface to your organization's needs and preferences. This guide covers theme customization, layout configuration, user preferences, and accessibility settings. +The Admin experience is shipped as a Jinja template (`mcpgateway/templates/admin.html`) +with supporting assets in `mcpgateway/static/`. It uses **HTMX** for +request/response swaps, **Alpine.js** for light-weight reactivity, and the +Tailwind CDN for styling. There are no environment-variable knobs for colors or +layoutβ€”the way to customise it is to edit those files (or layer overrides during +deployment). -## Overview - -The Admin UI is built with modern web technologies (HTMX, Alpine.js, and Tailwind CSS) that enable dynamic customization without page refreshes. All customization settings are persisted locally and can be exported for sharing across teams. - -## Theme Customization - -### Dark/Light Mode - -The Admin UI includes built-in support for dark and light themes that automatically persist your preference: - -```javascript -// Theme is automatically saved to localStorage -localStorage.setItem('theme', 'dark'); // or 'light' -``` - -To toggle between themes programmatically: - -```html - - -``` - -### Custom Color Schemes - -You can customize the color palette by modifying CSS variables in your custom stylesheet: - -```css -/* custom-theme.css */ -:root { - /* Light theme colors */ - --color-primary: #3b82f6; - --color-secondary: #10b981; - --color-accent: #f59e0b; - --color-background: #ffffff; - --color-surface: #f3f4f6; - --color-text: #1f2937; - --color-text-muted: #6b7280; -} - -[data-theme="dark"] { - /* Dark theme colors */ - --color-primary: #60a5fa; - --color-secondary: #34d399; - --color-accent: #fbbf24; - --color-background: #111827; - --color-surface: #1f2937; - --color-text: #f9fafb; - --color-text-muted: #9ca3af; -} -``` - -To apply custom themes, add your stylesheet to the Admin UI configuration: - -```python -# In your mcpgateway configuration -MCPGATEWAY_ADMIN_CUSTOM_CSS = "/static/custom-theme.css" -``` - -### Brand Customization - -#### Logo and Icons - -Replace the default logo with your organization's branding: - -```python -# Environment variables for branding -MCPGATEWAY_ADMIN_LOGO_URL = "/static/company-logo.svg" -MCPGATEWAY_ADMIN_FAVICON_URL = "/static/favicon.ico" -MCPGATEWAY_ADMIN_TITLE = "Your Company MCP Gateway" -``` - -#### Custom Icons for Servers and Tools - -Define custom icons for different server types and tools: - -```json -{ - "server_icons": { - "database": "database-icon.svg", - "api": "api-icon.svg", - "file": "file-icon.svg" - }, - "tool_icons": { - "search": "magnifying-glass.svg", - "create": "plus-circle.svg", - "delete": "trash.svg" - } -} -``` - -## Layout Configuration - -### Panel Management - -The Admin UI supports flexible panel arrangements with drag-and-drop functionality: - -```javascript -// Enable panel customization -const panelConfig = { - virtualServers: { - visible: true, - order: 1, - width: 'full' - }, - tools: { - visible: true, - order: 2, - width: 'half' - }, - resources: { - visible: true, - order: 3, - width: 'half' - }, - prompts: { - visible: false, - order: 4, - width: 'full' - } -}; - -// Save layout preferences -localStorage.setItem('panel-layout', JSON.stringify(panelConfig)); -``` - -### Section Visibility - -Control which sections appear in the Admin UI: - -```python -# Configure visible sections via environment variables -MCPGATEWAY_ADMIN_SHOW_SERVERS = true -MCPGATEWAY_ADMIN_SHOW_TOOLS = true -MCPGATEWAY_ADMIN_SHOW_RESOURCES = true -MCPGATEWAY_ADMIN_SHOW_PROMPTS = false -MCPGATEWAY_ADMIN_SHOW_GATEWAYS = true -MCPGATEWAY_ADMIN_SHOW_METRICS = true -``` - -### Widget Dashboard - -Create custom dashboards with configurable widgets: - -```javascript -// Widget configuration example -const dashboardWidgets = [ - { - id: 'server-status', - type: 'status-card', - position: { x: 0, y: 0, w: 4, h: 2 }, - config: { - title: 'Server Status', - refreshInterval: 5000 - } - }, - { - id: 'recent-tools', - type: 'list', - position: { x: 4, y: 0, w: 4, h: 3 }, - config: { - title: 'Recently Used Tools', - limit: 10 - } - }, - { - id: 'metrics-chart', - type: 'chart', - position: { x: 0, y: 2, w: 8, h: 4 }, - config: { - title: 'Request Metrics', - chartType: 'line', - dataSource: '/api/metrics' - } - } -]; -``` - -## User Preferences - -### Profile Management - -User profiles store personal customization settings: - -```javascript -// User profile structure -const userProfile = { - username: 'admin', - preferences: { - theme: 'dark', - language: 'en', - fontSize: 'medium', - highContrast: false, - reducedMotion: false, - keyboardShortcuts: true - }, - layout: { - // Panel configuration - }, - quickActions: [ - 'create-server', - 'refresh-tools', - 'export-config' - ] -}; - -// Save profile -localStorage.setItem('user-profile', JSON.stringify(userProfile)); -``` - -### Import/Export Settings - -Export and share configuration across teams: - -```javascript -// Export current settings -function exportSettings() { - const settings = { - profile: JSON.parse(localStorage.getItem('user-profile')), - theme: localStorage.getItem('theme'), - layout: JSON.parse(localStorage.getItem('panel-layout')), - widgets: JSON.parse(localStorage.getItem('dashboard-widgets')) - }; - - const blob = new Blob([JSON.stringify(settings, null, 2)], - { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'mcpgateway-ui-settings.json'; - a.click(); -} - -// Import settings -function importSettings(file) { - const reader = new FileReader(); - reader.onload = function(e) { - const settings = JSON.parse(e.target.result); - - // Apply imported settings - if (settings.profile) { - localStorage.setItem('user-profile', - JSON.stringify(settings.profile)); - } - if (settings.theme) { - localStorage.setItem('theme', settings.theme); - } - if (settings.layout) { - localStorage.setItem('panel-layout', - JSON.stringify(settings.layout)); - } - - // Reload UI to apply changes - location.reload(); - }; - reader.readAsText(file); -} -``` - -### Quick Actions and Shortcuts - -Configure frequently used actions for quick access: - -```javascript -// Define keyboard shortcuts -const keyboardShortcuts = { - 'ctrl+n': 'createNewServer', - 'ctrl+r': 'refreshAll', - 'ctrl+/': 'toggleSearch', - 'ctrl+d': 'toggleTheme', - 'ctrl+,': 'openSettings', - 'esc': 'closeModal' -}; - -// Quick action toolbar configuration -const quickActions = [ - { - id: 'create-server', - label: 'New Server', - icon: 'plus', - action: () => openModal('create-server') - }, - { - id: 'refresh-tools', - label: 'Refresh Tools', - icon: 'refresh', - action: () => refreshToolList() - } -]; -``` - -## Accessibility Options - -### High Contrast Mode - -Enable high contrast for better visibility: - -```css -/* High contrast mode styles */ -[data-high-contrast="true"] { - --color-contrast-ratio: 7:1; - --border-width: 2px; - - /* Stronger colors for better contrast */ - --color-primary: #0066cc; - --color-secondary: #008844; - --color-danger: #cc0000; - --color-warning: #ff6600; - - /* Enhanced borders */ - border-width: var(--border-width); - outline-width: 2px; -} -``` - -### Font Size Adjustments - -Support dynamic font sizing: - -```javascript -// Font size preferences -const fontSizeOptions = { - small: '14px', - medium: '16px', - large: '18px', - xlarge: '20px' -}; - -function setFontSize(size) { - document.documentElement.style.setProperty('--base-font-size', - fontSizeOptions[size]); - localStorage.setItem('font-size', size); -} -``` - -### Keyboard Navigation - -Full keyboard navigation support: - -```javascript -// Enhanced keyboard navigation -document.addEventListener('keydown', (e) => { - // Tab navigation between sections - if (e.key === 'Tab') { - const focusableElements = document.querySelectorAll( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' - ); - // Handle focus management - } - - // Arrow key navigation in lists - if (e.key.startsWith('Arrow')) { - const currentItem = document.activeElement; - const items = Array.from(currentItem.parentElement.children); - // Navigate through list items - } -}); -``` - -### Screen Reader Support - -Ensure proper ARIA labels and descriptions: - -```html - -
-

Virtual Servers

-
-
- -
-
-
- - -
- Server created successfully -
-``` - -## Mobile and Responsive Design - -### Touch-Friendly Interface - -Optimize for touch interactions: - -```css -/* Touch-friendly buttons and controls */ -@media (pointer: coarse) { - button, .clickable { - min-height: 44px; - min-width: 44px; - padding: 12px; - } - - /* Increased spacing for touch targets */ - .tool-list > * { - margin-bottom: 8px; - } -} -``` - -### Mobile-Specific Layouts - -Responsive layout configurations: - -```css -/* Mobile layout adjustments */ -@media (max-width: 768px) { - /* Stack panels vertically on mobile */ - .panel-container { - display: flex; - flex-direction: column; - } - - /* Hide less critical sections */ - .desktop-only { - display: none; - } - - /* Collapsible navigation */ - .nav-menu { - position: fixed; - transform: translateX(-100%); - transition: transform 0.3s; - } - - .nav-menu.open { - transform: translateX(0); - } -} -``` - -### Progressive Web App Features - -Enable PWA capabilities for mobile users: - -```json -{ - "name": "MCP Gateway Admin", - "short_name": "MCP Admin", - "description": "Admin interface for MCP Gateway", - "start_url": "/admin", - "display": "standalone", - "theme_color": "#3b82f6", - "background_color": "#ffffff", - "icons": [ - { - "src": "/static/icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/static/icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} -``` - -## Localization Support - -### Multi-Language Configuration - -Support multiple languages in the UI: - -```javascript -// Language configuration -const translations = { - en: { - 'servers.title': 'Virtual Servers', - 'servers.create': 'Create Server', - 'servers.empty': 'No servers configured' - }, - es: { - 'servers.title': 'Servidores Virtuales', - 'servers.create': 'Crear Servidor', - 'servers.empty': 'No hay servidores configurados' - }, - fr: { - 'servers.title': 'Serveurs Virtuels', - 'servers.create': 'CrΓ©er un Serveur', - 'servers.empty': 'Aucun serveur configurΓ©' - } -}; - -// Apply translations -function setLanguage(lang) { - const t = translations[lang]; - document.querySelectorAll('[data-i18n]').forEach(el => { - const key = el.dataset.i18n; - if (t[key]) { - el.textContent = t[key]; - } - }); - localStorage.setItem('language', lang); -} -``` - -### RTL Support - -Support for right-to-left languages: - -```css -/* RTL language support */ -[dir="rtl"] { - /* Flip layout direction */ - .panel-container { - flex-direction: row-reverse; - } - - /* Adjust text alignment */ - .text-left { - text-align: right; - } - - /* Mirror icons */ - .icon-arrow { - transform: scaleX(-1); - } -} -``` - -## Advanced Customization - -### Custom Plugins - -Extend the Admin UI with custom plugins: - -```javascript -// Plugin registration -class CustomPlugin { - constructor(config) { - this.name = config.name; - this.version = config.version; - } - - init() { - // Add custom functionality - this.registerCustomPanel(); - this.addCustomMenuItems(); - } - - registerCustomPanel() { - const panel = document.createElement('div'); - panel.className = 'custom-panel'; - panel.innerHTML = this.renderPanel(); - document.querySelector('#panels').appendChild(panel); - } - - renderPanel() { - return ` -
-

${this.name}

- -
- `; - } -} - -// Register plugin -const plugin = new CustomPlugin({ - name: 'Custom Analytics', - version: '1.0.0' -}); -plugin.init(); -``` - -### Custom CSS Framework Integration - -Integrate alternative CSS frameworks: - -```html - - - - - -``` - -### API Extensions - -Add custom API endpoints for UI features: - -```python -# Custom API endpoint for UI preferences -from fastapi import APIRouter, Depends -from mcpgateway.auth import get_current_user - -ui_router = APIRouter(prefix="/api/ui") - -@ui_router.get("/preferences") -async def get_preferences(user = Depends(get_current_user)): - """Get user UI preferences""" - return { - "theme": user.preferences.get("theme", "light"), - "layout": user.preferences.get("layout", {}), - "language": user.preferences.get("language", "en") - } - -@ui_router.post("/preferences") -async def save_preferences(preferences: dict, - user = Depends(get_current_user)): - """Save user UI preferences""" - user.preferences.update(preferences) - # Save to database - return {"status": "saved"} -``` - -## Performance Optimization - -### Lazy Loading - -Implement lazy loading for better performance: - -```javascript -// Lazy load panels -const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const panel = entry.target; - loadPanelContent(panel.dataset.panelId); - observer.unobserve(panel); - } - }); -}); - -document.querySelectorAll('.lazy-panel').forEach(panel => { - observer.observe(panel); -}); -``` - -### Caching Strategies - -Cache UI preferences and data: - -```javascript -// Service Worker for offline support -self.addEventListener('install', (event) => { - event.waitUntil( - caches.open('ui-v1').then((cache) => { - return cache.addAll([ - '/admin', - '/static/admin.css', - '/static/admin.js', - '/static/icons/' - ]); - }) - ); -}); - -// Cache API responses -const cacheAPI = async (url, data) => { - const cache = await caches.open('api-cache'); - const response = new Response(JSON.stringify(data)); - await cache.put(url, response); -}; -``` - -## Container CSS Overrides - -When running MCP Gateway in a Docker container, you can override the default CSS by mounting custom stylesheets. The Admin UI CSS is located at `/app/mcpgateway/static/admin.css` inside the container. - -### Mounting Custom CSS - -To override the default CSS when running the container: - -```bash -# Create a local directory for custom styles -mkdir -p ./custom-ui - -# Create your custom CSS file -cat > ./custom-ui/admin.css << 'EOF' -/* Custom theme overrides */ -:root { - --color-primary: #your-brand-color; - --color-secondary: #your-secondary-color; -} - -/* Additional custom styles */ -.admin-header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -} -EOF - -# Run container with custom CSS mounted -docker run -d --name mcpgateway \ - -p 4444:4444 \ - -v $(pwd)/custom-ui/admin.css:/app/mcpgateway/static/admin.css:ro \ - -v $(pwd)/data:/data \ - -e MCPGATEWAY_UI_ENABLED=true \ - -e MCPGATEWAY_ADMIN_API_ENABLED=true \ - -e HOST=0.0.0.0 \ - -e JWT_SECRET_KEY=my-test-key \ - -e PLATFORM_ADMIN_EMAIL=admin@example.com \ - -e PLATFORM_ADMIN_PASSWORD=changeme \ - -e PLATFORM_ADMIN_FULL_NAME="Platform Administrator" \ - ghcr.io/MCP-Mirror/mcpgateway:latest -``` - -### Mounting Multiple Static Assets - -To override multiple static files (CSS, JavaScript, images): - -```bash -# Create custom static directory structure -mkdir -p ./custom-static -cp -r /path/to/original/mcpgateway/static/* ./custom-static/ - -# Modify files as needed -vim ./custom-static/admin.css -vim ./custom-static/admin.js - -# Mount entire static directory -docker run -d --name mcpgateway \ - -p 4444:4444 \ - -v $(pwd)/custom-static:/app/mcpgateway/static:ro \ - -v $(pwd)/data:/data \ - -e MCPGATEWAY_UI_ENABLED=true \ - -e MCPGATEWAY_ADMIN_API_ENABLED=true \ - -e HOST=0.0.0.0 \ - -e JWT_SECRET_KEY=my-test-key \ - -e PLATFORM_ADMIN_EMAIL=admin@example.com \ - -e PLATFORM_ADMIN_PASSWORD=changeme \ - -e PLATFORM_ADMIN_FULL_NAME="Platform Administrator" \ - ghcr.io/MCP-Mirror/mcpgateway:latest -``` - -### Docker Compose with Custom CSS - -Using Docker Compose for easier management: - -```yaml -# docker-compose.yml -version: '3.8' - -services: - mcpgateway: - image: ghcr.io/MCP-Mirror/mcpgateway:latest - container_name: mcpgateway - restart: unless-stopped - ports: - - "4444:4444" - volumes: - # Mount custom CSS file - - ./custom-ui/admin.css:/app/mcpgateway/static/admin.css:ro - # Or mount entire static directory - # - ./custom-static:/app/mcpgateway/static:ro - - # Mount data directory for persistence - - ./data:/data - - # Optional: Mount custom favicon and JavaScript - - ./custom-ui/favicon.ico:/app/mcpgateway/static/favicon.ico:ro - - ./custom-ui/admin.js:/app/mcpgateway/static/admin.js:ro - environment: - - MCPGATEWAY_UI_ENABLED=true - - MCPGATEWAY_ADMIN_API_ENABLED=true - - HOST=0.0.0.0 - - PORT=4444 - - JWT_SECRET_KEY=${JWT_SECRET_KEY:-change-me-in-production} - - DATABASE_URL=sqlite:////data/mcp.db -``` - -### CSS File Locations - -The default static files in the container are located at: - -- **CSS**: `/app/mcpgateway/static/admin.css` -- **JavaScript**: `/app/mcpgateway/static/admin.js` -- **Favicon**: `/app/mcpgateway/static/favicon.ico` - -### Custom CSS Best Practices - -When creating custom CSS overrides: - -1. **Preserve Core Functionality**: Don't remove critical styles that affect functionality -2. **Use CSS Variables**: Override CSS custom properties for consistent theming -3. **Test Responsiveness**: Ensure custom styles work on mobile devices -4. **Maintain Accessibility**: Keep contrast ratios and focus indicators - -Example custom CSS file structure: - -```css -/* custom-ui/admin.css */ - -/* Import original CSS if needed */ -@import url('/static/admin.css'); - -/* Override CSS variables */ -:root { - /* Brand colors */ - --color-primary: #1e40af; - --color-primary-hover: #1e3a8a; - --color-secondary: #059669; - - /* Custom spacing */ - --spacing-unit: 0.5rem; - --border-radius: 0.375rem; - - /* Custom fonts */ - --font-family: 'Inter', system-ui, -apple-system, sans-serif; -} - -/* Dark mode overrides */ -[data-theme="dark"] { - --color-primary: #3b82f6; - --color-background: #0f172a; - --color-surface: #1e293b; -} - -/* Component-specific overrides */ -.admin-header { - background: var(--color-primary); - padding: calc(var(--spacing-unit) * 3); -} - -.server-card { - border-radius: var(--border-radius); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - -/* Custom animations */ -@keyframes fadeIn { - from { opacity: 0; transform: translateY(-10px); } - to { opacity: 1; transform: translateY(0); } -} - -.panel { - animation: fadeIn 0.3s ease-out; -} -``` - -### Kubernetes ConfigMap for CSS - -For Kubernetes deployments, use a ConfigMap: - -```yaml -# configmap-custom-css.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: mcpgateway-custom-css - namespace: default -data: - admin.css: | - :root { - --color-primary: #2563eb; - --color-secondary: #10b981; - } - /* Additional custom styles */ --- -# deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: mcpgateway -spec: - template: - spec: - containers: - - name: mcpgateway - image: ghcr.io/MCP-Mirror/mcpgateway:latest - volumeMounts: - - name: custom-css - mountPath: /app/mcpgateway/static/admin.css - subPath: admin.css - readOnly: true - volumes: - - name: custom-css - configMap: - name: mcpgateway-custom-css -``` - -### Verifying Custom CSS - -To verify your custom CSS is loaded: - -1. Access the Admin UI at `http://localhost:4444/admin` -2. Open browser developer tools (F12) -3. Check the Network tab for `admin.css` -4. Inspect elements to see applied styles -5. Look for your custom CSS variables in the computed styles - -### Troubleshooting Container CSS Issues - -Common issues and solutions: - -1. **CSS not updating**: Clear browser cache or use hard refresh (Ctrl+Shift+R) -2. **Permission denied**: Ensure mounted files are readable (`chmod 644 admin.css`) -3. **Path not found**: Verify the container path is exactly `/app/mcpgateway/static/` -4. **Styles not applying**: Check CSS specificity and use `!important` if necessary - -## Configuration Examples - -### Environment Variables - -Complete list of UI customization environment variables: - -```bash -# Theme and Appearance -MCPGATEWAY_ADMIN_THEME=dark -MCPGATEWAY_ADMIN_HIGH_CONTRAST=false -MCPGATEWAY_ADMIN_FONT_SIZE=medium -MCPGATEWAY_ADMIN_ANIMATIONS=true - -# Branding -MCPGATEWAY_ADMIN_TITLE="Custom MCP Gateway" -MCPGATEWAY_ADMIN_LOGO_URL="/static/logo.svg" -MCPGATEWAY_ADMIN_FAVICON_URL="/static/favicon.ico" -MCPGATEWAY_ADMIN_CUSTOM_CSS="/static/custom.css" - -# Layout -MCPGATEWAY_ADMIN_DEFAULT_LAYOUT=dashboard -MCPGATEWAY_ADMIN_SHOW_SERVERS=true -MCPGATEWAY_ADMIN_SHOW_TOOLS=true -MCPGATEWAY_ADMIN_SHOW_RESOURCES=true -MCPGATEWAY_ADMIN_SHOW_PROMPTS=true -MCPGATEWAY_ADMIN_SHOW_METRICS=true - -# Features -MCPGATEWAY_ADMIN_ENABLE_SEARCH=true -MCPGATEWAY_ADMIN_ENABLE_EXPORT=true -MCPGATEWAY_ADMIN_ENABLE_SHORTCUTS=true -MCPGATEWAY_ADMIN_ENABLE_DRAG_DROP=true - -# Localization -MCPGATEWAY_ADMIN_DEFAULT_LANGUAGE=en -MCPGATEWAY_ADMIN_AVAILABLE_LANGUAGES=en,es,fr,de,ja - -# Performance -MCPGATEWAY_ADMIN_LAZY_LOAD=true -MCPGATEWAY_ADMIN_CACHE_DURATION=3600 -MCPGATEWAY_ADMIN_UPDATE_INTERVAL=5000 -``` - -### Docker Configuration - -Mount custom configuration in Docker: - -```yaml -# docker-compose.yml -version: '3.8' - -services: - mcpgateway: - image: mcpgateway:latest - environment: - - MCPGATEWAY_ADMIN_THEME=dark - - MCPGATEWAY_ADMIN_TITLE=My Custom Gateway - volumes: - - ./custom-ui:/app/static/custom:ro - - ./ui-config.json:/app/config/ui.json:ro - ports: - - "4444:4444" -``` - -## Troubleshooting - -### Common Issues - -1. **Theme not persisting**: Check browser localStorage permissions -2. **Custom CSS not loading**: Verify file path and permissions -3. **Layout reset on refresh**: Ensure localStorage is not being cleared -4. **Mobile layout issues**: Check viewport meta tag in HTML - -### Debug Mode - -Enable debug mode for UI troubleshooting: - -```javascript -// Enable UI debug mode -localStorage.setItem('ui-debug', 'true'); - -// Debug logging -if (localStorage.getItem('ui-debug') === 'true') { - console.log('Panel configuration:', panelConfig); - console.log('Theme:', currentTheme); - console.log('User preferences:', userProfile); -} -``` - -## Building Your Own Custom UI - -The MCP Gateway provides comprehensive REST APIs that enable you to build completely custom user interfaces. This section covers API endpoints, authentication, real-time communication, and how to disable the built-in UI. - -### Disabling the Built-in UI - -When using a custom UI, you can disable the default Admin UI: - -```bash -# Disable built-in UI completely -MCPGATEWAY_UI_ENABLED=false # Disables static file serving and root redirect -MCPGATEWAY_ADMIN_API_ENABLED=false # Disables admin-specific API endpoints - -# Or keep APIs but disable UI -MCPGATEWAY_UI_ENABLED=false # Disable UI only -MCPGATEWAY_ADMIN_API_ENABLED=true # Keep admin APIs for custom UI -``` - -When the UI is disabled: -- Root path (`/`) returns API information instead of redirecting to `/admin` -- Static files (`/static/*`) are not served -- Admin UI routes (`/admin/*`) return 404 -- All API endpoints remain accessible (unless `MCPGATEWAY_ADMIN_API_ENABLED=false`) - -### API Documentation - -The gateway provides interactive API documentation: - -- **`/docs`** - Swagger UI interactive documentation -- **`/redoc`** - ReDoc API documentation -- **`/openapi.json`** - OpenAPI 3.0 schema (for code generation) - -Access the Swagger UI at `http://localhost:4444/docs` to explore all available endpoints interactively. - -### Core API Endpoints - -#### Virtual Server Management -```bash -GET /servers # List all virtual servers -POST /servers # Create new virtual server -GET /servers/{id} # Get specific server details -PUT /servers/{id} # Update server configuration -DELETE /servers/{id} # Delete virtual server -``` - -#### Tool Registry -```bash -GET /tools # List all available tools -POST /tools # Register new tool -GET /tools/{id} # Get tool details -PUT /tools/{id} # Update tool -DELETE /tools/{id} # Remove tool -POST /tools/{id}/invoke # Invoke a specific tool -``` - -#### Resource Management -```bash -GET /resources # List all resources -POST /resources # Create new resource -GET /resources/{id} # Get resource details -PUT /resources/{id} # Update resource -DELETE /resources/{id} # Delete resource -GET /resources/{id}/read # Read resource content -``` - -#### Prompt Templates -```bash -GET /prompts # List all prompts -POST /prompts # Create new prompt -GET /prompts/{id} # Get prompt details -PUT /prompts/{id} # Update prompt -DELETE /prompts/{id} # Delete prompt -POST /prompts/{id}/execute # Execute prompt -``` - -#### Gateway Federation -```bash -GET /gateways # List peer gateways -POST /gateways # Register new gateway -GET /gateways/{id} # Get gateway details -DELETE /gateways/{id} # Remove gateway -GET /gateways/{id}/health # Check gateway health -``` - -#### System Information -```bash -GET /version # System diagnostics and metrics -GET /health # Health check endpoint -GET /ready # Readiness check -GET /metrics # Prometheus-compatible metrics -``` - -#### MCP Protocol Operations -```bash -POST / # JSON-RPC endpoint for MCP protocol -POST /rpc # Alternative JSON-RPC endpoint -POST /protocol/initialize # Initialize MCP session -POST /protocol/ping # Ping for keepalive -POST /protocol/notify # Send notifications -``` - -### Authentication - -#### Generate JWT Token -```bash -# Generate a JWT token for API access -python3 -m mcpgateway.utils.create_jwt_token \ - --username admin \ - --exp 10080 \ - --secret $JWT_SECRET_KEY -# Export for use in API calls -export TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token \ - --username admin --exp 0 --secret my-test-key) -``` - -#### Using Authentication in API Calls -```bash -# Bearer token authentication (recommended) -curl -H "Authorization: Bearer $TOKEN" \ - http://localhost:4444/servers - -# Basic authentication (alternative) -curl -u admin:changeme \ - http://localhost:4444/servers - -# Cookie-based (for browser sessions) -curl -c cookies.txt -X POST \ - -d '{"username":"admin","password":"changeme"}' \ - http://localhost:4444/auth/login -``` - -### Real-time Communication - -#### Server-Sent Events (SSE) -```javascript -// Connect to SSE endpoint for real-time updates -const eventSource = new EventSource( - `/servers/${serverId}/sse`, - { headers: { 'Authorization': `Bearer ${token}` } } -); - -eventSource.onmessage = (event) => { - const data = JSON.parse(event.data); - console.log('Server update:', data); -}; - -eventSource.addEventListener('tool-invoked', (event) => { - console.log('Tool invoked:', JSON.parse(event.data)); -}); -``` - -#### WebSocket Connection -```javascript -// WebSocket for bidirectional communication -const ws = new WebSocket(`ws://localhost:4444/ws`); - -ws.onopen = () => { - // Send authentication - ws.send(JSON.stringify({ - type: 'auth', - token: token - })); +## Feature Flags to Enable the UI - // Subscribe to updates - ws.send(JSON.stringify({ - jsonrpc: '2.0', - method: 'subscribe', - params: { topics: ['tools', 'servers'] }, - id: 1 - })); -}; - -ws.onmessage = (event) => { - const message = JSON.parse(event.data); - console.log('WebSocket message:', message); -}; -``` +Ensure the Admin interface is turned on before making changes: -#### HTTP Streaming ```bash -# Stream responses using HTTP chunked encoding -curl -N -H "Authorization: Bearer $TOKEN" \ - -H "Accept: text/event-stream" \ - http://localhost:4444/servers/stream +MCPGATEWAY_UI_ENABLED=true +MCPGATEWAY_ADMIN_API_ENABLED=true ``` -### Building a React-Based Custom UI - -Example React application structure: - -```jsx -// api/client.js -class MCPGatewayClient { - constructor(baseUrl, token) { - this.baseUrl = baseUrl; - this.token = token; - } - - async fetchServers() { - const response = await fetch(`${this.baseUrl}/servers`, { - headers: { - 'Authorization': `Bearer ${this.token}`, - 'Content-Type': 'application/json' - } - }); - return response.json(); - } - - async createServer(config) { - const response = await fetch(`${this.baseUrl}/servers`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${this.token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(config) - }); - return response.json(); - } - - connectSSE(serverId, onMessage) { - const eventSource = new EventSource( - `${this.baseUrl}/servers/${serverId}/sse`, - { - headers: { - 'Authorization': `Bearer ${this.token}` - } - } - ); +The only other related tuning knob is: - eventSource.onmessage = onMessage; - return eventSource; - } -} +- `MCPGATEWAY_UI_TOOL_TEST_TIMEOUT` (milliseconds) – timeout for the "Test Tool" + action triggered from the Tools catalog. -// components/ServerDashboard.jsx -import React, { useState, useEffect } from 'react'; -import { MCPGatewayClient } from '../api/client'; +Every other visual/behaviour change is code-driven. -export function ServerDashboard() { - const [servers, setServers] = useState([]); - const client = new MCPGatewayClient( - process.env.REACT_APP_GATEWAY_URL, - process.env.REACT_APP_TOKEN - ); - - useEffect(() => { - // Load initial data - client.fetchServers().then(setServers); - - // Subscribe to real-time updates - const sse = client.connectSSE('all', (event) => { - const update = JSON.parse(event.data); - if (update.type === 'server-update') { - setServers(prev => - prev.map(s => s.id === update.server.id - ? update.server : s) - ); - } - }); - - return () => sse.close(); - }, []); - - return ( -
-

MCP Gateway Servers

-
- {servers.map(server => ( - - ))} -
-
- ); -} -``` - -### Python Custom UI Example - -```python -# custom_ui_client.py -import requests -import sseclient -from typing import Dict, List - -class MCPGatewayClient: - def __init__(self, base_url: str, token: str): - self.base_url = base_url - self.headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json" - } - - def list_servers(self) -> List[Dict]: - """List all virtual servers""" - response = requests.get( - f"{self.base_url}/servers", - headers=self.headers - ) - response.raise_for_status() - return response.json() - - def create_server(self, config: Dict) -> Dict: - """Create a new virtual server""" - response = requests.post( - f"{self.base_url}/servers", - json=config, - headers=self.headers - ) - response.raise_for_status() - return response.json() - - def invoke_tool(self, tool_id: str, params: Dict) -> Dict: - """Invoke a tool""" - response = requests.post( - f"{self.base_url}/tools/{tool_id}/invoke", - json={"params": params}, - headers=self.headers - ) - response.raise_for_status() - return response.json() - - def stream_events(self, server_id: str = "all"): - """Stream real-time events via SSE""" - response = requests.get( - f"{self.base_url}/servers/{server_id}/sse", - headers=self.headers, - stream=True - ) - client = sseclient.SSEClient(response) - for event in client.events(): - yield event - -# Example usage -if __name__ == "__main__": - client = MCPGatewayClient( - base_url="http://localhost:4444", - token="your-jwt-token" - ) - - # List servers - servers = client.list_servers() - print(f"Found {len(servers)} servers") - - # Stream events - for event in client.stream_events(): - print(f"Event: {event.event}, Data: {event.data}") -``` - -### TypeScript SDK Example - -```typescript -// mcp-gateway-sdk.ts -export interface Server { - id: string; - name: string; - description?: string; - tools: string[]; - resources: string[]; - status: 'active' | 'inactive'; -} - -export interface Tool { - id: string; - name: string; - description: string; - parameters: Record; -} - -export class MCPGatewaySDK { - constructor( - private baseUrl: string, - private token: string - ) {} - - private async request( - path: string, - options: RequestInit = {} - ): Promise { - const response = await fetch(`${this.baseUrl}${path}`, { - ...options, - headers: { - 'Authorization': `Bearer ${this.token}`, - 'Content-Type': 'application/json', - ...options.headers, - }, - }); - - if (!response.ok) { - throw new Error(`API Error: ${response.statusText}`); - } - - return response.json(); - } - - async getServers(): Promise { - return this.request('/servers'); - } - - async createServer(config: Partial): Promise { - return this.request('/servers', { - method: 'POST', - body: JSON.stringify(config), - }); - } +--- - async getTools(): Promise { - return this.request('/tools'); - } +## Recommended Editing Workflow + +1. Copy `.env.example` to `.env`, then set: + ```bash + DEV_MODE=true + RELOAD=true + ``` + This enables template + static reloads while you work. +2. Start the dev server: `make dev` (serves the UI at http://localhost:8000). +3. Edit any of the following and refresh your browser: + - `mcpgateway/templates/admin.html` + - `mcpgateway/static/admin.css` + - `mcpgateway/static/admin.js` + - Additional assets under `mcpgateway/static/` +4. Commit the customised files or prepare overrides for your deployment target + (see [Deploying Overrides](#deploying-overrides)). + +Tip: keep your changes on a dedicated branch so that rebase/merge with upstream +remains manageable. - async invokeTool( - toolId: string, - params: Record - ): Promise { - return this.request(`/tools/${toolId}/invoke`, { - method: 'POST', - body: JSON.stringify({ params }), - }); - } +--- - subscribeToEvents( - serverId: string = 'all', - onMessage: (event: MessageEvent) => void - ): EventSource { - const eventSource = new EventSource( - `${this.baseUrl}/servers/${serverId}/sse`, - { - headers: { - 'Authorization': `Bearer ${this.token}`, - }, - } - ); +## File Layout Reference - eventSource.onmessage = onMessage; +| Path | Description | +| --- | --- | +| `mcpgateway/templates/admin.html` | Single-page admin template containing header, navigation, tables, modals, metrics, etc. | +| `mcpgateway/static/admin.css` | Tailwind-friendly overrides (spinners, tooltips, table tweaks). | +| `mcpgateway/static/admin.js` | Behaviour helpers (form toggles, request utilities, validation). | +| `mcpgateway/static/images/` | Default logo, favicon, and imagery used in the UI. | - eventSource.onerror = (error) => { - console.error('SSE Error:', error); - }; +All static assets are served from `/static/` and respect `ROOT_PATH` when the +app is mounted behind a proxy. - return eventSource; - } -} -``` +--- -### CORS Configuration +## Branding Essentials + +### Document Title & Header +- Update the `` element and the main `<h1>` block near the top of + `admin.html` with your organisation's name. +- The secondary copy and links (Docs, GitHub star) live in the same header + sectionβ€”edit or remove them as needed. + +### Logo & Favicon +- Replace the default files in `mcpgateway/static/` (or add your own under + `static/images/`). +- Update the `<link rel="icon">` and `<img src="...">` references in + `admin.html` to point to your assets, e.g. + ```html + <link rel="icon" href="{{ root_path }}/static/images/company-favicon.ico" /> + <img src="{{ root_path }}/static/images/company-logo.svg" class="h-8" alt="Company" /> + ``` + +### Colors & Tailwind +- Tailwind is initialised in `admin.html` via `https://cdn.tailwindcss.com` with + `darkMode: "class"`. +- Add a custom config block to extend colours/fonts and swap utility classes, for example: + ```html + <script> + tailwind.config = { + darkMode: "class", + theme: { + extend: { + colors: { brand: "#1d4ed8", accent: "#f97316" }, + fontFamily: { display: ['"IBM Plex Sans"', 'sans-serif'] }, + }, + }, + }; + </script> + ``` +- For bespoke CSS (animations, overrides), append to `admin.css` or include a + new stylesheet in the `<head>`: + ```html + <link rel="stylesheet" href="{{ root_path }}/static/css/custom.css" /> + ``` + +### Theme Toggle +- The dark/light toggle persists a `darkMode` value in `localStorage`. Change the + default by altering the `x-data` initialiser in the `<html>` tag if you want to + default to dark: + ```html + x-data="{ darkMode: JSON.parse(localStorage.getItem('darkMode') || 'true') }" + ``` -For browser-based custom UIs, configure CORS: +--- -```bash -# Enable CORS for your custom UI domain -CORS_ENABLED=true -ALLOWED_ORIGINS=http://localhost:3000,https://my-custom-ui.com -``` +## Behaviour Customisation -### API Rate Limiting +- `admin.js` powers form helpers (e.g. locking the Tool URL field when MCP is + selected) and general UX‐polish. Append your scripts there or include a new JS + file at the end of `admin.html`. +- Use HTMX hooks (`htmx:beforeSwap`, `htmx:afterSwap`, etc.) if you need to + intercept requests. +- Alpine components live on each panel (look for `x-data="tabs"`, etc.)β€”extend + them by adding properties/methods in the `x-data` object. +- Avoid writing raw `innerHTML` with user data to preserve the UI's XSS + protections; prefer `textContent`. +- Lazy-loaded sections (bulk import, A2A, teams, etc.) are clearly marked in the + templateβ€”remove panels you don't need. -When building custom UIs, be aware of rate limits: +--- -```python -# Rate limiting configuration -RATE_LIMIT_ENABLED=true -RATE_LIMIT_PER_MINUTE=60 -RATE_LIMIT_BURST=10 -``` +## Key Template Anchors -Handle rate limit responses: -```javascript -async function apiCall(url, options) { - const response = await fetch(url, options); +Search for these comments in `admin.html` when hunting for specific areas: - if (response.status === 429) { - const retryAfter = response.headers.get('Retry-After'); - console.log(`Rate limited. Retry after ${retryAfter} seconds`); - // Implement exponential backoff - await sleep(retryAfter * 1000); - return apiCall(url, options); - } +- `<!-- Navigation Tabs -->` – top-level tab buttons. +- `<!-- Status Cards -->` – summary cards for totals. +- `<!-- Servers Table -->`, `<!-- Tools Table -->`, `<!-- Resources Table -->`, etc. – per-resource CRUD grids. +- `<!-- Bulk Import Modal -->`, `<!-- Team Modal -->` – modal dialogs. +- `id="metadata-tracking"`, `id="a2a-agents"`, `id="team-management"` – advanced sections you can prune or reorder. - return response; -} -``` +Make your edits and refresh the browser to confirm behaviour. -### Monitoring Your Custom UI +--- -Track custom UI interactions: +## Deploying Overrides -```javascript -// Send custom metrics to the gateway -fetch('/metrics/custom', { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - metric: 'ui.page_view', - value: 1, - labels: { - page: 'dashboard', - user: 'admin' - } - }) -}); -``` +When packaging the gateway: -## Best Practices +- **Bake into the image** – copy customised templates/static files during the + container build. +- **Mount at runtime** – overlay files via volumes: + ```bash + docker run \ + -v $(pwd)/overrides/admin.html:/app/mcpgateway/templates/admin.html:ro \ + -v $(pwd)/overrides/static:/app/mcpgateway/static/custom:ro \ + ghcr.io/ibm/mcp-context-forge:0.7.0 + ``` + Then update template references to point at `static/custom/...`. +- **Fork + rebase** – maintain a thin fork that carries your branding patches. -1. **Test customizations** across different browsers and devices -2. **Backup configurations** before major changes -3. **Use version control** for custom CSS and JavaScript files -4. **Document custom changes** for team members -5. **Monitor performance** impact of customizations -6. **Follow accessibility guidelines** (WCAG 2.1 AA) -7. **Implement progressive enhancement** for better compatibility -8. **Use API versioning** when building custom UIs to handle future changes -9. **Implement proper error handling** for API failures -10. **Cache API responses** appropriately to reduce load +In Kubernetes, place customised assets in a ConfigMap/Secret and mount over the +default paths (`/app/mcpgateway/templates/admin.html`, `/app/mcpgateway/static/`). +Roll the deployment after changes so the pod picks up the new files. -## Related Documentation +--- -- [Admin UI Overview](../overview/ui.md) - Basic UI concepts and navigation -- [Security Configuration](./securing.md) - Securing the Admin UI -- [Performance Tuning](./tuning.md) - Optimizing UI performance -- [API Reference](https://ibm.github.io/mcp-context-forge/api/admin/) - Admin API endpoints +## Testing Checklist + +1. `make dev` – confirm the UI renders, tabs switch, and tables load as expected. +2. Optional: `pytest tests/playwright/ -k admin` – run UI smoke tests if you + altered interaction logic. +3. Verify in a staging/production-like environment that: + - Static assets resolve behind your proxy (`ROOT_PATH`/`APP_DOMAIN`). + - Authentication flows still succeed (basic + JWT). + - Any branding assets load quickly (serve them via CDN if heavy). +4. Document your customisations internally so future upgrades know which sections + were changed. diff --git a/docs/docs/using/mcpgateway-translate.md b/docs/docs/using/mcpgateway-translate.md index 70dd72021..99600239a 100644 --- a/docs/docs/using/mcpgateway-translate.md +++ b/docs/docs/using/mcpgateway-translate.md @@ -430,15 +430,23 @@ Consider using the full [MCP Gateway](../overview/index.md). ## Advanced Configuration -### Environment Variables +### Configuration -All command-line options can be set via environment variables: +`mcpgateway.translate` reads its configuration from command-line arguments +only, with one exception: the HTTP `Content-Type` header defaults to the +`FORGE_CONTENT_TYPE` environment variable (falls back to `application/json`). +If you want shell-friendly defaults, wrap the invocation with an alias or +script: ```bash -export MCPGATEWAY_PORT=9000 -export MCPGATEWAY_LOG_LEVEL=debug -export MCPGATEWAY_CORS_ORIGINS="http://localhost:3000" -python3 -m mcpgateway.translate --stdio "mcp-server" +alias translate-git='python3 -m mcpgateway.translate --stdio "uvx mcp-server-git" --host 127.0.0.1 --port 9000 --expose-sse' +translate-git +``` + +Optional: adjust the outbound content type once for your shell session: + +```bash +export FORGE_CONTENT_TYPE=application/json ``` ### Custom Headers