# ============================================================================= # ContextForge Configuration Example # ============================================================================= # NOTE ON SOURCES # Values below match Pydantic Settings defaults unless noted. # Most env vars below are Pydantic Settings (mcpgateway/config.py). # Some are runtime/launcher envs (shell scripts, entrypoint) or direct env reads # in modules (not part of Settings). Those sections are labeled explicitly. # ============================================================================= # REQUIRED SECRETS - Application will NOT start without these # ============================================================================= # # ⚠️ SECURITY WARNING: JWT_SECRET_KEY and AUTH_ENCRYPTION_SECRET are REQUIRED # in EVERY environment — including local development. # The application will refuse to start if either value is a placeholder # (__REPLACE_ME__), a known-weak default, shorter than 32 characters, # or has low entropy. Both conditions are enforced in every environment. # # Quickstart (fresh checkout): # 1. make install-dev # installs Python deps — does NOT touch .env # 2. make init-secrets # generates .env.secrets (prompts before overwrite) # 3. Copy values from .env.secrets into this file, OR: # make init-secrets-patch-env # patches secrets directly into .env (no copy needed) # # Docker Compose / CI (non-interactive): # python3 -m mcpgateway.scripts.init_secrets --patch-env .env # # or: python3 -m mcpgateway.scripts.init_secrets --force && \ # # grep -E '^(JWT_SECRET_KEY|AUTH_ENCRYPTION_SECRET|BASIC_AUTH_PASSWORD)=' .env.secrets >> .env # # What each path writes: # make init-secrets / --stdout / --force (writes .env.secrets): # - JWT_SECRET_KEY (REQUIRED — all environments) # - AUTH_ENCRYPTION_SECRET (REQUIRED — all environments) # - BASIC_AUTH_PASSWORD (for UI/API Basic Auth) # - PLATFORM_ADMIN_PASSWORD (for bootstrap admin account) # # make setup / make init-secrets-patch-env / --patch-env (in-place patch): # - JWT_SECRET_KEY (patched when placeholder or weak) # - AUTH_ENCRYPTION_SECRET (patched when placeholder or weak) # - BASIC_AUTH_PASSWORD (patched when "changeme" or placeholder; 18 bytes → 24 chars) # NOTE: PLATFORM_ADMIN_PASSWORD is NOT patched by this path — set it manually # or via helm/k8s secrets before production deployment. # # ============================================================================= # JWT secret used to sign tokens (REQUIRED — all environments, including development) # Generate with: python3 -m mcpgateway.scripts.init_secrets JWT_SECRET_KEY=__REPLACE_ME__run_init-secrets_before_starting # Passphrase used to encrypt stored auth secrets (REQUIRED — all environments, including development) # Generate with: python3 -m mcpgateway.scripts.init_secrets AUTH_ENCRYPTION_SECRET=__REPLACE_ME__run_init-secrets_before_starting # ============================================================================= # Environment Mode # ============================================================================= # Controls CORS, cookie security, and operational defaults — but NOT secret # enforcement. Placeholder/weak secrets are rejected in every environment. # - development: Relaxed CORS (localhost:3000/8080), insecure cookies, dev info # - staging: Production-like CORS and cookie defaults, staging domain # - production: Strict CORS (APP_DOMAIN only), secure cookies, no debug info # Default: development (if not set) ENVIRONMENT=development # ============================================================================= # Admin UI HTTP Basic Auth credentials # ============================================================================= # BASIC_AUTH_PASSWORD is patched to a strong value by make setup / make init-secrets-patch-env. # PLATFORM_ADMIN_PASSWORD must be set manually — it is not patched by the automated path. BASIC_AUTH_USER=admin BASIC_AUTH_PASSWORD=changeme # pragma: allowlist secret # ----------------------------------------------------------------------------- # CSRF Protection Configuration # ----------------------------------------------------------------------------- # Cross-Site Request Forgery protection for state-changing operations # Enable CSRF protection (default: true) CSRF_ENABLED=true # Secret used to sign CSRF tokens. If left unset, the application reuses # JWT_SECRET_KEY for this purpose. Set it explicitly so that the two keys can # be rotated independently. CSRF_SECRET_KEY= # HTTP header name for CSRF token CSRF_TOKEN_NAME=X-CSRF-Token # Cookie name for CSRF token CSRF_COOKIE_NAME=mcpgateway_csrf_token # CSRF token expiration time in seconds (default: 3600 = 1 hour) CSRF_TOKEN_EXPIRY=3600 # Set Secure flag on CSRF cookie (HTTPS only, default: true) CSRF_COOKIE_SECURE=true # SameSite attribute for CSRF cookie (Strict, Lax, or None) CSRF_COOKIE_SAMESITE=Strict # Set HttpOnly flag on CSRF cookie (false allows JavaScript to read for API calls) CSRF_COOKIE_HTTPONLY=false # Validate Referer header for CSRF protection (default: true) CSRF_CHECK_REFERER=true # Rotate CSRF token on user login for enhanced security (default: true) CSRF_ROTATE_ON_LOGIN=true # Additional trusted origins for CSRF validation (JSON array) CSRF_TRUSTED_ORIGINS=["http://localhost:4444","http://localhost:8080"] # Paths exempt from CSRF protection (JSON array) # WARNING: Setting this env var REPLACES the config.py default entirely — it does not append. # Leave unset to use the secure default maintained in mcpgateway/config.py (csrf_exempt_paths). # Only set explicitly if you need a fully custom list (you must include every path you want exempt). #CSRF_EXEMPT_PATHS=["/health","/auth/login","/auth/logout","/auth/email/login","/auth/email/register","/auth/email/forgot-password","/auth/email/reset-password","/admin","/admin/login","/admin/forgot-password","/admin/reset-password","/oauth/fetch-tools","/docs","/redoc","/openapi.json","/metrics","/mcp/","/sse","/message","/rpc","/api/metrics/","/toolops/","/tokens","/teams/","/llmchat/","/api/logs/","/_internal/mcp/"] # Bootstrap admin credentials (email auth) # PRODUCTION: Change these values PLATFORM_ADMIN_EMAIL=admin@example.com #PLATFORM_ADMIN_PASSWORD="< replace me >" DEFAULT_USER_PASSWORD=UserP@ssw0rd!2026Secure # Set to false in development to enable the platform admin bootstrap path. # Production default is true (requires admin user seeded in the database). # Uncomment in development if you rely on the platform-admin email match # instead of seeding the user via init-secrets. # (full docs → Authentication section) #REQUIRE_USER_IN_DB=false # ============================================================================= # Security Defaults (secure by default) # ============================================================================= # These settings are enabled by default for security. Only disable for backward # compatibility with legacy tokens that lack these claims. # Require JTI (JWT ID) claim in all tokens for revocation support # Tokens without JTI cannot be revoked before expiration REQUIRE_JTI=true # Require expiration (exp) claim in all tokens # Tokens without expiration never expire and pose a security risk REQUIRE_TOKEN_EXPIRATION=true # Disable public user self-registration (admin must create accounts) PUBLIC_REGISTRATION_ENABLED=false # Allow entities to have public visibility (set false to block in team scope) ALLOW_PUBLIC_VISIBILITY=true # Basic Auth is DISABLED by default for security - use JWT tokens instead # Only enable for backwards compatibility with legacy clients # API_ALLOW_BASIC_AUTH=false # ----------------------------------------------------------------------------- # SSRF Protection (Server-Side Request Forgery) # ----------------------------------------------------------------------------- # Prevents the gateway from being used to access internal resources or cloud # metadata services. Enabled by default with safe settings for dev/internal use. # Master switch for SSRF protection (default: true) # SSRF_PROTECTION_ENABLED=true # Allow localhost/loopback addresses (127.0.0.0/8, ::1) # Default: false (strict) # SSRF_ALLOW_LOCALHOST=false # Allow RFC 1918 private network addresses (10.x, 172.16-31.x, 192.168.x) # Default: false (strict). Use SSRF_ALLOWED_NETWORKS for explicit exceptions. # SSRF_ALLOW_PRIVATE_NETWORKS=false # Optional CIDR allowlist when SSRF_ALLOW_PRIVATE_NETWORKS=false # Only private IPs in these ranges are allowed. # SSRF_ALLOWED_NETWORKS=["10.20.0.0/16","192.168.50.0/24"] # Fail closed on DNS resolution errors (default: true) # URLs that cannot be resolved are rejected # SSRF_DNS_FAIL_CLOSED=true # Gateway Test Endpoint Security # # The /admin/gateways/test endpoint allows testing gateway connectivity. To prevent # using this endpoint as an open proxy (ICA_ContextForgeICACF-14), it enforces an # allowlist of approved hosts. # # GATEWAY_TEST_ALLOW_REGISTERED_ONLY: When true, only allows testing URLs that match # registered gateway base URLs in the database. When false, uses GATEWAY_TEST_ALLOWED_HOSTS # patterns. Default: true (most secure). # GATEWAY_TEST_ALLOW_REGISTERED_ONLY=true # # GATEWAY_TEST_ALLOWED_HOSTS: List of allowed host patterns for /admin/gateways/test # when GATEWAY_TEST_ALLOW_REGISTERED_ONLY=false. Supports exact hostnames (example.com) # and wildcards (*.example.com). Empty list = reject all when registered-only mode is off. # Example: GATEWAY_TEST_ALLOWED_HOSTS=["api.example.com","*.partner.com"] # GATEWAY_TEST_ALLOWED_HOSTS=[] # # GATEWAY_TEST_DNS_TIMEOUT: Timeout in seconds for DNS resolution during gateway test # validation. Prevents slow/malicious DNS servers from stalling the handler. # Default: 5.0 seconds (range: 0.1 to 30.0) # GATEWAY_TEST_DNS_TIMEOUT=5.0 # # NOTE: Private IPs (RFC 1918), loopback addresses (127.0.0.0/8), and link-local # addresses (169.254.0.0/16) are ALWAYS blocked regardless of allowlist configuration. ################################################################################ # UAID Cross-Gateway Routing Security ################################################################################ # Domain allowlist for UAID cross-gateway routing (REQUIRED for production) # Empty list = DENY all cross-gateway routing (fail-closed, secure default) # Example: UAID_ALLOWED_DOMAINS=["gateway1.example.com", "gateway2.example.com"] UAID_ALLOWED_DOMAINS=[] # DANGEROUS: Allow UAID routing to ANY domain (dev-only) # ⚠️ WARNING: Setting this to true bypasses domain allowlist validation # ⚠️ NEVER use in production - creates SSRF vulnerability # Only enable for development/testing environments UAID_ALLOW_ALL_DOMAINS=false # Forward bearer tokens in cross-gateway UAID calls for RBAC enforcement # When enabled, user authentication context is preserved across gateway hops # Requires both gateways to trust the same JWT issuer (shared JWT_SECRET_KEY or federated SSO) # Disable only if you have a different cross-gateway auth mechanism UAID_FORWARD_AUTH=true # Startup behavior when UAID allowlist not configured (strict mode) # When true: Gateway startup FAILS if A2A enabled but UAID_ALLOWED_DOMAINS empty # When false (default): Logs ERROR but allows startup (backward compatible) # Recommended: true for production, false for dev/staging # UAID_REQUIRE_ALLOWLIST_ON_STARTUP=false # DoS Protection: Maximum UAID length (default 2048, matches database column limit) # Prevents DoS attacks via excessively long UAID parsing # Valid range: 512-2048 (cannot exceed database schema limit) # UAID_MAX_LENGTH=2048 # Networks to block (JSON array of CIDR ranges) - ALWAYS blocked regardless of above # Default blocks cloud metadata endpoints. Add more for stricter security. # SSRF_BLOCKED_NETWORKS=["169.254.169.254/32","169.254.169.123/32","fd00::1/128","169.254.0.0/16","fe80::/10"] # Hostnames to block (JSON array) - case-insensitive matching # SSRF_BLOCKED_HOSTS=["metadata.google.internal","metadata.internal"] # Example: STRICT mode (external endpoints only, no internal access) # SSRF_PROTECTION_ENABLED=true # SSRF_ALLOW_LOCALHOST=false # SSRF_ALLOW_PRIVATE_NETWORKS=false # SSRF_ALLOWED_NETWORKS=[] # SSRF_BLOCKED_NETWORKS=["169.254.169.254/32","169.254.169.123/32","fd00::1/128","169.254.0.0/16","fe80::/10","100.64.0.0/10"] # The 100.64.0.0/10 range is Carrier-Grade NAT (CGNAT) which some cloud providers use # ----------------------------------------------------------------------------- # Content Security - Size Limits and MIME Type Restrictions (US-2) # ----------------------------------------------------------------------------- # Maximum content sizes (in bytes) to prevent DoS attacks via large uploads # Maximum size for resource content (default: 102400 = 100KB) # Resources exceeding this limit will be rejected with 413 Payload Too Large # CONTENT_MAX_RESOURCE_SIZE=102400 # Maximum size for prompt templates (default: 10240 = 10KB) # Prompts exceeding this limit will be rejected with 413 Payload Too Large # CONTENT_MAX_PROMPT_SIZE=10240 # Allowed MIME types for resources (JSON array or comma-separated list) # In strict mode, only MIME types explicitly listed here are accepted. # Vendor types (application/x-*, text/x-*) and suffix types (+json, +xml) must be # explicitly added to this list if needed - they are NOT automatically allowed. # Default: text/plain,text/markdown,text/html,text/csv,application/json,application/xml,application/pdf,... # Both formats are accepted: # CONTENT_ALLOWED_RESOURCE_MIMETYPES=["text/plain","text/markdown","application/json"] # CONTENT_ALLOWED_RESOURCE_MIMETYPES=text/plain,text/markdown,application/json # Enable strict MIME type validation for resources (default: true) # Set to false to log violations without blocking (useful for testing/migration) # CONTENT_STRICT_MIME_VALIDATION=true # ----------------------------------------------------------------------------- # Content Security - Template Validation (US-4) # ----------------------------------------------------------------------------- # Enable validation of prompt templates for safe syntax (default: true) # Validates Jinja2 syntax and blocks dangerous patterns like __import__, eval, exec # Set to false to disable template security validation (not recommended) # CONTENT_VALIDATE_PROMPT_TEMPLATES=true # Dangerous patterns to block in templates (JSON array) # Blocks Python injection attempts like {{__import__('os')}} # Default blocks: __import__, eval, exec, __builtins__, and dunder methods # CONTENT_BLOCKED_TEMPLATE_PATTERNS=["__import__","eval\\s*\\(","exec\\s*\\(","__.*__"] # Example: Strict mode (block all dunder methods and common injection vectors) # CONTENT_BLOCKED_TEMPLATE_PATTERNS=["__import__","__builtins__","__globals__","__locals__","__class__","__base__","__subclasses__","eval\\s*\\(","exec\\s*\\(","compile\\s*\\(","open\\s*\\(","file\\s*\\(","input\\s*\\(","__\\w+__"] # ----------------------------------------------------------------------------- # Content Security - Runtime Pattern Detection (US-3) # ----------------------------------------------------------------------------- # Enable malicious pattern detection in resources and prompts (XSS, SQLi, etc.) # Default: true # CONTENT_PATTERN_DETECTION_ENABLED=true # Validation mode: strict (warn+block), moderate (same as strict), lenient (warn only) # Default: strict # CONTENT_PATTERN_VALIDATION_MODE=strict # Additional regex patterns to block (JSON array, appended to built-ins) # Default: [] (built-in patterns only) # CONTENT_BLOCKED_PATTERNS=[] # Enable caching of clean (non-malicious) pattern validation results # Default: true — disable to reduce memory use at the cost of scan latency # CONTENT_PATTERN_CACHE_ENABLED=true # Maximum clean-result cache entries (0 to disable) # Default: 1000 # CONTENT_PATTERN_MAX_CACHE_SIZE=1000 # Maximum bytes of content scanned per request (hard ReDoS defense) # Content exceeding this limit is rejected. Default: 200000 (200KB) # CONTENT_PATTERN_MAX_SCAN_SIZE=200000 # Per-pattern regex timeout in seconds (soft timeout via thread join) # Default: 1.0 # CONTENT_PATTERN_REGEX_TIMEOUT=1.0 # ============================================================================= # Project defaults (batteries-included overrides) # ============================================================================= # These values intentionally differ from config.py defaults to provide a working # local/dev setup out of the box. Comment out anything you do not want to override. # Bind to all interfaces for local containers and remote access HOST=0.0.0.0 # Local origin used for CORS/cookies in development examples APP_DOMAIN=http://localhost:8080 # Trusted React/UI origin and optional path prefix for links in invitation and password emails. # When unset, password recovery uses legacy /admin routes; invitations use APP_DOMAIN plus APP_ROOT_PATH. # UI_BASE_URL=http://localhost:3000 # Enable Admin UI and Admin API for local development MCPGATEWAY_UI_ENABLED=true MCPGATEWAY_ADMIN_API_ENABLED=true # Enable MCP Apps support (disabled by default for secure rollout) # When enabled, gateway advertises io.modelcontextprotocol/ui capability # SECURITY WARNING: MCP Apps can render UI returned by upstream MCP servers. # UI-originated links, form actions, and JavaScript navigation may open or post # directly from the user's browser instead of going through MCP tool calls/RBAC. # Only enable for trusted servers or with strict UI CSP, sandboxing, external # navigation allowlists/brokered opens, and audit logging. MCPGATEWAY_MCP_APPS_ENABLED=false MCPGATEWAY_MCP_APPS_SESSION_TTL=900 # Local/dev SSRF compatibility: allow local services and fail open on DNS lookups. # Keep strict values (false/false/true) in production environments. SSRF_ALLOW_LOCALHOST=true SSRF_ALLOW_PRIVATE_NETWORKS=true SSRF_DNS_FAIL_CLOSED=false # Disable WebSocket relay and reverse-proxy transports by default. # Enable only when those transports are explicitly required. MCPGATEWAY_WS_RELAY_ENABLED=false MCPGATEWAY_REVERSE_PROXY_ENABLED=false # Relax cookie security for local HTTP development SECURE_COOKIES=false # Enable experimental IO validation for visibility EXPERIMENTAL_VALIDATE_IO=true # DEPRECATED as of 2026-06-11; sunsets on 2026-07-07. Prefer endpoint-level Pydantic validation and protocol-specific validation. VALIDATION_MIDDLEWARE_ENABLED=false # Query-parameter validation patterns (override only when you need stricter or broader allow-lists) # VALIDATION_CURSOR_PATTERN=^[a-zA-Z0-9_=+/-]+$ # VALIDATION_TAGS_FILTER_PATTERN=^[a-zA-Z0-9_,+ .-]*$ # VALIDATION_GATEWAY_ID_LIST_PATTERN=^[a-zA-Z0-9_,-]*$ # VALIDATION_RENDER_MODE_PATTERN=^[a-zA-Z_-]+$ # VALIDATION_VISIBILITY_PATTERN=^(private|team|public)$ # VALIDATION_USER_IDENTIFIER_PATTERN=^[a-zA-Z0-9._%+@-]+$ # VALIDATION_HTTP_METHOD_PATTERN=^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|TRACE|CONNECT)$ # VALIDATION_EXPORT_FORMAT_PATTERN=^(json|csv|ndjson)$ # VALIDATION_ERROR_CODE_PATTERN=^[a-zA-Z0-9_]+$ # VALIDATION_TRACE_STATUS_PATTERN=^(ok|error)$ # VALIDATION_TOOLOPS_MODE_PATTERN=^(generate|query|status)$ # VALIDATION_HYPHEN_IDENTIFIER_PATTERN=^[a-zA-Z0-9_-]+$ # VALIDATION_TEAM_ID_PATTERN=^[a-zA-Z0-9_-]+$ # VALIDATION_SCOPE_ID_PATTERN=^[a-zA-Z0-9_-]+$ # VALIDATION_GATEWAY_ID_PATTERN=^[a-zA-Z0-9_-]+$ # VALIDATION_TRACE_ID_PATTERN=^[a-zA-Z0-9_-]+$ # VALIDATION_RESOURCE_NAME_PATTERN=^[a-zA-Z0-9_. /-]+$ # VALIDATION_RELATIONSHIP_PATTERN=^(owner|member|public)$ # VALIDATION_ENTITY_TYPE_PATTERN=^(tools|resources|prompts|servers)$ # VALIDATION_TIME_RANGE_PATTERN=^(1h|6h|12h|24h|7d|30d)$ # VALIDATION_STATUS_FILTER_PATTERN=^(all|ok|error)$ # VALIDATION_PERIOD_TYPE_PATTERN=^(hourly|daily)$ # VALIDATION_AGGREGATION_PATTERN=^(5m|24h)$ # VALIDATION_ENTITY_TYPES_PATTERN=^[a-zA-Z,]*$ # Tag length validation (applies to all resources: tools, prompts, resources, servers, gateways) # Minimum tag length (default: 2, range: 1-10) # VALIDATION_MIN_TAG_LENGTH=2 # Maximum tag length (default: 100, range: 10-255) # Supports system-generated tags, hashes, and namespaced identifiers # Example: VALIDATION_MAX_TAG_LENGTH=150 for very long descriptive tags # VALIDATION_MAX_TAG_LENGTH=100 # Permission audit logging (RBAC checks) - disabled by default for performance PERMISSION_AUDIT_ENABLED=false # Add SQL injection pattern on top of the default dangerous patterns DANGEROUS_PATTERNS=["[;&|`$(){}\\[\\]<>]", "\\.\\.[\\\\/]", "[\\x00-\\x1f\\x7f-\\x9f]", "(?i)(drop|delete|insert|update|select)\\s+(table|from|into|where)"] # Loosen password complexity for local bootstrap (production should re-enable) PASSWORD_REQUIRE_UPPERCASE=false PASSWORD_REQUIRE_LOWERCASE=false PASSWORD_REQUIRE_SPECIAL=false # Longer UI tool test timeout for slower dev environments MCPGATEWAY_UI_TOOL_TEST_TIMEOUT=120000 # Slow down health polling and extend config cache for local dev HEALTH_CHECK_INTERVAL=300 GLOBAL_CONFIG_CACHE_TTL=300 # Log to file by default for local debugging LOG_FILE=mcpgateway.log LOG_FOLDER=logs # Local OTEL collector endpoint OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 # ----------------------------------------------------------------------------- # Experimental DATAPLANE # ----------------------------------------------------------------------------- # Send db config to redis to be consumed by experimental Dataplane DATAPLANE_PUBLISHER=false DATAPLANE_PUBLISHER_INTERVAL_SECONDS=60 # ============================================================================= # Hot toggles (commented quick switches) # ============================================================================= # These are frequently changed flags. If a key is already set in the # Project defaults block above, change it there instead of uncommenting here. # Feature flags / UX (full docs → Plugin Framework Configuration section) # MCPGATEWAY_UI_ENABLED=false # MCPGATEWAY_ADMIN_API_ENABLED=false # PLUGINS_ENABLED=false # MCPGATEWAY_CATALOG_ENABLED=true # LLMCHAT_ENABLED=true # MCPGATEWAY_STDIO_TRANSPORT_ENABLED=false # PLUGINS_CAN_OVERRIDE_RBAC=false # PLUGINS_CAN_OVERRIDE_AUTH_HEADERS=false # Direct proxy mode # MCPGATEWAY_DIRECT_PROXY_ENABLED=false # MCPGATEWAY_DIRECT_PROXY_TIMEOUT=30 # Observability / metrics (full docs → Observability Settings + OpenTelemetry sections) # OBSERVABILITY_ENABLED=false # OTEL_ENABLE_OBSERVABILITY=false # CPEX_CONTROL_TELEMETRY_ENABLED=false # ENABLE_METRICS=true # DB_METRICS_RECORDING_ENABLED=true # METRICS_AGGREGATION_AUTO_START=false # Logging / audit (full docs → respective named sections below) # STRUCTURED_LOGGING_DATABASE_ENABLED=false # AUDIT_TRAIL_ENABLED=false # PERMISSION_AUDIT_ENABLED=false # SECURITY_LOGGING_ENABLED=false # SIEM_EXPORT_ENABLED=false # SIEM_EXPORT_EVENT_SOURCES=["auth","security","audit"] # SIEM_DESTINATIONS=[] # TOKEN_USAGE_LOGGING_ENABLED=true # TOKEN_LAST_USED_UPDATE_INTERVAL_MINUTES=5 # Security / auth (full docs → Authentication section) # AUTH_REQUIRED=true # MCP_CLIENT_AUTH_ENABLED=true # HTTP header name for JWT authentication (default: Authorization) # Use alternative header (e.g., X-MCP-Gateway-Auth) to avoid collision with # downstream server authentication. When using custom header, the standard # Authorization header is preserved and passed through to backend servers. # AUTH_HEADER_NAME=Authorization # AUTH_HEADER_NAME=X-MCP-Gateway-Auth # TRUST_PROXY_AUTH=false # TRUST_PROXY_AUTH_DANGEROUSLY=false # DANGER: Only set true behind a strictly trusted auth proxy # ALLOW_UNAUTHENTICATED_ADMIN=false # DANGER: Only for local dev — grants admin to unauthenticated requests # SECURITY_HEADERS_ENABLED=true # CORS_ALLOW_CREDENTIALS=true # SECURE_COOKIES=true # REQUIRE_USER_IN_DB=false # ANYIO_CANCEL_DELIVERY_PATCH_ENABLED=false # Rust MCP (simple) - DEPRECATED as of 2026-06-11; sunsets on 2026-07-07. # Prefer RUST_MCP_MODE=off and the default Python MCP transport. # See https://ibm.github.io/mcp-context-forge/deprecations/ # RUST_MCP_BUILD=false # build the Rust MCP runtime into Containerfile.lite images # RUST_MCP_MODE=off # off | shadow | edge | full # RUST_MCP_LOG=warn # default Rust sidecar log filter for the simple mode flow # # RUST_MCP_MODE=shadow -> Rust sidecar enabled, but public /mcp stays on Python for safe fallback # RUST_MCP_MODE=edge -> direct public /mcp on Rust with managed UDS sidecar defaults # RUST_MCP_MODE=full -> edge + Rust session/event-store/resume/live-stream/affinity cores # # Runtime override (in-memory only; restart re-reads RUST_MCP_MODE / RUST_A2A_MODE): # When the boot mode is edge an authorized caller (admin.system_config) can # flip shadow <-> edge live via: # (off, shadow, and full boot modes are not flippable — off has no Rust # sidecar; shadow did not opt into session-auth-reuse / delegate-enabled # so an edge override cannot safely route traffic; full would require # live migration of Rust-owned session/event-store cores.) # curl -X PATCH .../admin/runtime/mcp-mode -d '{"mode": "shadow"}' # curl -X PATCH .../admin/runtime/a2a-mode -d '{"mode": "edge"}' # If REDIS_URL is configured the override propagates to all pods; otherwise the # override is local to the pod that received the PATCH (visible via /health # under mcp_runtime.cluster_propagation). # # Advanced Rust MCP overrides - DEPRECATED with the Rust MCP runtime sidecar. # RUST_MCP_SESSION_AUTH_REUSE=false # advanced override for the fast direct public Rust session-auth path; prefer RUST_MCP_MODE presets above # EXPERIMENTAL_RUST_MCP_RUNTIME_ENABLED= # EXPERIMENTAL_RUST_MCP_RUNTIME_URL=http://127.0.0.1:8787 # EXPERIMENTAL_RUST_MCP_RUNTIME_UDS=/tmp/contextforge-mcp-rust.sock # EXPERIMENTAL_RUST_MCP_RUNTIME_TIMEOUT_SECONDS=30 # EXPERIMENTAL_RUST_MCP_SESSION_CORE_ENABLED= # enable Rust-owned MCP session metadata/lifecycle increment # EXPERIMENTAL_RUST_MCP_EVENT_STORE_ENABLED= # enable Rust-owned resumable event-store backend # EXPERIMENTAL_RUST_MCP_RESUME_CORE_ENABLED= # enable Rust-owned public GET /mcp replay/resume path # EXPERIMENTAL_RUST_MCP_LIVE_STREAM_CORE_ENABLED= # enable Rust-owned public GET /mcp live SSE path # EXPERIMENTAL_RUST_MCP_AFFINITY_CORE_ENABLED= # enable Rust-owned session-affinity forwarding path # EXPERIMENTAL_RUST_MCP_SESSION_AUTH_REUSE_ENABLED= # enable Rust-owned session-bound auth-context reuse # EXPERIMENTAL_RUST_MCP_RUNTIME_MANAGED= # launcher env, not a Pydantic setting # ENABLE_RUST_MCP_RMCP_BUILD= # container build arg override for rmcp-enabled Rust MCP binary # MCP_RUST_USE_RMCP_UPSTREAM_CLIENT= # runtime override for official rust-sdk upstream tools/call client # MCP_RUST_LISTEN_HTTP=127.0.0.1:8787 # runtime env for bundled Rust sidecar # MCP_RUST_LISTEN_UDS=/tmp/contextforge-mcp-rust.sock # MCP_RUST_SESSION_CORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_SESSION_CORE_ENABLED # MCP_RUST_SESSION_TTL_SECONDS=3600 # MCP_RUST_EVENT_STORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_EVENT_STORE_ENABLED # MCP_RUST_RESUME_CORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_RESUME_CORE_ENABLED # MCP_RUST_LIVE_STREAM_CORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_LIVE_STREAM_CORE_ENABLED # MCP_RUST_AFFINITY_CORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_AFFINITY_CORE_ENABLED # MCP_RUST_SESSION_AUTH_REUSE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_SESSION_AUTH_REUSE_ENABLED # MCP_RUST_SESSION_AUTH_REUSE_TTL_SECONDS=30 # MCP_RUST_EVENT_STORE_MAX_EVENTS_PER_STREAM=100 # MCP_RUST_EVENT_STORE_TTL_SECONDS=3600 # MCP_RUST_EVENT_STORE_POLL_INTERVAL_MS=250 # MCP_RUST_LOG= # advanced runtime log override for the bundled Rust sidecar # MCP_RUST_BACKEND_RPC_URL=http://127.0.0.1:4444/_internal/mcp/rpc # MCP_RUST_REDIS_URL=redis://redis:6379/0 # MCP_RUST_CACHE_PREFIX=mcpgw: # MCP_RUST_DATABASE_URL=postgresql://postgres:mysecretpassword@pgbouncer:6432/mcp # MCP_RUST_DB_POOL_MAX_SIZE=20 # MCP_RUST_MAX_REQUEST_BODY_SIZE_BYTES=10485760 # max request body (bytes) enforced by the Rust ingress (default 10MB). Only applies when RUST_MCP_MODE=edge|full # Rust A2A runtime controls are also DEPRECATED as of 2026-06-11; sunsets on 2026-07-07. # Prefer RUST_A2A_MODE=off and the default Python A2A invocation path. # ============================================================================= # Performance Tuning (quick reference) # ============================================================================= # Use this section to tune throughput, latency, and cache behavior. # If a key is already set in the Project defaults block, change it there instead. # Detailed explanations for each setting appear later in this file. # ----------------------------------------------------------------------------- # Cache TTLs (seconds) # ----------------------------------------------------------------------------- # AUTH / registry / admin caches # AUTH_CACHE_USER_TTL=60 # AUTH_CACHE_REVOCATION_TTL=30 # AUTH_CACHE_TEAM_TTL=60 # AUTH_CACHE_ROLE_TTL=60 # AUTH_CACHE_TEAMS_TTL=60 # REGISTRY_CACHE_TOOLS_TTL=20 # REGISTRY_CACHE_PROMPTS_TTL=15 # REGISTRY_CACHE_RESOURCES_TTL=15 # REGISTRY_CACHE_AGENTS_TTL=20 # REGISTRY_CACHE_SERVERS_TTL=20 # REGISTRY_CACHE_GATEWAYS_TTL=20 # REGISTRY_CACHE_CATALOG_TTL=300 # ADMIN_STATS_CACHE_SYSTEM_TTL=60 # ADMIN_STATS_CACHE_OBSERVABILITY_TTL=30 # ADMIN_STATS_CACHE_TAGS_TTL=120 # ADMIN_STATS_CACHE_PLUGINS_TTL=120 # ADMIN_STATS_CACHE_PERFORMANCE_TTL=60 # TEAM_MEMBER_COUNT_CACHE_TTL=300 # METRICS_CACHE_TTL_SECONDS=60 # Tool + pagination caches # TOOL_LOOKUP_CACHE_TTL_SECONDS=60 # TOOL_LOOKUP_CACHE_NEGATIVE_TTL_SECONDS=10 # PAGINATION_COUNT_CACHE_TTL=300 # LLM chat caches # LLMCHAT_SESSION_TTL=300 # LLMCHAT_SESSION_LOCK_TTL=30 # LLMCHAT_CHAT_HISTORY_TTL=3600 # Catalog / DCR / performance caches # MCPGATEWAY_CATALOG_CACHE_TTL=3600 # DCR_METADATA_CACHE_TTL=3600 # MCPGATEWAY_PERFORMANCE_NET_CONNECTIONS_CACHE_TTL=15 # Static resource caching # RESOURCE_CACHE_TTL=3600 # Redis leader election # REDIS_LEADER_TTL=15 # REDIS_LEADER_HEARTBEAT_INTERVAL=5 # ----------------------------------------------------------------------------- # Pooling, concurrency, and limits # ----------------------------------------------------------------------------- # Database connection pool (SQLAlchemy) # DB_POOL_CLASS=auto # DB_POOL_PRE_PING=auto # DB_POOL_SIZE=200 # DB_MAX_OVERFLOW=10 # DB_POOL_TIMEOUT=30 # DB_POOL_RECYCLE=3600 # Redis connection pool # REDIS_MAX_CONNECTIONS=50 # REDIS_SOCKET_TIMEOUT=2.0 # REDIS_SOCKET_CONNECT_TIMEOUT=2.0 # REDIS_HEALTH_CHECK_INTERVAL=30 # Timeout for individual Redis operations (seconds). Prevents API hangs during Redis connectivity issues. # Circuit breaker opens after REDIS_CIRCUIT_FAILURE_THRESHOLD consecutive failures # (timeouts or connection errors) and cools down for REDIS_CIRCUIT_OPEN_DURATION seconds # before allowing a single probe. A successful probe closes the circuit. # REDIS_OPERATION_TIMEOUT=0.5 # REDIS_CIRCUIT_FAILURE_THRESHOLD=3 # REDIS_CIRCUIT_OPEN_DURATION=30.0 # Redis TLS (disabled by default for local dev; enable in production) # To use TLS, change REDIS_URL to use the rediss:// scheme and set REDIS_SSL=true. # Example production URL: REDIS_URL=rediss://redis:6380/0 # REDIS_SSL=false # REDIS_SSL_CA_CERTS=/certs/ca.crt # CA bundle to verify the Redis server certificate # REDIS_SSL_CERTFILE=/certs/client.crt # Client certificate for mutual TLS (mTLS) # REDIS_SSL_KEYFILE=/certs/client.key # Client private key for mutual TLS (mTLS) # REDIS_SSL_CHECK_HOSTNAME=true # Set true only when Redis presents a valid CA-signed cert # HTTPX shared client pool # HTTPX_MAX_CONNECTIONS=200 # HTTPX_MAX_KEEPALIVE_CONNECTIONS=100 # HTTPX_KEEPALIVE_EXPIRY=30.0 # HTTPX_POOL_TIMEOUT=10.0 # Tool and federation limits # TOOL_TIMEOUT=60 # MAX_TOOL_RETRIES=3 # TOOL_RATE_LIMIT=100 # TOOL_CONCURRENT_LIMIT=10 # FEDERATION_TIMEOUT=120 # Health checks # HEALTH_CHECK_INTERVAL=60 # HEALTH_CHECK_TIMEOUT=30 # UNHEALTHY_THRESHOLD=3 # GATEWAY_VALIDATION_TIMEOUT=5 # MAX_CONCURRENT_HEALTH_CHECKS=10 # ----------------------------------------------------------------------------- # Timeouts, polling, and backoff # ----------------------------------------------------------------------------- # Session registry polling (cache_type=database) # POLL_INTERVAL=1.0 # MAX_INTERVAL=5.0 # BACKOFF_FACTOR=1.5 # DB startup resilience # DB_MAX_RETRIES=30 # DB_RETRY_INTERVAL_MS=2000 # DB_MAX_BACKOFF_SECONDS=30 # Redis startup resilience # REDIS_MAX_RETRIES=30 # REDIS_RETRY_INTERVAL_MS=2000 # REDIS_MAX_BACKOFF_SECONDS=30 # ----------------------------------------------------------------------------- # Retention and cleanup windows # ----------------------------------------------------------------------------- # METRICS_RETENTION_DAYS=7 # METRICS_CLEANUP_INTERVAL_HOURS=1 # METRICS_ROLLUP_ENABLED=true # METRICS_ROLLUP_INTERVAL_HOURS=1 # METRICS_ROLLUP_RETENTION_DAYS=365 # METRICS_ROLLUP_LATE_DATA_HOURS=1 # METRICS_DELETE_RAW_AFTER_ROLLUP=true # METRICS_DELETE_RAW_AFTER_ROLLUP_HOURS=1 # MCPGATEWAY_PERFORMANCE_RETENTION_HOURS=24 # MCPGATEWAY_PERFORMANCE_RETENTION_DAYS=90 # OBSERVABILITY_TRACE_RETENTION_DAYS=7 # LOG_RETENTION_DAYS=30 # HTTPX timeouts # HTTPX_CONNECT_TIMEOUT=5.0 # HTTPX_READ_TIMEOUT=120.0 # HTTPX_WRITE_TIMEOUT=30.0 # HTTPX_ADMIN_READ_TIMEOUT=30.0 # SSE_TASK_GROUP_CLEANUP_TIMEOUT=5.0 # ANYIO_CANCEL_DELIVERY_PATCH_ENABLED=false # ANYIO_CANCEL_DELIVERY_MAX_ITERATIONS=100 # ----------------------------------------------------------------------------- # Middleware overhead and compression # ----------------------------------------------------------------------------- # COMPRESSION_ENABLED=true # COMPRESSION_GZIP_LEVEL=6 # COMPRESSION_BROTLI_QUALITY=4 # COMPRESSION_ZSTD_LEVEL=3 # COMPRESSION_MINIMUM_SIZE=500 # CLIENT_DISCONNECT_MIDDLEWARE_ENABLED=true # VALIDATION_MIDDLEWARE_ENABLED=false # CORRELATION_ID_ENABLED=true # TEMPLATES_AUTO_RELOAD=false # STRUCTURED_LOGGING_DATABASE_ENABLED=false # full docs → Structured Log Database Persistence section # AUDIT_TRAIL_ENABLED=false # full docs → Audit Trail Logging section # SECURITY_LOGGING_ENABLED=false # full docs → Security Event Logging section # ============================================================================= # Basic Server Configuration # ============================================================================= # Application name displayed in UI and logs # APP_NAME=ContextForge # Host interface to bind to (127.0.0.1 = localhost only) # Project defaults block sets HOST=0.0.0.0 for local containers # HOST=127.0.0.1 # Port number for the HTTP server # PORT=4444 # Runtime environment - affects CORS, cookies, and security defaults # Options: development, staging, production # - development: Relaxed CORS (localhost:3000/8080), debug info, insecure cookies # - staging: Production-like CORS and cookie defaults, but use staging domains # - 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) # Project defaults block sets APP_DOMAIN=http://localhost for local dev # APP_DOMAIN=http://localhost:4444 # FastAPI root_path for reverse proxy deployments (empty = serve from root "/") # Used when gateway is behind a proxy with path prefix (e.g., "/api/v1") # See FastAPI docs: https://fastapi.tiangolo.com/advanced/behind-a-proxy/ # APP_ROOT_PATH= # Client mode for gateway-as-client usage # Options: true, false (default) # CLIENT_MODE=false # Override templates/static directories (absolute paths) # Leave unset to use package defaults # TEMPLATES_DIR=/absolute/path/to/templates # STATIC_DIR=/absolute/path/to/static # 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 # canonical entry — remove stub in project-defaults if needed # Database Configuration # SQLite (default) - good for development and small deployments # macOS note: If you see "sqlite3.OperationalError: disk I/O error" on macOS when running # `make serve`, move the DB to a safe APFS path (avoid iCloud/Dropbox/OneDrive/Google Drive, # network shares, or external exFAT) and use an absolute path, for example: # DATABASE_URL=sqlite:////Users/$USER/Library/Application Support/mcpgateway/mcp.db # DATABASE_URL=sqlite:///./mcp.db # Skip alembic upgrade head on startup (migrations managed externally via init container or CI). # Set to 'true' when using docker-compose (the migration service handles schema before gateway starts). # Set to 'false' for standalone 'docker run' or direct 'make serve' deployments. MCPGATEWAY_SKIP_MIGRATIONS=false # PostgreSQL - recommended for production deployments # Uses psycopg3 driver (psycopg[binary]) # IMPORTANT: Use postgresql+psycopg:// (not postgresql://) for psycopg3 # DATABASE_URL=postgresql+psycopg://postgres:mysecretpassword@localhost:5432/mcp # Database Connection Pool Configuration # ============================================================================ # IMPORTANT: Pool size depends on your database connection strategy: # # WITH PgBouncer (recommended for PostgreSQL, default in docker-compose): # - Use SMALL pool (10-20) since PgBouncer handles connection pooling # - docker-compose.yml sets DB_POOL_SIZE=15 by default # - Do NOT override here unless you know what you're doing # - Formula: (replicas × workers × pool) should be < PgBouncer MAX_CLIENT_CONN # # WITHOUT PgBouncer (direct PostgreSQL or SQLite): # - Use LARGER pool based on: (replicas × workers × pool) < max_connections # - Uncomment and set DB_POOL_SIZE=50-200 depending on workload # # Uncomment for SQLite or direct PostgreSQL without PgBouncer # DB_POOL_SIZE=200 # ============================================================================ # Additional connections beyond pool_size for burst handling (default: 10) # DB_MAX_OVERFLOW=10 # Seconds to wait for connection before timeout (default: 30) # DB_POOL_TIMEOUT=30 # Seconds before recreating connection to prevent stale connections (default: 3600) # DB_POOL_RECYCLE=3600 # Database driver identifier (advanced; used by SQLAlchemy engine selection) # DB_DRIVER=postgresql+psycopg # Connection pool class selection # Options: auto (default), null, queue # DB_POOL_CLASS=auto # Connection pool pre-ping behavior # Options: auto (default), true, false # DB_POOL_PRE_PING=auto # Database Startup Resilience (exponential backoff with jitter) # Retry progression: 2s → 4s → 8s → 16s → 30s (capped), ±25% jitter # 30 retries ≈ 5 minutes total wait before worker gives up # DB_MAX_RETRIES=30 # Base retry interval in milliseconds (doubles each attempt) # DB_RETRY_INTERVAL_MS=2000 # Maximum backoff cap in seconds (jitter ±25% applied after cap) # DB_MAX_BACKOFF_SECONDS=30 # psycopg3: Number of query executions before auto-preparing server-side (default: 5) # Set to 0 to disable, 1 to prepare immediately. Higher values reduce memory usage. # DB_PREPARE_THRESHOLD=5 # SQLite Configuration # SQLite busy timeout (milliseconds) - maximum time SQLite will block while waiting # to acquire a database lock before returning SQLITE_BUSY. Limits lock-wait latency # and prevents prolonged thread blocking under write contention (default: 5000ms) # DB_SQLITE_BUSY_TIMEOUT=5000 # Database Performance Optimization # Use database-native percentile functions for observability performance metrics # When true: PostgreSQL uses native percentile_cont (5-10x faster for large datasets) # When false: Falls back to Python-based percentile calculations (works with all databases) # Recommended: true for PostgreSQL production deployments, auto-detected for SQLite # USE_POSTGRESDB_PERCENTILES=true # The number of rows fetched from the database at a time when streaming results, # to limit memory usage and avoid loading all rows into RAM at once. # YIELD_BATCH_SIZE=1000 # 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 # REQUIRED for multi-worker session affinity (MCPGATEWAY_SESSION_AFFINITY_ENABLED=true) # CACHE_TYPE=database # Session Registry Database Polling (Adaptive Backoff) # When CACHE_TYPE=database, sessions use polling to check for messages. # Adaptive backoff reduces database load by ~90% during idle periods while # maintaining responsiveness when messages arrive. # # How it works: # - Starts polling at POLL_INTERVAL (1.0s default) # - When no messages found, interval increases by BACKOFF_FACTOR (1.5x) # - Backs off until reaching MAX_INTERVAL (5.0s cap) # - Immediately resets to POLL_INTERVAL when a message arrives # # Example progression: 1.0s → 1.5s → 2.25s → 3.375s → 5.0s (capped) # ============================================================================= # Tuning guide: # - Lower POLL_INTERVAL (0.1-0.5s) for real-time applications needing <1s latency # - Higher MAX_INTERVAL (10-30s) for batch workloads to minimize DB queries # - Higher BACKOFF_FACTOR (2.0) for faster backoff, lower (1.2) for gradual # POLL_INTERVAL=1.0 # MAX_INTERVAL=5.0 # BACKOFF_FACTOR=1.5 # 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 # Redis Startup Resilience (exponential backoff with jitter) # Same behavior as DB retries: 2s → 4s → 8s → 16s → 30s (capped), ±25% jitter # 30 retries ≈ 5 minutes total wait before worker gives up # REDIS_MAX_RETRIES=30 # Base retry interval in milliseconds (doubles each attempt) # REDIS_RETRY_INTERVAL_MS=2000 # Maximum backoff cap in seconds (jitter ±25% applied after cap) # REDIS_MAX_BACKOFF_SECONDS=30 # ============================================================================= # Redis Connection Pool - Performance Tuned # ============================================================================= # Connection pool size per worker process # Formula: (concurrent_requests / workers) * 1.5 # Default 50 handles ~500 concurrent requests with 10 workers # REDIS_MAX_CONNECTIONS=50 # Socket read/write timeout (seconds) # Keep low for fast failure detection; Redis ops typically <100ms # REDIS_SOCKET_TIMEOUT=2.0 # Connection establishment timeout (seconds) # Keep low to avoid blocking event loop on network issues # REDIS_SOCKET_CONNECT_TIMEOUT=2.0 # Retry commands that timeout (recommended: true) # REDIS_RETRY_ON_TIMEOUT=true # Connection health check interval (seconds, 0=disabled) # Prevents stale connections in pool # REDIS_HEALTH_CHECK_INTERVAL=30 # Return strings instead of bytes (recommended: true) # REDIS_DECODE_RESPONSES=true # ============================================================================= # Redis Parser Configuration (Performance - ADR-026) # ============================================================================= # Redis protocol parser selection # Options: # - auto (default): Use hiredis C parser if available, fallback to pure-Python # - hiredis: Require hiredis C parser (fails if not installed) # - python: Force pure-Python parser (useful for debugging) # # Performance benchmarks (hiredis vs pure-Python): # - Simple SET/GET: ~1.1x faster # - LRANGE (10 items): ~2.7x faster # - LRANGE (100 items): ~10x faster # - LRANGE (999 items): ~83x faster # # Recommendation: Leave as "auto" - hiredis is installed by default with redis[hiredis] # REDIS_PARSER=auto # ============================================================================= # Redis Leader Election - Multi-Node Deployments # ============================================================================= # Leader TTL in seconds (time before failover if leader dies) # Lower = faster failover, but more sensitive to network blips # Recommended: 15s for production, 5s for development # REDIS_LEADER_TTL=15 # Leader heartbeat interval (seconds) # Must be < leader_ttl/2 to prevent false failovers # Rule: heartbeat_interval <= leader_ttl / 3 # REDIS_LEADER_HEARTBEAT_INTERVAL=5 # Leader key name in Redis # REDIS_LEADER_KEY=gateway_service_leader # Override path for the primary-worker election lock file # (mcpgateway/utils/primary_worker.py). Lets a side-effecting non-hook plugin run # its work on one worker per host. Defaults to a port-scoped file in the system # temp dir when unset; point it at a gateway-owned directory on hostile hosts. # PRIMARY_WORKER_LOCK_PATH= # Primary-worker election backend: # - filelock (default): one primary per host # - redis: one primary across all instances sharing the same Redis # PRIMARY_WORKER_ELECTION_BACKEND=filelock # PRIMARY_WORKER_REDIS_KEY=mcpgw:primary_worker # PRIMARY_WORKER_LEASE_TTL=15 # PRIMARY_WORKER_HEARTBEAT_INTERVAL=5 # When the redis backend can't reach Redis: fail_closed (no primary) or # filelock_fallback (per-host). fail_closed preserves the global guarantee. # PRIMARY_WORKER_REDIS_UNAVAILABLE_POLICY=fail_closed # ============================================================================= # ============================================================================= # Rate Limiter Redis Configuration # ============================================================================= # Optional: Dedicated Redis instance for rate limiting middleware to prevent # contention with main Redis (cache, sessions, federation). # When unset, rate limiting uses REDIS_URL (backward compatible). # # IMPORTANT: Rate limiter Redis operates independently of CACHE_TYPE setting. # Optional Redis URL for rate limiting middleware # Must start with redis:// or rediss:// # RATELIMITER_REDIS_URL=redis://localhost:6380/0 # Connection pool size for rate limiter Redis (default: 50) # RATELIMITER_REDIS_MAX_CONNECTIONS=50 # Socket read/write timeout for rate limiter Redis (default: 2.0 seconds) # RATELIMITER_REDIS_SOCKET_TIMEOUT=2.0 # Connection timeout for rate limiter Redis (default: 2.0 seconds) # RATELIMITER_REDIS_SOCKET_CONNECT_TIMEOUT=2.0 # Redis TLS (disabled by default for local dev; enable in production) # To use TLS, change RATELIMITER_REDIS_URL to use the rediss:// scheme and set RATELIMITER_REDIS_SSL=true. # Example production URL: REDIS_URL=rediss://redis:6380/0 # RATELIMITER_REDIS_SSL=false # RATELIMITER_REDIS_SSL_CA_CERTS=/certs/ca.crt # CA bundle to verify the Redis server certificate # RATELIMITER_REDIS_SSL_CERTFILE=/certs/client.crt # Client certificate for mutual TLS (mTLS) # RATELIMITER_REDIS_SSL_KEYFILE=/certs/client.key # Client private key for mutual TLS (mTLS) # RATELIMITER_REDIS_SSL_CHECK_HOSTNAME=true # Set true only when Redis presents a valid CA-signed cert # Protocol Settings # ============================================================================= # MCP protocol version supported by this gateway # PROTOCOL_VERSION=2025-06-18 # ============================================================================= # Authentication # ============================================================================= # 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! # 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 # MCP endpoint authentication requirement # Options: true, false # Default when unset: follows AUTH_REQUIRED # - AUTH_REQUIRED=true -> MCP auth required # - AUTH_REQUIRED=false -> public-only /mcp access allowed # Set to false explicitly to allow unauthenticated public-only MCP access. # MCP_REQUIRE_AUTH=true # JWT Algorithm Selection # Supported algorithms: # HMAC (Symmetric): HS256, HS384, HS512 - Simple deployments, shared secret # RSA (Asymmetric): RS256, RS384, RS512 - Enterprise, distributed systems # ECDSA (Asymmetric): ES256, ES384, ES512 - High performance, modern crypto # JWT_ALGORITHM=HS256 # === HMAC (Symmetric) Configuration === # Secret used to sign JWTs (required for HMAC algorithms: HS256, HS384, HS512) # REQUIRED in all environments — see "REQUIRED SECRETS" section at the top of this file. # Use a strong, random secret (minimum 32 characters). Generate with: # python3 -m mcpgateway.scripts.init_secrets # JWT_SECRET_KEY= # === RSA/ECDSA (Asymmetric) Configuration - Recommended for Production === # Public and private key paths (required for asymmetric algorithms: RS*, ES*) # Generate RSA keys with: make certs-jwt # (creates certs/jwt/private.pem and certs/jwt/public.pem with proper permissions) # Generate ECDSA keys with: make certs-jwt-ecdsa # (creates certs/jwt/ec_private.pem and certs/jwt/ec_public.pem with proper permissions) # Generate both SSL and JWT keys: make certs-all #JWT_PUBLIC_KEY_PATH=certs/jwt/public.pem #JWT_PRIVATE_KEY_PATH=certs/jwt/private.pem # JWT Claims Configuration # PRODUCTION: Set these to your service-specific values # JWT_AUDIENCE=mcpgateway-api # JWT_ISSUER=mcpgateway # Cross-environment token isolation (GHSA-vgf8-3685-66j9) # Use a DISTINCT JWT_SECRET_KEY per environment. Do not copy .env across DEV/STAGING/PROD. # Also set a distinct ENVIRONMENT value per deployment (development/staging/production) -- # DERIVE_KEY_PER_ENVIRONMENT isolation requires distinct ENVIRONMENT values. EMBED_ENVIRONMENT_IN_TOKENS=true VALIDATE_TOKEN_ENVIRONMENT=true # Optional: derive a per-environment signing key from JWT_SECRET_KEY (HS* only). # Enabling this re-keys tokens (treat as a key rotation); same-environment federation # peers must share the same JWT_SECRET_KEY and ENVIRONMENT. Not applicable to RS*/ES*. # DERIVE_KEY_PER_ENVIRONMENT=false # JWT Validation Options # Set to false for Dynamic Client Registration (DCR) scenarios where audience varies # JWT_AUDIENCE_VERIFICATION=true # Set to false for custom auth flows where issuer varies or is not present # JWT_ISSUER_VERIFICATION=true # Expiry time for generated JWT tokens (in minutes; e.g. 7 days) # TOKEN_EXPIRY=10080 # Session lifecycle (POST /auth/refresh + GET /auth/validate) # Absolute maximum session lifetime in minutes, enforced at refresh (0 disables the cap) # SESSION_MAX_LIFETIME=480 # Maximum POST /auth/refresh requests per minute per client dimension (IP/user/team) # SESSION_REFRESH_RATE_LIMIT=10 # Client-behavior hints surfaced via GET /auth/validate (seconds) # SESSION_WARNING_TIME=60 # SESSION_REFRESH_BUFFER=300 # SESSION_ACTIVITY_TRACKING=true # SECURITY: Require expiration claim in all tokens (default: true) # Set to false only for backward compatibility with legacy tokens # REQUIRE_TOKEN_EXPIRATION=true # SECURITY: Require JTI (JWT ID) claim for token revocation support (default: true) # Set to false only for backward compatibility with legacy tokens # REQUIRE_JTI=true # Require all authenticated users to exist in the database # When true, disables the platform admin bootstrap mechanism # WARNING: Enabling this on a fresh deployment will lock you out! # REQUIRE_USER_IN_DB=false # ============================================================================= # Security Validation & Sanitization # ============================================================================= # Enable experimental input validation and output sanitization # This implements gateway-level security controls to protect against: # - Path traversal attacks (../../../etc/passwd) # - Command injection (file.jpg; rm -rf /) # - SQL injection ('; DROP TABLE users; --) # - XSS attacks () # - Control character injection (\x1b[31m) # # Roll-out phases: # Phase 0: EXPERIMENTAL_VALIDATE_IO=false (disabled, default) # Phase 1: EXPERIMENTAL_VALIDATE_IO=true, VALIDATION_STRICT=false (log-only) # Phase 2: EXPERIMENTAL_VALIDATE_IO=true, VALIDATION_STRICT=true (enforce in staging) # Phase 3: Production deployment with all features enabled # Project defaults block enables EXPERIMENTAL_VALIDATE_IO for local dev # EXPERIMENTAL_VALIDATE_IO=false # Deprecated validation middleware for all requests # Keep disabled unless an existing deployment depends on it # Options: true, false (default) # VALIDATION_MIDDLEWARE_ENABLED=false # active value set in project-defaults block # Strict validation mode # Options: # - true: Reject requests with validation failures (422 status) # - false: Log warnings but allow requests (log-only mode) # Recommended: false for dev/staging, true for production # # Note: this also controls the forbidden-pattern check on tool descriptions # during registration (shell metacharacters such as "> ", "< ", "|", ";"). # Set to false if your MCP server tools have Markdown-formatted descriptions # that contain these characters (e.g. "> blockquote", "< input", "cmd | grep"). # For more targeted control, see TOOL_DESCRIPTION_FORBIDDEN_PATTERNS below. # VALIDATION_STRICT=true # Tool description forbidden pattern validation # Master switch to enable/disable forbidden pattern checks on tool descriptions. # When disabled, no pattern checks are performed regardless of VALIDATION_STRICT. # Options: true (default), false # TOOL_DESCRIPTION_FORBIDDEN_PATTERNS_ENABLED=true # Override the list of substrings blocked in tool descriptions. # Accepts a JSON array. Default: ["&&", ";", "||", "$(", "|", "> ", "< "] # Set to a custom list to allow specific patterns while still blocking others. # Example: TOOL_DESCRIPTION_FORBIDDEN_PATTERNS=["&&", "$("] # TOOL_DESCRIPTION_FORBIDDEN_PATTERNS=["&&", ";", "||", "$(", "|", "> ", "< "] # Strict JSON Schema validation for tools and prompts # Options: # - true: Reject invalid JSON schemas during registration (strict spec compliance) # - false: Log warnings only (backward compatibility for legacy tools) # JSON_SCHEMA_VALIDATION_STRICT=true # Sanitize output to remove control characters # Removes ANSI escape sequences and C0/C1 control characters from responses # Preserves newlines (\n) and tabs (\t) # Options: true (default), false # SANITIZE_OUTPUT=true # MCP Apps session cleanup (AppBridge) # Enable automatic cleanup of expired AppBridge sessions # MCPGATEWAY_MCP_APPS_SESSION_CLEANUP_ENABLED=true # Seconds between cleanup runs (60–86400) # MCPGATEWAY_MCP_APPS_SESSION_CLEANUP_INTERVAL_SECONDS=300 # Max expired sessions deleted per cleanup batch # MCPGATEWAY_MCP_APPS_SESSION_CLEANUP_BATCH_SIZE=1000 # Allowed root paths for resource access # Restricts file system access to specific directories # Format: JSON array or comma-separated list # Examples: # - JSON: ["/srv/data", "/var/app/uploads"] # - CSV: /srv/data,/var/app/uploads # - Empty: [] (no restrictions, not recommended) # PRODUCTION: Always configure this to limit resource access # ALLOWED_ROOTS=[] # Maximum allowed path depth # Prevents deeply nested path attacks # Default: 10 levels # MAX_PATH_DEPTH=10 # Maximum parameter length (characters) # Prevents buffer overflow and DoS attacks # Default: 10000 characters # MAX_PARAM_LENGTH=10000 # CWE-400: Limits for user-supplied meta_data forwarded to upstream MCP servers. # Keeps arbitrarily large dicts from amplifying into downstream network/DB load. # Maximum number of top-level keys in meta_data (default: 16) # META_MAX_KEYS=16 # Maximum nesting depth in meta_data (default: 2) # META_MAX_DEPTH=2 # Maximum JSON-encoded byte size of meta_data (default: 4096) # META_MAX_BYTES=4096 # Regex patterns for dangerous input (JSON array) # Used to detect and block malicious input patterns # Default patterns: # 1. Shell metacharacters: [;&|`$(){}\[\]<>] # 2. Path traversal: \.\.[/\\] # 3. Control characters: [\x00-\x1f\x7f-\x9f] # Format: JSON array of regex patterns # Project defaults block adds an SQL injection pattern on top of defaults # DANGEROUS_PATTERNS=["[;&|`$(){}\\[\\]<>]", "\\.\\.[\\\\/]", "[\\x00-\\x1f\\x7f-\\x9f]"] # ============================================================================= # Email-Based Authentication # ============================================================================= # Enable email-based authentication system # EMAIL_AUTH_ENABLED=true # Public registration control # When false (default), only admins can create user accounts via /admin/users # When true, anyone can self-register via /auth/email/register # SECURITY: Keep this false in production unless you explicitly need public sign-up # PUBLIC_REGISTRATION_ENABLED=false # Admin login-lockout protection # When true (default), active admin accounts can bypass login lockout # Admin self-demotion and last-active-admin protection are always enforced independently # PROTECT_ALL_ADMINS=true # Platform admin user (bootstrap from environment) # PRODUCTION: Change these to your actual admin credentials! # PLATFORM_ADMIN_EMAIL=admin@example.com # PLATFORM_ADMIN_PASSWORD=changeme # PLATFORM_ADMIN_FULL_NAME=Platform Administrator # Default password for newly created users (bootstrap only) # DEFAULT_USER_PASSWORD=changeme # Argon2id Password Hashing Configuration # Time cost (iterations) - higher = more secure but slower # ARGON2ID_TIME_COST=3 # Memory cost (KB) - higher = more secure but uses more RAM # ARGON2ID_MEMORY_COST=65536 # Parallelism (threads) - typically 1 for web apps # ARGON2ID_PARALLELISM=1 # Password Policy Configuration # PASSWORD_MIN_LENGTH=8 # Minimum password length for regular user accounts (OWASP recommendation: 12) # PASSWORD_MIN_LENGTH_USER=12 # Minimum password length for admin/platform_admin accounts (user baseline + 10 chars for added complexity) # PASSWORD_MIN_LENGTH_PRIVILEGED=22 # Minimum password length for API/service accounts (64-128 bit entropy recommendation) # PASSWORD_MIN_LENGTH_SERVICE=20 # Project defaults block relaxes these for local bootstrap # PASSWORD_REQUIRE_UPPERCASE=true # PASSWORD_REQUIRE_LOWERCASE=true # PASSWORD_REQUIRE_NUMBERS=false # PASSWORD_REQUIRE_SPECIAL=true # Password Change Enforcement # Master switch for all password change enforcement checks # PASSWORD_CHANGE_ENFORCEMENT_ENABLED=true # Force admin to change password after bootstrap # ADMIN_REQUIRE_PASSWORD_CHANGE_ON_BOOTSTRAP=true # Rate Limiting (for local dev/testing, disable to prevent account lockouts) # Enable Redis-backed rate limiting middleware # RATE_LIMITING_ENABLED=true # Enable Redis-backed rate limiting (fallback to in-memory if Redis unavailable) # RATE_LIMITING_REDIS_ENABLED=true # Enable temporary lockout after excessive violations # RATE_LIMIT_LOCKOUT_ENABLED=true # Violations before account lockout triggers # RATE_LIMIT_LOCKOUT_THRESHOLD=5 # Duration of lockout in minutes # RATE_LIMIT_LOCKOUT_DURATION_MINUTES=15 # Per-tier RPM and burst limits (CRITICAL/HIGH/MEDIUM/LOW) # CRITICAL: Auth endpoints (login, register, password reset) # RATE_LIMIT_CRITICAL_RPM=10 # RATE_LIMIT_CRITICAL_BURST=0 # HIGH: Token management, admin, OAuth # RATE_LIMIT_HIGH_RPM=30 # RATE_LIMIT_HIGH_BURST=0 # MEDIUM: MCP, tools, LLM chat # RATE_LIMIT_MEDIUM_RPM=100 # RATE_LIMIT_MEDIUM_BURST=20 # LOW: Health checks, metrics, static content # RATE_LIMIT_LOW_RPM=500 # RATE_LIMIT_LOW_BURST=100 # Detect default password during login and mark user for change # DETECT_DEFAULT_PASSWORD_ON_LOGIN=true # Require password change when using default password # REQUIRE_PASSWORD_CHANGE_FOR_DEFAULT_PASSWORD=true # Enable password complexity validation for new/changed passwords # PASSWORD_POLICY_ENABLED=true # Prevent reusing the current password when changing # PASSWORD_PREVENT_REUSE=true # Number of previous passwords that cannot be reused # PASSWORD_HISTORY_COUNT=5 # Password maximum age in days before expiry forces a change # PASSWORD_MAX_AGE_DAYS=90 # Maximum length for password error messages in URL redirects (prevents browser URL overflow) # PASSWORD_ERROR_MESSAGE_MAX_LENGTH=200 # Account Security Configuration # Maximum failed login attempts before account lockout (recommended: 5) # Production deployments should use the default value (5) or lower to prevent brute-force attacks # Security Note: Per ICACF-16, accounts are locked after 5 failed attempts within 60 minutes # MAX_FAILED_LOGIN_ATTEMPTS=5 # Account lockout duration in minutes (recommended: 60) # Locks account for this duration after MAX_FAILED_LOGIN_ATTEMPTS is exceeded # Security Note: Per ICACF-16, 60-minute lockout prevents brute-force attacks # ACCOUNT_LOCKOUT_DURATION_MINUTES=60 # Send lockout notification emails when an account is locked # ACCOUNT_LOCKOUT_NOTIFICATION_ENABLED=true # Minimum response time for failed login attempts (milliseconds) # Helps reduce timing-based account enumeration # FAILED_LOGIN_MIN_RESPONSE_MS=250 # Self-Service Password Reset # Enable forgot-password and reset-password workflows # Set to false to disable public self-service reset UI/API endpoints. # PASSWORD_RESET_ENABLED=true # Password reset token validity (minutes) # PASSWORD_RESET_TOKEN_EXPIRY_MINUTES=60 # Max password reset requests allowed per email in each window # PASSWORD_RESET_RATE_LIMIT=5 # Rate limit window length (minutes) # PASSWORD_RESET_RATE_WINDOW_MINUTES=15 # Invalidate active sessions after successful password reset # PASSWORD_RESET_INVALIDATE_SESSIONS=true # Minimum response time for forgot-password requests (milliseconds) # Helps reduce timing-based account enumeration # PASSWORD_RESET_MIN_RESPONSE_MS=250 # SMTP Email Delivery (for password reset + lockout notifications) # Enable SMTP delivery # SMTP_ENABLED=false # SMTP_HOST=smtp.example.com # SMTP_PORT=587 # SMTP_USER=noreply@example.com # SMTP_PASSWORD=changeme # SMTP_FROM_EMAIL=noreply@example.com # SMTP_FROM_NAME=ContextForge # Use STARTTLS # SMTP_USE_TLS=true # Use implicit SSL/TLS (set true for port 465) # SMTP_USE_SSL=false # SMTP_TIMEOUT_SECONDS=15 # MCP Client Authentication # Controls JWT authentication for /mcp endpoints # MCP_CLIENT_AUTH_ENABLED=true # TRUST_PROXY_AUTH=false # PROXY_USER_HEADER=X-Authenticated-User # SECURITY NOTE: MCP Access Control Dependencies # Full MCP access control (visibility + team scoping + membership validation) requires: # 1. MCP_CLIENT_AUTH_ENABLED=true (JWT auth extracts user identity and teams) # 2. Valid Bearer token with teams claim for team-scoped access # When MCP_CLIENT_AUTH_ENABLED=false: # - Access control relies on MCP_REQUIRE_AUTH + tool/resource visibility only # - Team membership validation is skipped (no JWT to extract teams from) # - Use TRUST_PROXY_AUTH=true with a reverse proxy for user identification # Used to derive an AES encryption key for secure auth storage # Must be at least 32 characters with high entropy — startup fails otherwise. # Generate with: make init-secrets-patch-env (or python3 -m mcpgateway.scripts.init_secrets) # AUTH_ENCRYPTION_SECRET= # Identity Propagation - forward end-user identity to upstream MCP servers # IDENTITY_PROPAGATION_ENABLED=false # IDENTITY_PROPAGATION_MODE=both # headers, meta, or both # IDENTITY_PROPAGATION_HEADERS_PREFIX=X-Forwarded-User # IDENTITY_SENSITIVE_ATTRIBUTES=["password_hash","internal_id","ssn"] # IDENTITY_SIGN_CLAIMS=false # IDENTITY_CLAIMS_SECRET= # uses JWT_SECRET_KEY if unset # OAuth Configuration # OAUTH_REQUEST_TIMEOUT=30 # OAUTH_MAX_RETRIES=3 # OAuth Security Settings # When MCP servers require OAuth authorization code flow, # tokens are stored per-user to prevent cross-user token access. # Users must individually authorize each OAuth-protected gateway. # Enforce audience mismatches as blocking even when the expected resource was # auto-derived (i.e. no explicit `resource` configured on the gateway and no # learned aud persisted yet from a prior IdP token for the current user). # Default (false) keeps the auto-derived audience check advisory so brand-new # gateways still forward tokens to the upstream MCP server for validation. # Set to true in strict environments where the gateway must reject # cross-resource tokens itself rather than relying on the upstream server # to validate `aud`. See docs/docs/manage/oauth-resource-configuration.md. # OAUTH_REQUIRE_CONFIGURED_RESOURCE=false # ============================================================================= # OAuth Token Storage Backend # ============================================================================= # Pluggable token storage backend for OAuth access/refresh tokens # Options: 'database' (default), 'vault' (HashiCorp Vault) # - database: Store tokens in the primary database (SQLite/PostgreSQL) # - vault: Store tokens in HashiCorp Vault KV v2 (recommended for enterprise) # OAUTH_TOKEN_BACKEND=database # ───────────────────────────────────────────────────────────────────────────── # Vault Configuration (only used when OAUTH_TOKEN_BACKEND=vault) # ───────────────────────────────────────────────────────────────────────────── # Vault server URL # Example: https://vault.example.com:8200 # VAULT_ADDR=http://127.0.0.1:8200 # Vault authentication token (required when using Vault backend) # Phase 1: Static token (root or long-lived token) # Phase 2: AppRole authentication (future enhancement) # Generate with: vault token create -policy=contextforge-oauth # VAULT_TOKEN= # Vault namespace (Vault Enterprise only; leave empty for Community Edition) # Example: engineering/team1 # VAULT_NAMESPACE= # KV v2 mount path where secrets are stored # Must be a KV v2 secrets engine (not KV v1) # Verify with: vault secrets list | grep "kv" # VAULT_KV_MOUNT=secret # Path prefix within the KV mount for organizing OAuth tokens # Full Vault path structure: {mount}/data/{prefix}/{team_id}/{server_id}/{email} # Example: secret/data/contextforge/oauth/team-123/server-456/user@example.com # VAULT_KV_PATH_PREFIX=contextforge/oauth # TLS certificate verification (set false for local dev only) # PRODUCTION: Always keep true to prevent MITM attacks # VAULT_TLS_VERIFY=true # ───────────────────────────────────────────────────────────────────────────── # Vault Token Cache (optional, Vault backend only) # ───────────────────────────────────────────────────────────────────────────── # Enable in-memory caching of OAuth tokens to reduce Vault API calls # When enabled: Reduces read latency from ~25ms (Vault) to ~0.5ms (cache hit) # Trade-off: Tokens may be stale within TTL window if rotated externally # VAULT_TOKEN_CACHE_ENABLED=false # Cache TTL in seconds (how long tokens remain cached) # Shorter = more fresh but higher Vault load; Longer = lower load but potentially stale # Recommended: 300 (5 minutes) for balance between freshness and performance # VAULT_TOKEN_CACHE_TTL=300 # Maximum number of cached token entries before LRU eviction # Each entry ≈ 1 KB (access + refresh token) → 10,000 entries ≈ 10 MB # Tune based on: (active users × OAuth gateways per user) # VAULT_TOKEN_CACHE_MAX_SIZE=10000 # ============================================================================= # OAuth Dynamic Client Registration (DCR) and PKCE # ============================================================================= # Enable Dynamic Client Registration (RFC 7591) # When enabled, ContextForge can automatically register as an OAuth client with Authorization Servers # that support DCR, eliminating the need for manual client credential configuration. # DCR_ENABLED=true # Auto-register when gateway has issuer but no client_id # When true, gateway automatically registers with the Authorization Server when configured # with an issuer URL but no client credentials. # DCR_AUTO_REGISTER_ON_MISSING_CREDENTIALS=true # Default scopes to request during DCR # JSON array of OAuth scopes to request when auto-registering # DCR_DEFAULT_SCOPES=["mcp:read"] # Optional allowlist of issuer URLs for DCR (empty = allow any) # JSON array of trusted Authorization Server issuer URLs # Example: ["https://auth.example.com", "https://auth2.example.com"] # Empty array [] allows DCR with any issuer (not recommended for production) # DCR_ALLOWED_ISSUERS=[] # Token endpoint authentication method for DCR # Options: client_secret_basic (default), client_secret_post, none # - client_secret_basic: Send credentials via HTTP Basic Auth header # - client_secret_post: Send credentials in POST body # - none: Public client (no client secret, PKCE-only) # DCR_TOKEN_ENDPOINT_AUTH_METHOD=client_secret_basic # AS metadata cache TTL in seconds (RFC 8414 discovery) # How long to cache Authorization Server metadata after discovery # DCR_METADATA_CACHE_TTL=3600 # Template for client_name in DCR requests # {gateway_name} will be replaced with the actual gateway name # DCR_CLIENT_NAME_TEMPLATE=ContextForge ({gateway_name}) # Request refresh_token even when AS metadata omits grant_types_supported # Default: false (strict mode - only request refresh_token if AS explicitly advertises support) # Set to true for AS servers that support refresh tokens but don't advertise it in metadata # DCR_REQUEST_REFRESH_TOKEN_WHEN_UNSUPPORTED=false # Enable OAuth AS metadata discovery (RFC 8414) # When enabled, gateway automatically discovers Authorization Server endpoints # from the issuer URL using well-known metadata endpoints # OAUTH_DISCOVERY_ENABLED=true # Preferred PKCE code challenge method # Options: S256 (SHA-256, recommended), plain (not recommended) # PKCE (Proof Key for Code Exchange) is always enabled for Authorization Code flows # OAUTH_PREFERRED_CODE_CHALLENGE_METHOD=S256 # ============================================================================== # SSO (Single Sign-On) Configuration # ============================================================================== # 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 # Google OAuth Configuration # SSO_GOOGLE_ENABLED=false # SSO_GOOGLE_CLIENT_ID=your-google-client-id.googleusercontent.com # SSO_GOOGLE_CLIENT_SECRET=your-google-client-secret # IBM Security Verify OIDC Configuration # SSO_IBM_VERIFY_ENABLED=false # SSO_IBM_VERIFY_CLIENT_ID=your-ibm-verify-client-id # SSO_IBM_VERIFY_CLIENT_SECRET=your-ibm-verify-client-secret # SSO_IBM_VERIFY_ISSUER=https://your-tenant.verify.ibm.com/oidc/endpoint/default # Okta OIDC Configuration # SSO_OKTA_ENABLED=false # SSO_OKTA_CLIENT_ID=your-okta-client-id # SSO_OKTA_CLIENT_SECRET=your-okta-client-secret # SSO_OKTA_ISSUER=https://your-okta-domain.okta.com # SSO_OKTA_SCOPE=openid profile email groups # OKTA_GROUP_MAPPING={"Engineering": "team-uuid-1", "Admins": "team-uuid-2"} # Keycloak OIDC Configuration (with auto-discovery) # SSO_KEYCLOAK_ENABLED=false # SSO_KEYCLOAK_BASE_URL=https://keycloak.example.com # Optional: browser-facing Keycloak URL when gateway uses an internal base URL (e.g., Docker DNS) # SSO_KEYCLOAK_PUBLIC_BASE_URL=https://login.example.com # SSO_KEYCLOAK_REALM=master # SSO_KEYCLOAK_CLIENT_ID=mcp-gateway # SSO_KEYCLOAK_CLIENT_SECRET=your-keycloak-client-secret # SSO_KEYCLOAK_MAP_REALM_ROLES=true # SSO_KEYCLOAK_MAP_CLIENT_ROLES=false # SSO_KEYCLOAK_USERNAME_CLAIM=preferred_username # SSO_KEYCLOAK_EMAIL_CLAIM=email # SSO_KEYCLOAK_GROUPS_CLAIM=groups # Optional: map Keycloak realm roles/groups to Gateway RBAC roles # Example: {"gateway-admin":"platform_admin","gateway-developer":"developer","gateway-viewer":"viewer"} # SSO_KEYCLOAK_ROLE_MAPPINGS={} # Optional: fallback role when no mapping matches # SSO_KEYCLOAK_DEFAULT_ROLE= # If true, map team-scoped roles to the user's personal team automatically # SSO_KEYCLOAK_RESOLVE_TEAM_SCOPE_TO_PERSONAL_TEAM=false # Microsoft Entra ID (Azure AD) OIDC Configuration # See docs/docs/manage/sso-microsoft-entra-id-tutorial.md for detailed setup instructions # SSO_ENTRA_ENABLED=false # SSO_ENTRA_CLIENT_ID=your-entra-application-client-id # SSO_ENTRA_CLIENT_SECRET=your-entra-client-secret-value # SSO_ENTRA_TENANT_ID=your-entra-tenant-id # ADFS Configuration # SSO_ADFS_ENABLED=false # SSO_ADFS_DISPLAY_NAME=your-value # SSO_ADFS_CLIENT_ID=your-adfs-client-id # SSO_ADFS_CLIENT_SECRET=your-adfs-client-secret # pragma: allowlist secret # SSO_ADFS_AUTHORIZATION_URL=https://adfs.ds.example.net/adfs/oauth2/authorize # SSO_ADFS_TOKEN_URL=https://adfs.ds.example.net/adfs/oauth2/token # SSO_ADFS_ISSUER=https://adfs.ds.example.net/adfs # SSO_ADFS_SCOPE=openid profile email # Fallback: Default email domain for ADFS when UPN is plain username (e.g., converts 'user123' to 'user123@company.com') # SSO_ADFS_DEFAULT_EMAIL_DOMAIN=company.com # ───────────────────────────────────────────────────────────────────────────── # EntraID Role Mapping Configuration # ───────────────────────────────────────────────────────────────────────────── # IMPORTANT: Configure group claims in Azure Portal > App Registration > Token Configuration # Add "groups" claim to ID tokens for Security Groups, or use App Roles for semantic names. # # JWT claim containing groups (default: "groups" for Security Groups, use "roles" for App Roles) # SSO_ENTRA_GROUPS_CLAIM=groups # # Admin Groups - members get platform_admin role and is_admin=true (full platform access) # Accepts Object IDs (GUIDs) or App Role names. Case-insensitive matching. # Example with Object IDs: ["a1b2c3d4-1234-5678-90ab-cdef12345678"] # Example with App Roles: ["Admin", "PlatformAdmin"] # SSO_ENTRA_ADMIN_GROUPS=[] # # Role Mappings - map EntraID groups/roles to ContextForge RBAC roles # Available roles: platform_admin (global), team_admin, developer, viewer (team scope) # Format: JSON object {"group-id-or-name": "role-name"} # Example with Object IDs: # SSO_ENTRA_ROLE_MAPPINGS={"e5f6g7h8-1234-5678-90ab-cdef12345678":"developer","i9j0k1l2-1234-5678-90ab-cdef12345678":"team_admin"} # Example with App Roles (recommended - more readable): # SSO_ENTRA_ROLE_MAPPINGS={"Developer":"developer","TeamAdmin":"team_admin","Viewer":"viewer"} # SSO_ENTRA_ROLE_MAPPINGS={} # # Default role for users without any group mappings (default: None = no automatic role) # Set to "viewer" to give all EntraID users read-only access, or leave empty for explicit mapping only # SSO_ENTRA_DEFAULT_ROLE= # # Synchronize role assignments on each login (default: true) # When true: roles are updated based on current group membership (recommended for security) # When false: roles are only assigned on first login (user creation) # SSO_ENTRA_SYNC_ROLES_ON_LOGIN=true # # Group overage fallback (users with >200 Entra groups) # When enabled, ContextForge calls Microsoft Graph /v1.0/me/getMemberObjects # to resolve full group membership during SSO login. # SSO_ENTRA_GRAPH_API_ENABLED=true # SSO_ENTRA_GRAPH_API_TIMEOUT=10 # Maximum number of groups retained from Graph response (0 = no cap) # SSO_ENTRA_GRAPH_API_MAX_GROUPS=0 # Generic OIDC Provider Configuration (Keycloak, Auth0, Authentik, etc.) # SSO_GENERIC_ENABLED=false # SSO_GENERIC_PROVIDER_ID=keycloak # SSO_GENERIC_DISPLAY_NAME=Keycloak # SSO_GENERIC_CLIENT_ID=your-oidc-client-id # SSO_GENERIC_CLIENT_SECRET=your-oidc-client-secret # SSO_GENERIC_AUTHORIZATION_URL=https://keycloak.company.com/auth/realms/master/protocol/openid-connect/auth # SSO_GENERIC_TOKEN_URL=https://keycloak.company.com/auth/realms/master/protocol/openid-connect/token # SSO_GENERIC_USERINFO_URL=https://keycloak.company.com/auth/realms/master/protocol/openid-connect/userinfo # SSO_GENERIC_ISSUER=https://keycloak.company.com/auth/realms/master # SSO_GENERIC_JWKS_URI=https://keycloak.company.com/auth/realms/master/protocol/openid-connect/certs # SSO_GENERIC_SCOPE=openid profile email # JWT claim that contains the user's groups (default: groups) # SSO_GENERIC_GROUPS_CLAIM=groups # Groups that grant platform_admin — checked at login (CSV or JSON list) # SSO_GENERIC_ADMIN_GROUPS=["cf-platform-admin"] # Map IdP group names to ContextForge roles (JSON object) # SSO_GENERIC_ROLE_MAPPINGS={"cf-platform-admin":"platform_admin","cf-dev":"developer"} # Default role assigned to users with no matching group mapping (omit to assign no role) # SSO_GENERIC_DEFAULT_ROLE=platform_viewer # SSO General Settings # SSO_AUTO_CREATE_USERS=true # JSON array of trusted email domains, e.g., ["example.com", "company.org"] # SSO_TRUSTED_DOMAINS=[] # Keep local admin authentication when SSO is enabled # SSO_PRESERVE_ADMIN_AUTH=true # Bootstrap Behavior: Auto-disable providers not in environment config # When true: Providers configured in database but missing from SSO_*_ENABLED # environment variables will be automatically disabled during bootstrap. # This enforces environment config as the single source of truth. # When false: Manually configured providers in database are preserved (default). # IMPORTANT: Enabling this may be a breaking change for existing deployments # with manually configured SSO providers. # Default: false (backward compatible) # SSO_AUTO_DISABLE_UNCONFIGURED_PROVIDERS=false # SSO Issuers Configuration # Optional JSON array of issuer URLs for SSO providers # Example: ["https://idp1.example.com", "https://idp2.example.com"] # Default: null (not set) # SSO_ISSUERS=["https://idp.example.com"] # SSO Admin Assignment Settings # Email domains that automatically get admin privileges, e.g., ["yourcompany.com"] # SSO_AUTO_ADMIN_DOMAINS=[] # GitHub organizations whose members get admin privileges, e.g., ["your-org", "partner-org"] # SSO_GITHUB_ADMIN_ORGS=[] # Google Workspace domains that get admin privileges, e.g., ["company.com"] # SSO_GOOGLE_ADMIN_DOMAINS=[] # Require admin approval for new SSO registrations # SSO_REQUIRE_ADMIN_APPROVAL=false # Accept access tokens from trusted external SSO providers as API/MCP bearer creds. # Each provider must also be opted in (SSOProvider.trusted_for_api_auth). Default off. # SSO_API_TOKEN_AUTH_ENABLED=false # Seconds to cache a provisioned external-IdP identity per token (avoids re-provisioning # every M2M request). Shared across workers when CACHE_TYPE=redis. 0 disables. # EXTERNAL_IDENTITY_CACHE_TTL=60 # ============================================================================= # Personal Teams Configuration # ============================================================================= # Enable automatic personal team creation for new users # AUTO_CREATE_PERSONAL_TEAMS=true # Personal team naming prefix (optional; empty default derives slug from display name) # PERSONAL_TEAM_PREFIX=personal # Allow users to create organizational teams (admins can always create teams) # ALLOW_TEAM_CREATION=true # Allow users to request to join public teams # ALLOW_TEAM_JOIN_REQUESTS=true # Allow team owners to send invitations # ALLOW_TEAM_INVITATIONS=true # Default global role assigned to admin users # DEFAULT_ADMIN_ROLE=platform_admin # Default global role assigned to non-admin users # DEFAULT_USER_ROLE=platform_viewer # Default team role assigned to team owners (e.g. personal team creator) # DEFAULT_TEAM_OWNER_ROLE=team_admin # Default team role assigned to team members # DEFAULT_TEAM_MEMBER_ROLE=viewer # Team Limits # MAX_TEAMS_PER_USER=50 # MAX_MEMBERS_PER_TEAM=100 # Hard ceiling on how many members can be seeded in a single POST /teams request # (the `members` array). Validated at the request boundary before any write; the # per-team MAX_MEMBERS_PER_TEAM limit still applies underneath. # MAX_TEAM_MEMBER_SEEDS=500 # Team Invitation Settings # INVITATION_EXPIRY_DAYS=7 # REQUIRE_EMAIL_VERIFICATION_FOR_INVITES=true # ============================================================================= # Admin UI and API Toggles # ============================================================================= # Enable the web-based Admin UI at /admin # Options: true, false (default) # PRODUCTION: Set to false for security unless needed # Project defaults block enables this for local dev (MCPGATEWAY_UI_ENABLED=true there) # MCPGATEWAY_UI_ENABLED=false # Enable Admin REST API endpoints (/tools, /servers, /resources, etc.) # Options: true, false (default) # Required for: Admin UI functionality, programmatic management # Project defaults block enables this for local dev (MCPGATEWAY_ADMIN_API_ENABLED=true there) # MCPGATEWAY_ADMIN_API_ENABLED=false # Use local CDN assets for airgapped deployments # Options: true, false (default) # When enabled, UI loads CSS/JS from local files instead of external CDNs # Requires container build with downloaded assets (automatic in Containerfile.lite) # MCPGATEWAY_UI_AIRGAPPED=false # Embedded UI mode (hides logout + team selector by default) # Options: true, false (default) # MCPGATEWAY_UI_EMBEDDED=false # Comma-separated list of UI sections to hide # Valid values: overview, servers, gateways, tools, prompts, resources, roots, mcp-registry, metrics, plugins, export-import, logs, version-info, maintenance, teams, users, agents, tokens, settings # Example: MCPGATEWAY_UI_HIDE_SECTIONS=prompts,resources,teams # MCPGATEWAY_UI_HIDE_SECTIONS= # Comma-separated list of header items to hide # Valid values: logout, team_selector, user_identity, theme_toggle # Example: MCPGATEWAY_UI_HIDE_HEADER_ITEMS=logout,team_selector # MCPGATEWAY_UI_HIDE_HEADER_ITEMS= # Admin-specific UI section/header hiding (separate from non-admin lists above) # When unset, admins see all sections. Embedded mode defaults do NOT apply to admins. # Same valid values as MCPGATEWAY_UI_HIDE_SECTIONS / MCPGATEWAY_UI_HIDE_HEADER_ITEMS # Example: MCPGATEWAY_UI_HIDE_SECTIONS_ADMIN=maintenance # MCPGATEWAY_UI_HIDE_SECTIONS_ADMIN= # Example: MCPGATEWAY_UI_HIDE_HEADER_ITEMS_ADMIN=theme_toggle # MCPGATEWAY_UI_HIDE_HEADER_ITEMS_ADMIN= # 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 # MCPGATEWAY_BULK_IMPORT_MAX_TOOLS=200 # Rate limiting for bulk import endpoint (requests per minute) # MCPGATEWAY_BULK_IMPORT_RATE_LIMIT=10 # ============================================================================= # Tool Execution Cancellation # ============================================================================= # Enable gateway-authoritative tool execution cancellation # Options: true (default), false # When enabled: Provides REST API endpoints for cancelling long-running tool executions # - POST /cancellation/cancel - Cancel a running tool execution # - GET /cancellation/status/{id} - Query tool execution status # When disabled: Cancellation endpoints return 404, tool executions not tracked # Features: Real asyncio task interruption, multi-worker coordination via Redis # MCPGATEWAY_TOOL_CANCELLATION_ENABLED=true # ============================================================================= # A2A (Agent-to-Agent) Configuration # ============================================================================= # Enable A2A agent features (true/false) # Allows registration and management of external AI agents # MCPGATEWAY_A2A_ENABLED=true # Maximum number of A2A agents allowed # MCPGATEWAY_A2A_MAX_AGENTS=100 # Default timeout for A2A agent HTTP requests (seconds) # MCPGATEWAY_A2A_DEFAULT_TIMEOUT=30 # Maximum retry attempts for failed A2A agent calls # MCPGATEWAY_A2A_MAX_RETRIES=3 # Enable A2A agent metrics collection (true/false) # MCPGATEWAY_A2A_METRICS_ENABLED=true # ============================================================================= # MCP Server Catalog Configuration # ============================================================================= # Enable MCP server catalog feature # Allows defining a catalog of pre-configured MCP servers in a YAML file # for easy discovery and management via the Admin UI # Options: true (default), false # MCPGATEWAY_CATALOG_ENABLED=true # Path to the catalog configuration file # YAML file containing MCP server definitions # Default: mcp-catalog.yml # MCPGATEWAY_CATALOG_FILE=mcp-catalog.yml # Automatically health check catalog servers on startup and periodically # Options: true (default), false # MCPGATEWAY_CATALOG_AUTO_HEALTH_CHECK=true # Catalog cache TTL in seconds # How long to cache catalog data before refreshing # Default: 3600 (1 hour) # MCPGATEWAY_CATALOG_CACHE_TTL=3600 # Number of catalog servers to display per page # Default: 100 # MCPGATEWAY_CATALOG_PAGE_SIZE=100 # ============================================================================= # Elicitation Support (MCP 2025-06-18) # ============================================================================= # Enable elicitation passthrough - allows upstream MCP servers to request # structured user input through connected clients (e.g., Claude Desktop) # Per MCP spec 2025-06-18, elicitation enables interactive workflows where # servers can dynamically gather information from users during operations # MCPGATEWAY_ELICITATION_ENABLED=true # Default timeout for user responses (seconds) # How long to wait for users to respond to elicitation requests # MCPGATEWAY_ELICITATION_TIMEOUT=60 # Maximum concurrent elicitation requests # Prevents resource exhaustion from too many pending user input requests # MCPGATEWAY_ELICITATION_MAX_CONCURRENT=100 # ============================================================================= # Header Passthrough Configuration # ============================================================================= # SECURITY WARNING: Header passthrough is disabled by default for security. # Only enable if you understand the security implications and have reviewed # which headers should be passed through to backing MCP servers. # ENABLE_HEADER_PASSTHROUGH=false # Enable overwriting of base headers (advanced usage only) # When disabled, passthrough headers cannot override gateway headers like Content-Type, Authorization # ENABLE_OVERWRITE_BASE_HEADERS=false # Default headers to pass through (when feature is enabled) # JSON array format recommended: ["X-Tenant-Id", "X-Trace-Id"] # Comma-separated also supported: X-Tenant-Id,X-Trace-Id # NOTE: Authorization header removed from defaults for security # DEFAULT_PASSTHROUGH_HEADERS=["X-Tenant-Id", "X-Trace-Id"] # Passthrough headers source priority # Controls where header configuration is read from: # - "db": Database wins if configured, env as fallback (default, backward compatible) # - "env": Environment variable always wins (ideal for Kubernetes/containerized deployments) # - "merge": Union of both sources - env provides base, DB can add more headers # PASSTHROUGH_HEADERS_SOURCE=db # ============================================================================= # Security and CORS # ============================================================================= # 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 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 (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) # CORS_ALLOW_CREDENTIALS=true # Environment setting (development/production) - affects security defaults # development: Auto-configures CORS for localhost:3000, localhost:8080, etc. # production: Uses APP_DOMAIN for HTTPS origins, enforces secure cookies # ENVIRONMENT is already defined in Basic Server Configuration section # Domain configuration for production CORS origins # In production, automatically creates origins: https://APP_DOMAIN, https://app.APP_DOMAIN, https://admin.APP_DOMAIN # For production: set to your actual domain (e.g., mycompany.com) # APP_DOMAIN is already defined in Basic Server Configuration section # Security settings for cookies # production: Automatically enables secure cookies regardless of this setting # development: Set to false for HTTP development, true for HTTPS # Project defaults block sets SECURE_COOKIES=false for local dev # SECURE_COOKIES=true # active value set in project-defaults block # Cookie SameSite attribute for CSRF protection # strict: Maximum security, may break some OAuth flows # lax: Good balance of security and compatibility (recommended) # none: Requires Secure=true, allows cross-site usage # COOKIE_SAMESITE=lax # ============================================================================= # Query Parameter Authentication (INSECURE) # ============================================================================= # WARNING: Query parameter authentication exposes API keys in URLs. # API keys may appear in proxy logs, browser history, and server access logs. # See CWE-598: Use of GET Request Method With Sensitive Query Strings. # Only use when the upstream MCP server (e.g., Tavily) requires this method. # Enable query parameter authentication for gateway peers # Options: true, false (default) # SECURITY: Disabled by default. Only enable with explicit allowlist. # INSECURE_ALLOW_QUERYPARAM_AUTH=false # Allowlist of hosts permitted to use query parameter authentication # Format: JSON array of hostnames, e.g., ["mcp.tavily.com", "api.example.com"] # Empty list [] allows any host when feature is enabled (NOT RECOMMENDED) # PRODUCTION: Always configure an explicit allowlist # INSECURE_QUERYPARAM_AUTH_ALLOWED_HOSTS=[] # ============================================================================= # Security Headers Configuration # ============================================================================= # Enable security headers middleware (true/false) # SECURITY_HEADERS_ENABLED=true # X-Frame-Options setting - Controls iframe embedding (also sets CSP frame-ancestors) # DENY: Prevents all iframe embedding (recommended for security) → frame-ancestors 'none' # SAMEORIGIN: Allows embedding from same domain only → frame-ancestors 'self' # "" (empty string) / null / none: Removes iframe restrictions (no headers sent, allows embedding) # ALLOW-FROM uri: Allows specific domain (deprecated, use CSP instead) # ALLOW-ALL: Allows all embedding → frame-ancestors * file: http: https: # # Both X-Frame-Options header and CSP frame-ancestors directive are automatically synced. # Modern browsers prioritize CSP frame-ancestors over X-Frame-Options. # X_FRAME_OPTIONS=DENY # Other security headers (true/false) # X_CONTENT_TYPE_OPTIONS_ENABLED=true # X_XSS_PROTECTION_ENABLED=true # X_DOWNLOAD_OPTIONS_ENABLED=true # HSTS (HTTP Strict Transport Security) settings # HSTS_ENABLED=true # HSTS max age in seconds (31536000 = 1 year) # HSTS_MAX_AGE=31536000 # HSTS_INCLUDE_SUBDOMAINS=true # Remove server identification headers (true/false) # REMOVE_SERVER_HEADERS=true # Enable HTTP Basic Auth for docs endpoints (in addition to Bearer token auth) # Uses the same credentials as BASIC_AUTH_USER and BASIC_AUTH_PASSWORD # DOCS_ALLOW_BASIC_AUTH is already defined in Basic Server Configuration section # ============================================================================= # Response Compression Configuration # ============================================================================= # Enable response compression (Brotli, Zstd, GZip) # Options: true (default), false # Reduces bandwidth by 30-70% for text-based responses (JSON, HTML, CSS, JS) # Automatically negotiates compression algorithm based on client Accept-Encoding header # Priority: Brotli (best compression) > Zstd (fast) > GZip (universal fallback) # COMPRESSION_ENABLED=true # Minimum response size in bytes to compress # Responses smaller than this won't be compressed (compression overhead not worth it) # Default: 500 bytes # Set to 0 to compress all responses # COMPRESSION_MINIMUM_SIZE=500 # GZip compression level (1-9) # 1 = fastest compression, larger files # 6 = balanced (recommended default) # 9 = best compression, slower # Default: 6 # COMPRESSION_GZIP_LEVEL=6 # Brotli compression quality (0-11) # 0-3 = fast compression (lower quality) # 4-9 = balanced compression (recommended) # 10-11 = maximum compression (slower) # Default: 4 (balanced) # Note: Brotli offers 15-20% better compression than GZip at similar speeds # COMPRESSION_BROTLI_QUALITY=4 # Zstd compression level (1-22) # 1-3 = fast compression # 4-9 = balanced compression # 10+ = slower, maximum compression # Default: 3 (fast) # Note: Zstd is the fastest algorithm with good compression ratio # COMPRESSION_ZSTD_LEVEL=3 # ============================================================================= # HTTPX Client Connection Pool Configuration # ============================================================================= # Controls HTTP client settings for outbound requests (federation, health checks, # A2A, SSO, MCP server connections, etc.). Most requests use a shared singleton # client for ~20x better performance. SSE/streaming MCP connections use factory # clients with the same settings for proper connection lifecycle management. # Maximum total connections in the pool (default: 200, range: 10-1000) # Formula: concurrent_outbound_requests × 1.5 # HTTPX_MAX_CONNECTIONS=200 # Maximum keepalive connections (default: 100, range: 1-500) # Connections held open for reuse; typically 50% of max_connections # HTTPX_MAX_KEEPALIVE_CONNECTIONS=100 # Keepalive connection expiry in seconds (default: 30.0, range: 5.0-300.0) # How long idle connections stay in the pool before being closed # HTTPX_KEEPALIVE_EXPIRY=30.0 # Connection timeout in seconds (default: 5.0, range: 1.0-60.0) # Time to establish a new TCP connection (5s for LAN, increase for WAN) # HTTPX_CONNECT_TIMEOUT=5.0 # Read timeout in seconds (default: 120.0, range: 1.0-600.0) # Time to wait for response data after connection established # Set high to accommodate slow MCP tool calls (60-90s+) # HTTPX_READ_TIMEOUT=120.0 # Write timeout in seconds (default: 30.0, range: 1.0-600.0) # Time to wait when sending request data # HTTPX_WRITE_TIMEOUT=30.0 # Pool timeout in seconds (default: 10.0, range: 1.0-120.0) # Time to wait for a connection from the pool (fail fast on exhaustion) # HTTPX_POOL_TIMEOUT=10.0 # Enable HTTP/2 support (default: false) # HTTP/2 provides multiplexing but may not be supported by all upstream servers # HTTPX_HTTP2_ENABLED=false # Admin operations read timeout in seconds (default: 30.0, range: 1.0-120.0) # Shorter timeout for admin UI operations (model fetching, health checks) # Use this to fail fast on admin pages instead of waiting for httpx_read_timeout # HTTPX_ADMIN_READ_TIMEOUT=30.0 # ============================================================================= # Retry Config for HTTP Requests # ============================================================================= # RETRY_MAX_ATTEMPTS=3 # seconds # RETRY_BASE_DELAY=1.0 # seconds # RETRY_MAX_DELAY=60.0 # fraction of delay # RETRY_JITTER_MAX=0.5 # ============================================================================= # Logging # ============================================================================= # Logging verbosity level # Options: DEBUG, INFO, WARNING, ERROR (default), CRITICAL # DEBUG: Detailed diagnostic info (verbose) # INFO: General operational messages # WARNING: Warning messages for potential issues # ERROR: Error messages for failures (recommended for production) # CRITICAL: Only critical failures # PRODUCTION: Use ERROR to minimize I/O overhead and improve performance # LOG_LEVEL=ERROR # 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 # Enable request payload logging for debugging # Options: true, false (default) # When enabled, logs HTTP request method, headers, query params, and body # Sensitive data (passwords, tokens, etc.) is automatically masked # LOG_REQUESTS=false # File write mode when LOG_TO_FILE=true # Options: a+ (append, default), w (overwrite on startup) # LOG_FILEMODE=a+ # Project defaults block sets LOG_FILE and LOG_FOLDER for local debug logs # LOG_FILE=mcpgateway.log # LOG_FOLDER=logs # LOG_ROTATION_ENABLED=false # LOG_MAX_SIZE_MB=1 # LOG_BACKUP_COUNT=5 # LOG_BUFFER_SIZE_MB=1.0 # Masking value used for sensitive data in logs # MASKED_AUTH_VALUE=***** # Maximum request body size to log in detailed mode (bytes) # Controls how much of the request body is parsed and logged when LOG_REQUESTS=true # Separate from LOG_MAX_SIZE_MB which is for log file rotation # Default: 16384 (16KB), Range: 1024-1048576 (1KB-1MB) # LOG_DETAILED_MAX_BODY_SIZE=16384 # Path prefixes to skip from detailed request logging (JSON array or comma-separated) # Use to exclude high-volume or low-value endpoints from logging overhead # Examples: '[]' (empty), '["/metrics","/health"]', or "/metrics,/health" # Default: [] (no additional endpoints skipped beyond built-in health checks) # LOG_DETAILED_SKIP_ENDPOINTS=[] # Sampling rate for detailed request logging (0.0-1.0) # When LOG_REQUESTS=true, only log a fraction of requests to reduce overhead # 1.0 = log all requests, 0.5 = log 50%, 0.1 = log 10% # Default: 1.0 (log all requests when detailed logging is enabled) # LOG_DETAILED_SAMPLE_RATE=1.0 # Enable user identity resolution via database lookup during request logging # When false (default), only uses cached user identity from request context # When true, falls back to DB lookup if no cached identity (adds overhead) # Default: false (avoid implicit DB queries for better performance) # LOG_RESOLVE_USER_IDENTITY=false # ═══════════════════════════════════════════════════════════════════════════════ # Structured Log Database Persistence # ═══════════════════════════════════════════════════════════════════════════════ # Persist structured logs to the database for search, tracing, and metrics. # Options: true, false (default) # # When ENABLED, you get: # - Log Search API (/api/logs/search) - search logs by level, component, user, time # - Request Tracing (/api/logs/trace/{id}) - trace all logs for a correlation ID # - Performance Metrics - aggregated p50/p95/p99 latencies, error rates # - Admin UI log viewer with filtering and search # # When DISABLED: # - Logs only go to console/file (no database writes) # - Better performance (no DB I/O per log entry) # - Log search/trace/metrics APIs return empty results # - Use this if you have an external log aggregator (ELK, Datadog, etc.) # # PERFORMANCE NOTE: Each log entry triggers a synchronous database write. # Disable this in high-throughput production environments or use external logging. # STRUCTURED_LOGGING_ENABLED=true # STRUCTURED_LOGGING_DATABASE_ENABLED=false # STRUCTURED_LOGGING_EXTERNAL_ENABLED=false # Log Search Configuration # Maximum results per log search query # LOG_SEARCH_MAX_RESULTS=1000 # Number of days to retain logs in the database # LOG_RETENTION_DAYS=30 # External Log Integration Configuration # Send logs to Elasticsearch # ELASTICSEARCH_ENABLED=false # ELASTICSEARCH_URL= # ELASTICSEARCH_INDEX_PREFIX=mcpgateway-logs # Send logs to syslog # SYSLOG_ENABLED=false # SYSLOG_HOST= # SYSLOG_PORT=514 # Send logs to webhook endpoints # WEBHOOK_LOGGING_ENABLED=false # WEBHOOK_LOGGING_URLS=[] # SIEM Export Configuration (security/audit event export pipeline) # SIEM_EXPORT_ENABLED=false # SIEM_EXPORT_BATCH_SIZE=100 # SIEM_EXPORT_FLUSH_INTERVAL_SECONDS=5 # SIEM_EXPORT_QUEUE_MAX_SIZE=10000 # SIEM_EXPORT_MAX_RETRIES=10 # SIEM_EXPORT_BACKOFF_MAX_SECONDS=60 # SIEM_EXPORT_BACKPRESSURE_POLICY=drop_oldest # SIEM_EXPORT_EVENT_SOURCES=["auth","security","audit"] # SIEM_EXPORT_STREAM_NAME=mcpgateway:siem:events # SIEM_EXPORT_CONSUMER_GROUP=siem-exporters # SIEM_EXPORT_URL_ALLOWLIST=[] # SIEM_EXPORT_REDACT_FIELDS=["user_email","authorization","token","password","secret","api_key"] # SIEM_DESTINATIONS=[] # SIEM_DESTINATIONS_FILE= # Correlation ID / Request Tracking # Enable automatic correlation ID tracking for unified request tracing # Options: true (default), false # CORRELATION_ID_ENABLED=true # HTTP header name for correlation ID (default: X-Correlation-ID) # CORRELATION_ID_HEADER=X-Correlation-ID # Preserve incoming correlation IDs from clients (default: true) # CORRELATION_ID_PRESERVE=true # Include correlation ID in HTTP response headers (default: true) # CORRELATION_ID_RESPONSE_HEADER=true # ═══════════════════════════════════════════════════════════════════════════════ # Database Query Logging (N+1 Detection) # ═══════════════════════════════════════════════════════════════════════════════ # Enable database query logging to file for N+1 detection and performance analysis # Use: make dev-query-log (starts server with logging enabled) # Use: make query-log-analyze (analyze logs for N+1 patterns) # DB_QUERY_LOG_ENABLED=false # DB_QUERY_LOG_FILE=logs/db-queries.log # DB_QUERY_LOG_JSON_FILE=logs/db-queries.jsonl # DB_QUERY_LOG_FORMAT=both # DB_QUERY_LOG_MIN_QUERIES=1 # DB_QUERY_LOG_INCLUDE_PARAMS=false # DB_QUERY_LOG_DETECT_N1=true # DB_QUERY_LOG_N1_THRESHOLD=3 # ============================================================================= # Metrics Aggregation Configuration # ============================================================================= # Aggregates structured logs into performance metrics on a schedule. # Requires STRUCTURED_LOGGING_DATABASE_ENABLED=true. # METRICS_AGGREGATION_ENABLED=true # Hours of structured logs to backfill into metrics on startup # METRICS_AGGREGATION_BACKFILL_HOURS=6 # Time window for metrics aggregation (minutes) # METRICS_AGGREGATION_WINDOW_MINUTES=5 # Seconds between aggregation runs (defaults to METRICS_AGGREGATION_WINDOW_MINUTES * 60). # Raise to reduce DB pressure on multi-worker deployments. # METRICS_AGGREGATION_INTERVAL_SECONDS=300 # Automatically start the aggregation loop on application startup # METRICS_AGGREGATION_AUTO_START=false # ============================================================================= # Execution Metrics Recording # ============================================================================= # Controls whether tool/resource/prompt/server/A2A execution metrics are written # to the database. Each MCP operation (tool call, resource read, etc.) creates # one database row with: entity_id, timestamp, response_time (seconds), is_success, error_message. # # Disable if you rely entirely on external observability (ELK, Datadog, Splunk) # to reduce database I/O overhead. # # Note: This does NOT affect: # - Log aggregation (METRICS_AGGREGATION_ENABLED) - aggregates StructuredLogEntry into PerformanceMetric # - Prometheus metrics (ENABLE_METRICS) - /metrics endpoint for Prometheus scraping # - Observability metrics (OBSERVABILITY_METRICS_ENABLED) - internal observability system # # To disable log aggregation as well, also set METRICS_AGGREGATION_ENABLED=false # DB_METRICS_RECORDING_ENABLED=true # ============================================================================= # Metrics Buffer Configuration # ============================================================================= # Batches tool/resource/prompt/server metric writes to reduce DB pressure under load # Enable buffered metrics writes (default: true) # When enabled, metrics are accumulated in memory and flushed periodically # METRICS_BUFFER_ENABLED=true # Seconds between automatic metrics buffer flushes (default: 60, range: 5-300) # Lower values = more frequent writes, higher values = better batching # METRICS_BUFFER_FLUSH_INTERVAL=60 # Maximum buffered metrics before forced flush (default: 1000, range: 100-10000) # Prevents unbounded memory growth under very high load # METRICS_BUFFER_MAX_SIZE=1000 # Metrics Cache Configuration # ============================================================================= # Caches aggregate metrics queries to reduce database load under high traffic # See GitHub Issue #1734 for performance optimization details # Enable in-memory caching for aggregate metrics queries (default: true) # When enabled, aggregate_metrics() results are cached to reduce database load # METRICS_CACHE_ENABLED=true # TTL for cached aggregate metrics in seconds (default: 60, range: 1-300) # Lower values = fresher data, higher values = better performance # Recommended: 60-300 seconds for high-traffic deployments (see Issue #1906) # METRICS_CACHE_TTL_SECONDS=60 # Metrics Cleanup Configuration # ============================================================================= # Automatically deletes old metrics data to prevent unbounded table growth # Enable automatic cleanup of old metrics data (default: true) # METRICS_CLEANUP_ENABLED=true # Days to retain raw metrics when rollup is disabled (default: 7, range: 1-365) # This is a fallback - when METRICS_DELETE_RAW_AFTER_ROLLUP=true, raw metrics # are deleted based on METRICS_DELETE_RAW_AFTER_ROLLUP_HOURS instead. # METRICS_RETENTION_DAYS=7 # Hours between automatic cleanup runs (default: 1, range: 1-168) # METRICS_CLEANUP_INTERVAL_HOURS=1 # Batch size for metrics deletion (default: 10000, range: 100-100000) # Larger batches are faster but may cause longer table locks # METRICS_CLEANUP_BATCH_SIZE=10000 # Milliseconds to sleep between batch DELETEs (default: 50, range: 0-5000) # Increase to reduce DB pressure on high-traffic deployments (0 = no sleep) # METRICS_CLEANUP_BATCH_SLEEP_MS=50 # Metrics Rollup Configuration # ============================================================================= # Aggregates raw metrics into hourly summaries for efficient historical queries # Rollups preserve counts, averages, and percentiles (p50, p95, p99) # Enable hourly metrics rollup for efficient historical queries (default: true) # METRICS_ROLLUP_ENABLED=true # Hours between rollup runs (default: 1, range: 1-24) # METRICS_ROLLUP_INTERVAL_HOURS=1 # Days to retain hourly rollup data (default: 365, range: 30-3650) # METRICS_ROLLUP_RETENTION_DAYS=365 # Hours to re-process on each rollup run to catch late-arriving data (default: 1, range: 1-48) # Smaller = less CPU/IO overhead, larger = more tolerance for delayed metrics # METRICS_ROLLUP_LATE_DATA_HOURS=1 # Delete raw metrics after hourly rollup exists (default: true) # When true, raw metrics older than METRICS_DELETE_RAW_AFTER_ROLLUP_HOURS are # deleted once hourly rollups exist. Rollups preserve all analytics. # # Set to false only if you need raw metrics indefinitely (e.g., exact error # messages, individual request debugging without external observability). # # If using ELK, Datadog, Splunk, or similar platforms for debugging, keep this # true - your external platform handles detailed logs and traces. # METRICS_DELETE_RAW_AFTER_ROLLUP=true # Hours to retain raw metrics when hourly rollup exists (default: 1, range: 1-8760) # After this period, raw metrics are deleted but hourly rollups remain. # Increase to 168 if you need raw data for debugging without external observability. # METRICS_DELETE_RAW_AFTER_ROLLUP_HOURS=1 # Authentication Cache Configuration # ============================================================================= # Caches authentication data (user, team, revocation) to reduce database queries # Uses Redis when available, falls back to in-memory cache # Applies to both Python MCP and Rust MCP because public MCP auth still runs in Python first # Enable Redis/in-memory caching for authentication data (default: true) # Significantly reduces database queries during authentication # Disabling this also disables the shared auth cache benefit for RUST_MCP_MODE=edge/full # AUTH_CACHE_ENABLED=true # TTL in seconds for cached user data (default: 60, range: 10-300) # Also affects MCP Streamable HTTP auth, including Rust-fronted MCP requests # AUTH_CACHE_USER_TTL=60 # TTL in seconds for token revocation cache (default: 30, range: 5-120) # Security-critical: keep short to limit exposure window for revoked tokens # Also affects MCP auth on both Python and Rust runtime modes # AUTH_CACHE_REVOCATION_TTL=30 # TTL in seconds for team membership cache (default: 60, range: 10-300) # AUTH_CACHE_TEAM_TTL=60 # TTL in seconds for user role in team cache (default: 60, range: 10-300) # Caches get_user_role_in_team() which is called 11+ times per team operation # AUTH_CACHE_ROLE_TTL=60 # Enable caching for get_user_teams() (default: true) # Set to false to disable teams list caching (useful for debugging) # Also affects session-token MCP auth on Python and Rust modes # AUTH_CACHE_TEAMS_ENABLED=true # TTL in seconds for user teams list cache (default: 60, range: 10-300) # Caches get_user_teams() which is called 20+ times per request for auth checks # AUTH_CACHE_TEAMS_TTL=60 # Batch auth DB queries into single call (default: true) # Reduces 3 separate queries to 1, improving performance under load # Streamable HTTP MCP auth uses this too before falling back to per-query checks # AUTH_CACHE_BATCH_QUERIES=true # Registry Cache Configuration # ============================================================================= # Caches registry list endpoints (tools, prompts, resources, agents, servers, gateways) # Uses Redis when available, falls back to in-memory cache # Reduces DB queries for frequently accessed list endpoints # Enable registry caching (default: true) # REGISTRY_CACHE_ENABLED=true # TTL in seconds for tools list cache (default: 20, range: 5-300) # REGISTRY_CACHE_TOOLS_TTL=20 # TTL in seconds for prompts list cache (default: 15, range: 5-300) # REGISTRY_CACHE_PROMPTS_TTL=15 # TTL in seconds for resources list cache (default: 15, range: 5-300) # REGISTRY_CACHE_RESOURCES_TTL=15 # TTL in seconds for A2A agents list cache (default: 20, range: 5-300) # REGISTRY_CACHE_AGENTS_TTL=20 # TTL in seconds for servers list cache (default: 20, range: 5-300) # REGISTRY_CACHE_SERVERS_TTL=20 # TTL in seconds for gateways list cache (default: 20, range: 5-300) # REGISTRY_CACHE_GATEWAYS_TTL=20 # TTL in seconds for catalog servers list cache (default: 300, range: 60-600) # Longer TTL since external catalog changes infrequently # REGISTRY_CACHE_CATALOG_TTL=300 # Tool Lookup Cache Configuration # ============================================================================= # Caches tool lookup by name in the invoke_tool hot path # Uses in-memory L1 cache and optional Redis L2 cache when CACHE_TYPE=redis # Enable tool lookup caching (default: true) # TOOL_LOOKUP_CACHE_ENABLED=true # TTL in seconds for tool lookup cache entries (default: 60, range: 5-600) # TOOL_LOOKUP_CACHE_TTL_SECONDS=60 # TTL in seconds for negative cache entries (default: 10, range: 1-60) # Used for missing/inactive/offline tool lookups # TOOL_LOOKUP_CACHE_NEGATIVE_TTL_SECONDS=10 # Max entries for in-memory L1 tool cache (default: 10000, range: 100-1000000) # TOOL_LOOKUP_CACHE_L1_MAXSIZE=10000 # Enable Redis L2 cache when CACHE_TYPE=redis (default: true) # TOOL_LOOKUP_CACHE_L2_ENABLED=true # Admin Stats Cache Configuration # ============================================================================= # Caches admin dashboard statistics (entity counts, observability metrics) # Reduces expensive aggregate queries under dashboard load # Enable admin stats caching (default: true) # ADMIN_STATS_CACHE_ENABLED=true # TTL in seconds for system stats cache (default: 60, range: 10-300) # ADMIN_STATS_CACHE_SYSTEM_TTL=60 # TTL in seconds for observability stats cache (default: 30, range: 10-120) # ADMIN_STATS_CACHE_OBSERVABILITY_TTL=30 # TTL in seconds for tags listing cache (default: 120, range: 30-600) # ADMIN_STATS_CACHE_TAGS_TTL=120 # TTL in seconds for plugin stats cache (default: 120, range: 30-600) # ADMIN_STATS_CACHE_PLUGINS_TTL=120 # TTL in seconds for performance aggregates cache (default: 60, range: 15-300) # ADMIN_STATS_CACHE_PERFORMANCE_TTL=60 # Team Member Count Cache # Reduces N+1 queries in admin UI team listings # Enable team member count caching (default: true) # TEAM_MEMBER_COUNT_CACHE_ENABLED=true # TTL in seconds for team member count cache (default: 300, range: 30-3600) # TEAM_MEMBER_COUNT_CACHE_TTL=300 # 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 # Enable legacy WebSocket JSON-RPC relay endpoint (/ws) # SECURITY: Disabled by default. Enable only for clients that require /ws. # MCPGATEWAY_WS_RELAY_ENABLED=false # Enable reverse-proxy transport endpoints (/reverse-proxy/*) # SECURITY: Disabled by default. Enable only when using mcpgateway.reverse_proxy. # MCPGATEWAY_REVERSE_PROXY_ENABLED=false # 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 # Streamable HTTP — MCP GET stream support # Enable the GET /mcp stream endpoint for MCP Streamable HTTP transport # Default: true # MCP_GET_STREAM_ENABLED=true # TTL in seconds for the per-session GET-stream listener claim # Refreshed by heartbeat while the GET handler holds the connection # Default: 30 # MCP_GET_STREAM_LISTENER_TTL_SECONDS=30 # Maximum bytes the body-peek helper will buffer per POST before falling through # to the streaming receive path. NEVER rejects the request — only bounds peek memory. # Default: 4194304 (4MB) # MCP_BODY_PEEK_MAX_BYTES=4194304 # ───────────────────────────────────────────────────────────────────────────── # SSE Connection Protection (CPU Spin Loop Mitigation - Layer 1) # ───────────────────────────────────────────────────────────────────────────── # These settings detect and close dead SSE connections before they trigger # CPU spin loops in anyio's _deliver_cancellation method. # # Part of Issue #2360 mitigation. See: docs/docs/operations/cpu-spin-loop-mitigation.md # Upstream issue: https://github.com/agronholm/anyio/issues/695 # SSE send timeout in seconds # Timeout for ASGI send() calls - protects against sends that hang indefinitely # when client connection is in a bad state. Does NOT affect MCP server response times. # Set to 0 to disable. Default matches keepalive interval. # SSE_SEND_TIMEOUT=30.0 # SSE rapid yield detection # If more than SSE_RAPID_YIELD_MAX yields occur within SSE_RAPID_YIELD_WINDOW_MS, # the connection is assumed dead and closed. Set SSE_RAPID_YIELD_MAX=0 to disable. # SSE_RAPID_YIELD_WINDOW_MS=1000 # SSE_RAPID_YIELD_MAX=50 # Streaming HTTP Configuration # Enable stateful sessions (stores session state server-side) # Options: true, false (default) # false: Stateless mode (better for scaling) # true: Stateful mode (requires CACHE_TYPE=redis for multi-worker deployments) # USE_STATEFUL_SESSIONS=false # Multi-Worker Session Affinity (ADR-038) # Routes client requests to the same worker for session continuity in multi-worker deployments # Requires: CACHE_TYPE=redis, USE_STATEFUL_SESSIONS=true, Redis accessible at REDIS_URL # IMPORTANT: Redis must be enabled (CACHE_TYPE=redis) for session affinity to work # MCPGATEWAY_SESSION_AFFINITY_ENABLED=false # Session ownership TTL in seconds (default: 300 = 5 minutes) # How long a worker owns a session before it expires # MCPGATEWAY_SESSION_AFFINITY_TTL=300 # Forwarded request timeout in seconds (default: 30) # Timeout when forwarding requests between workers via Redis Pub/Sub # MCPGATEWAY_POOL_RPC_FORWARD_TIMEOUT=30 # Enable JSON response format for streaming HTTP # Options: true (default), false # true: Return JSON responses, false: Return SSE stream # JSON_RESPONSE_ENABLED=true # Event store configuration for stateful sessions # Ring buffer size per stream (default: 100) # Controls how many events are kept in memory before oldest are evicted # STREAMABLE_HTTP_MAX_EVENTS_PER_STREAM=100 # Stream TTL in seconds (default: 3600 = 1 hour) # How long event streams are kept in Redis before automatic cleanup # STREAMABLE_HTTP_EVENT_TTL=3600 # Federation Configuration # Timeout for federation requests in seconds # Default: 120 seconds (matches config.py) # FEDERATION_TIMEOUT=120 # Resource Configuration # 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 # TOOL_RATE_LIMIT=100 # TOOL_CONCURRENT_LIMIT=10 # GATEWAY_TOOL_NAME_SEPARATOR=- # Maximum length of response text returned for non-JSON REST API responses # Longer responses are truncated to prevent exposing excessive sensitive data # Default: 5000 characters, Range: 1000-100000 # REST_RESPONSE_TEXT_MAX_LENGTH=5000 # jq filter sandbox for tool jsonpath_filter execution. # Filters run in a forked worker with a cleared environment and a time limit. # Options: subprocess (default, safe), inprocess (unsafe, no environment scrub, no time limit) # JQ_FILTER_EXECUTION=subprocess # JQ_FILTER_TIMEOUT_SECONDS=2.0 # JQ_FILTER_WORKERS=2 # Prompt Configuration # PROMPT_CACHE_SIZE=100 # MAX_PROMPT_SIZE=102400 # PROMPT_RENDER_TIMEOUT=10 # ============================================================================= # MCP Server Health Check Configuration # ============================================================================= # Interval between health checks in seconds (default: 60) # Project defaults block sets HEALTH_CHECK_INTERVAL=300 for local dev # HEALTH_CHECK_INTERVAL=60 # Health check timeout in seconds (default: 30) # HEALTH_CHECK_TIMEOUT=30 # Per-check timeout (seconds) to bound total time of one gateway health check (default: 30.0) # GATEWAY_HEALTH_CHECK_TIMEOUT=30.0 # Consecutive failures before marking gateway offline (default: 3) # UNHEALTHY_THRESHOLD=3 # Gateway URL validation timeout in seconds (default: 5) # GATEWAY_VALIDATION_TIMEOUT=5 # Maximum redirects allowed during gateway validation (default: 5) # GATEWAY_MAX_REDIRECTS=5 # Maximum concurrent health checks per worker (default: 10) # MAX_CONCURRENT_HEALTH_CHECKS=10 # ----------------------------------------------------------------------------- # Async Gateway Lifecycle # ----------------------------------------------------------------------------- # When enabled, gateway create/update/delete requests return 202 Accepted after # persisting the gateway lifecycle state. Background workers then initialize or # delete gateways from the database-backed pending/deleting queues. # # Disabled by default to preserve existing synchronous gateway behavior. # GATEWAY_ASYNC_LIFECYCLE_ENABLED=false # Worker polling interval in seconds for pending/deleting gateways (default: 5.0) # GATEWAY_ASYNC_LIFECYCLE_POLL_INTERVAL=5.0 # Timeout in seconds for one async gateway initialization attempt (default: 30.0) # GATEWAY_ASYNC_LIFECYCLE_ATTEMPT_TIMEOUT=30.0 # Lease TTL in seconds for database-backed lifecycle claims (default: 90.0) # GATEWAY_ASYNC_LIFECYCLE_LEASE_SECONDS=90.0 # Bounded shutdown wait in seconds for lifecycle task cancellation (default: 5.0) # GATEWAY_ASYNC_LIFECYCLE_SHUTDOWN_TIMEOUT=5.0 # ----------------------------------------------------------------------------- # Auto-Refresh / Polling (requires health checks above) # ----------------------------------------------------------------------------- # Automatically re-fetch tools, prompts, and resources from upstream MCP # servers during health-check cycles. When disabled (default), tool lists # are only updated on manual refresh or gateway registration. # # AUTO_REFRESH_SERVERS=false # GATEWAY_AUTO_REFRESH_INTERVAL=300 # interval in seconds (minimum: 60) # ----------------------------------------------------------------------------- # Hot/Cold Server Classification (requires auto-refresh + Redis) # ----------------------------------------------------------------------------- # Classifies upstream servers by MCP session pool usage: # hot (top 20% by recent usage) → polled at 1x GATEWAY_AUTO_REFRESH_INTERVAL # cold (remaining 80%) → polled at 3x GATEWAY_AUTO_REFRESH_INTERVAL # # Requires Redis for multi-worker leader election and state sharing. # Falls back to local-only (always-poll) in single-worker mode (make dev). # Poll intervals are auto-derived — no additional config needed. # # HOT_COLD_CLASSIFICATION_ENABLED=false # 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 # ============================================================================= # MCP Session Pool Configuration # ============================================================================= # ───────────────────────────────────────────────────────────────────────────── # Cleanup Timeouts (CPU Spin Loop Mitigation - Layer 2) # ───────────────────────────────────────────────────────────────────────────── # Limit how long cleanup waits for stuck tasks. Shorter timeouts = faster # recovery from spin loops but may interrupt legitimate cleanup. # # Part of Issue #2360 mitigation. See: docs/docs/operations/cpu-spin-loop-mitigation.md # Upstream issue: https://github.com/agronholm/anyio/issues/695 # Timeout for session/transport cleanup operations (seconds). # Controls how long to wait for session.__aexit__() and transport.__aexit__() # when closing sessions. # # IMPORTANT: Does NOT affect tool execution time - only cleanup of # idle/released sessions. Tool execution uses TOOL_TIMEOUT instead. # # Timeout for SSE task group cleanup (seconds). # Controls how long to wait for internal tasks to respond before forcing cleanup. # Only affects cancelled connections, not normal SSE operation. # Default: 5.0 # SSE_TASK_GROUP_CLEANUP_TIMEOUT=5.0 # ============================================================================= # EXPERIMENTAL: anyio Monkey-Patch (CPU Spin Loop Mitigation - Layer 3) # ============================================================================= # Last resort workaround that patches anyio to limit _deliver_cancellation iterations. # Use only if Layers 1-2 don't fully resolve the issue. # # Part of Issue #2360 mitigation. See: docs/docs/operations/cpu-spin-loop-mitigation.md # Upstream issue: https://github.com/agronholm/anyio/issues/695 # # WARNING: This is EXPERIMENTAL and may be removed when upstream fixes the issue. # ============================================================================= # Trade-offs when enabled: # - Prevents indefinite CPU spin (good) # - May leave some tasks uncancelled (usually harmless) # - Worker recycling (GUNICORN_MAX_REQUESTS) cleans up orphaned tasks # # Default: false # ANYIO_CANCEL_DELIVERY_PATCH_ENABLED=false # Maximum iterations for _deliver_cancellation before forcing termination. # Only used when ANYIO_CANCEL_DELIVERY_PATCH_ENABLED=true. # - Higher (100+) = more attempts to cancel, longer potential spin # - Lower (50) = faster recovery, more orphaned tasks # Default: 100 # ANYIO_CANCEL_DELIVERY_MAX_ITERATIONS=100 # ============================================================================= # Default Root Paths # ============================================================================= # Default root URIs (JSON array). Values are validated by the root URI policy below. # Compatibility note: roots are disabled by default until accepted metadata schemes # are explicitly allowlisted. MCP filesystem roots require file:// opt-in plus prefixes. # Example: ["https://example.com/project"] # Default: [] # DEFAULT_ROOTS=[] # Root URI metadata policy. ROOT_ALLOWED_SCHEMES accepts scheme names without ://. # Supported non-file schemes: http, https, ws, wss. # Default: [] # ROOT_ALLOWED_SCHEMES=[] # Example: # ROOT_ALLOWED_SCHEMES=["https","wss"] # file:// roots are disabled by default and require explicit POSIX prefixes. # Default: false # ROOT_ALLOW_FILE_SCHEME=false # Default: [] # ROOT_ALLOWED_FILE_PREFIXES=[] # Example: # ROOT_ALLOW_FILE_SCHEME=true # ROOT_ALLOWED_FILE_PREFIXES=["/workspace","/srv/data"] # ============================================================================= # OpenTelemetry Observability Configuration # ============================================================================= # Enable distributed tracing and metrics collection # Options: true (default), false # OTEL_ENABLE_OBSERVABILITY=false # 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 # Project defaults block sets OTEL_EXPORTER_OTLP_ENDPOINT for local tracing # 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 # OTEL_EXPORTER_ZIPKIN_ENDPOINT=http://localhost:9411/api/v2/spans # OTEL_SERVICE_NAME=mcp-gateway # OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,environment=production # OTEL_BSP_MAX_QUEUE_SIZE=2048 # OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512 # OTEL_BSP_SCHEDULE_DELAY=5000 # ============================================================================= # W3C Baggage Configuration # ============================================================================= # OpenTelemetry baggage allows propagating context across service boundaries. # Use this to extract HTTP headers into baggage for distributed tracing. # # Common use cases: # - Multi-tenant request tracking (tenant IDs, organization IDs) # - User context propagation (user IDs, session IDs) # - Request correlation (trace IDs, request IDs) # - Feature flags and A/B testing metadata # - Security context (authentication realm, authorization scope) # Enable extraction of HTTP headers into OpenTelemetry baggage # Options: true, false (default) # When enabled, headers matching OTEL_BAGGAGE_HEADER_MAPPINGS are extracted # and attached to the current span's baggage for downstream propagation # OTEL_BAGGAGE_ENABLED=false # JSON array mapping HTTP headers to baggage keys # Format: [{"header_name": "X-Header", "baggage_key": "baggage.key"}] # # IMPORTANT: Baggage keys should use dot notation for namespacing # Recommended prefixes: tenant., user., request., feature., security. # # Example 1: Basic multi-tenant tracking # OTEL_BAGGAGE_HEADER_MAPPINGS='[ # {"header_name": "X-Tenant-ID", "baggage_key": "tenant.id"}, # {"header_name": "X-Organization-ID", "baggage_key": "tenant.org_id"} # ]' # # Example 2: User context and request correlation # OTEL_BAGGAGE_HEADER_MAPPINGS='[ # {"header_name": "X-User-ID", "baggage_key": "user.id"}, # {"header_name": "X-User-Email", "baggage_key": "user.email"}, # {"header_name": "X-Session-ID", "baggage_key": "user.session_id"}, # {"header_name": "X-Request-ID", "baggage_key": "request.id"}, # {"header_name": "X-Correlation-ID", "baggage_key": "request.correlation_id"} # ]' # # Example 3: Comprehensive tracking (multi-tenant + user + features) # OTEL_BAGGAGE_HEADER_MAPPINGS='[ # {"header_name": "X-Tenant-ID", "baggage_key": "tenant.id"}, # {"header_name": "X-Tenant-Name", "baggage_key": "tenant.name"}, # {"header_name": "X-User-ID", "baggage_key": "user.id"}, # {"header_name": "X-User-Role", "baggage_key": "user.role"}, # {"header_name": "X-Request-ID", "baggage_key": "request.id"}, # {"header_name": "X-Feature-Flags", "baggage_key": "feature.flags"}, # {"header_name": "X-AB-Test-Variant", "baggage_key": "feature.ab_variant"} # ]' # # Example 4: Security and compliance tracking # OTEL_BAGGAGE_HEADER_MAPPINGS='[ # {"header_name": "X-Auth-Realm", "baggage_key": "security.realm"}, # {"header_name": "X-Auth-Scope", "baggage_key": "security.scope"}, # {"header_name": "X-Client-IP", "baggage_key": "security.client_ip"}, # {"header_name": "X-Forwarded-For", "baggage_key": "security.forwarded_for"}, # {"header_name": "X-Compliance-Level", "baggage_key": "security.compliance_level"} # ]' # Propagate baggage to external services # Options: true, false (default: false) # # ⚠️ SECURITY WARNING: DISABLED BY DEFAULT FOR GOOD REASON ⚠️ # # When enabled, baggage is propagated to ALL external HTTP requests made by # the gateway, including: # - Upstream MCP servers # - External APIs and webhooks # - Third-party services # - Plugin endpoints # # SECURITY IMPLICATIONS: # 1. DATA LEAKAGE: Baggage may contain sensitive tenant/user identifiers that # external services should NOT receive. This can leak: # - Internal tenant IDs and organizational structure # - User identifiers and session tokens # - Internal request correlation IDs # - Feature flags revealing your product roadmap # # 2. COMPLIANCE VIOLATIONS: Propagating user data to third parties without # consent may violate GDPR, CCPA, HIPAA, or other regulations. # # 3. ATTACK SURFACE: Malicious external services could harvest baggage data # to map your internal architecture or user base. # # 4. TRUST BOUNDARY: External services are outside your security perimeter. # Baggage should only cross trust boundaries when explicitly required. # # WHEN TO ENABLE: # - Only enable if you control ALL external services the gateway calls # - Use allowlists to restrict which external hosts receive baggage # - Audit external service access to baggage data # - Document which external services receive what baggage keys # - Consider using separate baggage keys for internal vs external propagation # # ALTERNATIVES TO CONSIDER: # - Use service-specific headers instead of baggage for external calls # - Implement a baggage filtering proxy for external requests # - Use separate observability backends for internal vs external traces # # OTEL_BAGGAGE_PROPAGATE_TO_EXTERNAL=false # Maximum number of baggage items (default: 32) # Prevents unbounded baggage growth and DoS attacks # W3C Baggage spec recommends keeping this low for performance # OTEL_BAGGAGE_MAX_ITEMS=32 # Maximum total baggage size in bytes (default: 8192) # Total size of all baggage key-value pairs combined # Prevents header size attacks and network overhead # W3C Baggage spec recommends 8KB limit for HTTP header compatibility # OTEL_BAGGAGE_MAX_SIZE_BYTES=8192 # Log baggage items that were rejected (invalid key/value, size limit exceeded) # Default: false — enable for debugging baggage propagation issues # OTEL_BAGGAGE_LOG_REJECTED=false # Log when baggage values are sanitized (control characters stripped) # Default: false — enable for auditing baggage sanitization # OTEL_BAGGAGE_LOG_SANITIZATION=false # Prometheus Metrics Configuration # Enable Prometheus-compatible metrics endpoint at /metrics/prometheus # Options: true, false (default) # When true: Exposes metrics at /metrics/prometheus (requires JWT authentication) # When false: Returns HTTP 503 on metrics endpoint # Enable this only when a Prometheus stack is configured to scrape. # Prometheus scrape config needs: authorization: { type: Bearer, credentials: } # Generate a scrape token: python -m mcpgateway.utils.create_jwt_token --username prometheus@monitoring --exp 0 --secret $JWT_SECRET_KEY # ENABLE_METRICS=false # Comma-separated regex patterns for endpoints to exclude from metrics collection # Use this to avoid high-cardinality issues with dynamic paths or reduce overhead # Examples: # - Exclude SSE endpoints: /servers/.*/sse # - Exclude static files: /static/.* # - Exclude health checks: .*health.* # - Multiple patterns: /servers/.*/sse,/static/.*,.*health.* # Default: "" (no exclusions) # METRICS_EXCLUDED_HANDLERS= # Prometheus metrics namespace (prefix for all metric names) # Used to group metrics by application or organization # Example: mycompany_gateway_http_requests_total # Default: "default" # METRICS_NAMESPACE=default # Prometheus metrics subsystem (secondary prefix for metric names) # Used for further categorization within namespace # Example: mycompany_api_http_requests_total (if subsystem=api) # Default: "" (no subsystem) # METRICS_SUBSYSTEM= # Custom static labels for app_info gauge metric # Format: comma-separated "key=value" pairs (low-cardinality values only) # WARNING: Never use high-cardinality values (user IDs, request IDs, timestamps) # Examples: # - Single label: environment=production # - Multiple labels: environment=production,region=us-east-1,team=platform # - K8s example: cluster=prod-us-east,namespace=mcp-gateway # Default: "" (no custom labels) # METRICS_CUSTOM_LABELS= # ----------------------------------------------------------------------------- # Plugin Framework Settings # ----------------------------------------------------------------------------- # The plugin framework has its own configuration via pydantic-settings with the # PLUGINS_ env var prefix. These settings allow the plugin framework to operate # independently of the gateway configuration (mcpgateway.config). # # When the plugin framework is used standalone (e.g., via the mcpplugins CLI or # as a library), only these PLUGINS_-prefixed variables are needed. When running # inside the gateway, both the gateway settings (above) AND these framework # settings are in effect. # # The plugin framework settings share some env var names with the gateway # (e.g. PLUGINS_ENABLED, PLUGINS_CLI_MARKUP_MODE). Other settings mirror # gateway-level HTTPX_*/SKIP_SSL_VERIFY but are scoped to plugin requests: # HTTPX_CONNECT_TIMEOUT → PLUGINS_HTTPX_CONNECT_TIMEOUT # HTTPX_READ_TIMEOUT → PLUGINS_HTTPX_READ_TIMEOUT # SKIP_SSL_VERIFY → PLUGINS_SKIP_SSL_VERIFY # Plugin Framework Configuration # Enable the plugin system for extending gateway functionality # Options: true, false (default) # When true: Loads and executes plugins from PLUGINS_CONFIG_FILE # PLUGINS_ENABLED=false # Allow plugin HTTP_AUTH_CHECK_PERMISSION grants to override built-in RBAC decisions. # Disabled by default: plugin grants are audit-only unless this is explicitly enabled. # PLUGINS_CAN_OVERRIDE_RBAC=false # DANGEROUS: Allow pre-request plugin hooks to override auth-sensitive headers # (authorization, cookie, x-api-key, proxy-authorization) the client already sent. # Only enable when all loaded plugins are fully trusted (e.g. WXO auth token exchange). # Requires server restart to take effect. # PLUGINS_CAN_OVERRIDE_AUTH_HEADERS=false # Path to the plugin configuration file # Contains plugin definitions, hooks, and settings # Default: plugins/config.yaml # PLUGINS_CONFIG_FILE=plugins/config.yaml # Plugin execution timeout in seconds # PLUGINS_PLUGIN_TIMEOUT=30 # Plugin framework log level # PLUGINS_LOG_LEVEL=INFO # Skip SSL/TLS certificate verification for plugin HTTP requests # WARNING: Only use in development or with self-signed certificates # PLUGINS_SKIP_SSL_VERIFY=false # HTTP client pool settings for plugin framework # These mirror the gateway HTTPX_* settings but are scoped to plugin requests # PLUGINS_HTTPX_MAX_CONNECTIONS=200 # PLUGINS_HTTPX_MAX_KEEPALIVE_CONNECTIONS=100 # PLUGINS_HTTPX_KEEPALIVE_EXPIRY=30.0 # PLUGINS_HTTPX_CONNECT_TIMEOUT=5.0 # PLUGINS_HTTPX_READ_TIMEOUT=120.0 # PLUGINS_HTTPX_WRITE_TIMEOUT=30.0 # PLUGINS_HTTPX_POOL_TIMEOUT=10.0 # Optional defaults for mTLS when connecting to external MCP plugins (STREAMABLEHTTP transport) # Provide file paths inside the container. Plugin-specific TLS blocks override these defaults. # PLUGINS_CLIENT_MTLS_CA_BUNDLE=/app/certs/plugins/ca.crt # PLUGINS_CLIENT_MTLS_CERTFILE=/app/certs/plugins/gateway-client.pem # PLUGINS_CLIENT_MTLS_KEYFILE=/app/certs/plugins/gateway-client.key # PLUGINS_CLIENT_MTLS_KEYFILE_PASSWORD= # PLUGINS_CLIENT_MTLS_VERIFY=true # PLUGINS_CLIENT_MTLS_CHECK_HOSTNAME=true # Optional defaults for plugin server TLS when exposing plugins over HTTP # PLUGINS_SERVER_SSL_ENABLED=false # PLUGINS_SERVER_SSL_KEYFILE=/app/certs/plugins/server.key # PLUGINS_SERVER_SSL_CERTFILE=/app/certs/plugins/server.pem # PLUGINS_SERVER_SSL_CA_CERTS=/app/certs/plugins/ca.crt # PLUGINS_SERVER_SSL_CERT_REQS=2 # PLUGINS_SERVER_SSL_KEYFILE_PASSWORD= # Plugin MCP server bind settings # PLUGINS_SERVER_HOST=0.0.0.0 # PLUGINS_SERVER_PORT=9000 # PLUGINS_SERVER_UDS=/tmp/mcpgateway-plugins.sock # Plugin server runtime (external MCP server) # PLUGINS_TRANSPORT=stdio # PLUGINS_CONFIG_PATH=./resources/plugins/config.yaml # Optional defaults for mTLS when connecting to external plugins over gRPC # PLUGINS_GRPC_CLIENT_MTLS_CA_BUNDLE=/app/certs/plugins/grpc-ca.crt # PLUGINS_GRPC_CLIENT_MTLS_CERTFILE=/app/certs/plugins/grpc-client.pem # PLUGINS_GRPC_CLIENT_MTLS_KEYFILE=/app/certs/plugins/grpc-client.key # PLUGINS_GRPC_CLIENT_MTLS_KEYFILE_PASSWORD= # PLUGINS_GRPC_CLIENT_MTLS_VERIFY=true # Optional defaults for plugin gRPC server TLS # PLUGINS_GRPC_SERVER_SSL_ENABLED=false # PLUGINS_GRPC_SERVER_SSL_KEYFILE=/app/certs/plugins/grpc-server.key # PLUGINS_GRPC_SERVER_SSL_CERTFILE=/app/certs/plugins/grpc-server.pem # PLUGINS_GRPC_SERVER_SSL_CA_CERTS=/app/certs/plugins/grpc-ca.crt # PLUGINS_GRPC_SERVER_SSL_CLIENT_AUTH=none # PLUGINS_GRPC_SERVER_SSL_KEYFILE_PASSWORD= # Plugin gRPC server bind settings # PLUGINS_GRPC_SERVER_HOST=0.0.0.0 # PLUGINS_GRPC_SERVER_PORT=50051 # PLUGINS_GRPC_SERVER_UDS=/tmp/mcpgateway-plugins-grpc.sock # Unix domain socket transport for plugin communication # PLUGINS_UNIX_SOCKET_PATH=/tmp/mcpgateway-plugins-unix.sock # Enable auto-completion for plugins CLI # PLUGINS_CLI_COMPLETION=false # Set markup mode for plugins CLI # Valid options: # rich: use rich markup # markdown: allow markdown in help strings # disabled: disable markup # If unset (commented out), uses "rich" if rich is detected, otherwise disables it. # Project defaults block sets PLUGINS_CLI_MARKUP_MODE=rich PLUGINS_CLI_MARKUP_MODE=rich # ============================================================================= # Well-Known URI Configuration # ============================================================================= # Enable well-known URI endpoints (/.well-known/*) # WELL_KNOWN_ENABLED=true # robots.txt content - Default blocks all crawlers (private API) # Use multiline with proper escaping or keep on one line # WELL_KNOWN_ROBOTS_TXT="User-agent: *\nDisallow: /\n\n# ContextForge is a private API gateway\n# Public crawling is disabled by default" # security.txt content - Define your security contact information # Format: RFC 9116 (https://www.rfc-editor.org/rfc/rfc9116.html) # Leave empty to disable security.txt # Example: # WELL_KNOWN_SECURITY_TXT="Contact: mailto:security@example.com\nExpires: 2025-12-31T23:59:59Z\nPreferred-Languages: en\nCanonical: https://example.com/.well-known/security.txt" # WELL_KNOWN_SECURITY_TXT="" # Enable security.txt endpoint (auto-enabled when content is provided) # WELL_KNOWN_SECURITY_TXT_ENABLED=false # Additional custom well-known files (JSON format) # Example: {"ai.txt": "AI Usage: This service uses AI for tool orchestration...", "dnt-policy.txt": "We respect DNT headers..."} # WELL_KNOWN_CUSTOM_FILES="{}" # Cache control for well-known files (seconds) - 3600 = 1 hour # WELL_KNOWN_CACHE_MAX_AGE=3600 # ============================================================================= # Well-Known URI Examples # ============================================================================= # Example 1: Basic security.txt # WELL_KNOWN_SECURITY_TXT="Contact: mailto:security@mycompany.com\nContact: https://mycompany.com/security\nEncryption: https://mycompany.com/pgp-key.txt\nPreferred-Languages: en, es\nCanonical: https://api.mycompany.com/.well-known/security.txt" # Example 2: Custom AI policy # WELL_KNOWN_CUSTOM_FILES={"ai.txt": "# AI Usage Policy\n\nThis ContextForge 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"} # Example 3: Allow specific crawlers # WELL_KNOWN_ROBOTS_TXT="User-agent: internal-monitor\nAllow: /health\nAllow: /metrics\n\nUser-agent: *\nDisallow: /" # Example 4: Multiple custom files # WELL_KNOWN_CUSTOM_FILES={"ai.txt": "# AI Usage Policy\n\nThis ContextForge 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"} # ============================================================================= # Startup Tuning # ============================================================================= # Batch size for gateway/tool slug refresh at startup # SLUG_REFRESH_BATCH_SIZE=1000 # ============================================================================= # 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 # # Default dangerous HTML pattern (regex) # VALIDATION_DANGEROUS_HTML_PATTERN="<(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)\\b|" # Default dangerous JS pattern (regex) # VALIDATION_DANGEROUS_JS_PATTERN="(?i)(?:^|\\s|[\\\"'`<>=])(javascript:|vbscript:|data:\\s*[^,]*[;\\s]*(javascript|vbscript)|\\bon[a-z]+\\s*=|<\\s*script\\b)" # 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 # # Name pattern (allows spaces) # VALIDATION_NAME_PATTERN="^[a-zA-Z0-9_.\\-\\s]+$" # Identifier pattern (no spaces) # VALIDATION_IDENTIFIER_PATTERN="^[a-zA-Z0-9_\\-\\.]+$" # Safe URI pattern # VALIDATION_SAFE_URI_PATTERN="^[a-zA-Z0-9_\\-.:/?=&%{}]+$" # Unsafe URI pattern # VALIDATION_UNSAFE_URI_PATTERN="[<>\"'\\]" # MCP tool naming pattern per SEP-986 # VALIDATION_TOOL_NAME_PATTERN="^[a-zA-Z0-9_][a-zA-Z0-9._/-]*$" # MCP tool method naming pattern # VALIDATION_TOOL_METHOD_PATTERN="^[a-zA-Z][a-zA-Z0-9_\\./-]*$" # 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"] # ============================================================================= # Non-Settings Environment Variables # ============================================================================= # --- Runtime / launcher envs -------------------------------------------------- # Disable access logging for performance # Options: true, false (default) # When true: Disables both gunicorn and uvicorn access logs # Access logs create massive I/O overhead under high concurrency # Default: true (disabled for performance) # Set to false to enable access logging for debugging # DISABLE_ACCESS_LOG=true # Force start even if another instance is running # Options: true, false (default) # Bypasses the lock file check at /tmp/mcpgateway-gunicorn.lock # FORCE_START=false # --- Gunicorn Production Server Configuration -------------------------------- # These settings are used by run-gunicorn.sh for production deployments. # They control the Gunicorn WSGI server behavior. # Number of worker processes # Options: "auto" (default, 2*CPU+1 capped at 16), or any positive integer # Recommendation: 2-4 x $(NUM_CORES) for CPU-bound, more for I/O-bound workloads # GUNICORN_WORKERS=auto # Worker timeout in seconds # Workers that don't respond within this time are killed and restarted # Increase for long-running requests (e.g., LLM streaming, large file uploads) # Default: 600 (10 minutes) # GUNICORN_TIMEOUT=600 # Maximum requests per worker before automatic restart # Helps prevent memory leaks by periodically recycling workers # Default: 100000 # GUNICORN_MAX_REQUESTS=100000 # Random jitter added to max requests (prevents thundering herd on restart) # Default: 100 # GUNICORN_MAX_REQUESTS_JITTER=100 # Preload application before forking workers # Options: true (default), false # true: Saves memory (shared code), runs migrations once before forking # false: Each worker loads app independently (more memory, better isolation) # GUNICORN_PRELOAD_APP=true # Developer mode with hot reload # Options: true, false (default) # Enables --reload flag and reduces workers for easier debugging # WARNING: Disables preload_app. Not for production! # GUNICORN_DEV_MODE=false # --- SSL/TLS Configuration (launcher) ---------------------------------------- # Enable HTTPS for production deployments (run-gunicorn.sh) # Enable TLS/SSL # Options: true, false (default) # SSL=false # Path to SSL certificate file (PEM format) # CERT_FILE=certs/cert.pem # Path to SSL private key file (PEM format) # KEY_FILE=certs/key.pem # Passphrase for encrypted private key (optional) # If your key is passphrase-protected, set this value # The key will be decrypted by the SSL key manager before Gunicorn starts # KEY_FILE_PASSWORD= # CERT_PASSPHRASE= # --- Direct env reads (application code) ------------------------------------- # Content type for outgoing HTTP requests to upstream services # Options: application/json (default), application/x-www-form-urlencoded, multipart/form-data # Direct env read (mcpgateway/config.py) # FORGE_CONTENT_TYPE=application/json # SQLAlchemy echo commands - debug only, used to identify N+1 issues, etc. # Direct env read (mcpgateway/db.py) # SQLALCHEMY_ECHO=0 # Copy resource attributes to span attributes (for Arize compatibility) # Some observability backends like Arize require certain attributes as span attributes # rather than resource attributes. Enable this to copy arize.project.name and model_id. # Read via mcpgateway/config.py # OTEL_COPY_RESOURCE_ATTRS_TO_SPANS=false # Deployment environment label for observability resource attributes # Read via mcpgateway/config.py # DEPLOYMENT_ENV=development # Jaeger exporter auth (only used when OTEL_TRACES_EXPORTER=jaeger) # Read via mcpgateway/config.py # OTEL_EXPORTER_JAEGER_USER= # OTEL_EXPORTER_JAEGER_PASSWORD= # Test mode for observability (disables tracing when set to 1) # Direct env read (mcpgateway/observability.py) # MCP_TESTING=0 # ============================================================================= # Langfuse LLM Observability Integration # ============================================================================= # Langfuse provides trace visualization, prompt management, evaluations, # cost tracking, and LLM analytics. Integrates via OTLP/HTTP. # # Quick start: make langfuse-up # Access: http://localhost:3100 # Combined: make langfuse-monitoring-up (Langfuse + Grafana/Tempo; gateway traces still go to Langfuse by default) # # Usage: docker compose -f docker-compose.yml -f docker-compose.with-langfuse.yml up -d # Langfuse OTLP endpoint override for the gateway. # Defaults to the local compose service when unset in the overlay. # LANGFUSE_OTEL_ENDPOINT=http://localhost:3100/api/public/otel/v1/traces # Langfuse API keys used by ContextForge to connect to Langfuse via OTLP. # For the local self-hosted compose overlay, unset values fall back to the # compose-local dev defaults `pk-lf-contextforge` / `sk-lf-contextforge`. # Set these when you want a different local project or when connecting to an # external Langfuse instance. # LANGFUSE_PUBLIC_KEY=pk-lf- # LANGFUSE_SECRET_KEY=sk-lf- # Optional OTEL auth override: base64("publicKey:secretKey") # When LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY are set, the gateway can # derive the Authorization header automatically and this override is not needed. # LANGFUSE_OTEL_AUTH= # Langfuse UI host port (default: 3100 to avoid Grafana conflict on 3000) # LANGFUSE_PORT=3100 # Langfuse worker metrics port (localhost only) # LANGFUSE_WORKER_PORT=3130 # Langfuse UI URL (used for NEXTAUTH_URL and CORS) # LANGFUSE_URL=http://localhost:3100 # Auto-provisioned admin user override for the local self-hosted Langfuse overlay # LANGFUSE_INIT_USER_EMAIL=admin@example.com # LANGFUSE_INIT_USER_PASSWORD= # Auto-provisioned organization and project # LANGFUSE_INIT_ORG_ID=contextforge # LANGFUSE_INIT_ORG_NAME=ContextForge # LANGFUSE_INIT_PROJECT_ID=contextforge-gateway # LANGFUSE_INIT_PROJECT_NAME=ContextForge Gateway # Optional overrides for the local self-hosted Langfuse overlay only. # ContextForge does not read these. They are used only by docker-compose.with-langfuse.yml. # If unset, the overlay uses local-only defaults defined in that compose file. # LANGFUSE_POSTGRES_PASSWORD= # LANGFUSE_CLICKHOUSE_USER=clickhouse # LANGFUSE_CLICKHOUSE_PASSWORD= # LANGFUSE_MINIO_USER=minio # LANGFUSE_MINIO_PASSWORD= # LANGFUSE_REDIS_AUTH= # LANGFUSE_NEXTAUTH_SECRET= # LANGFUSE_SALT= # LANGFUSE_ENCRYPTION_KEY= # Langfuse optional features # LANGFUSE_TELEMETRY_ENABLED=true # LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=false # Monitoring stack host port overrides for `make monitoring-up` and # `make langfuse-monitoring-up`. These are compose-only and are not read by # mcpgateway/config.py. # NGINX_PORT=8080 # GRAFANA_PORT=3000 # LOKI_PORT=3101 # PROMETHEUS_PORT=9090 # TEMPO_PORT=3200 # TEMPO_OTLP_GRPC_PORT=4317 # TEMPO_OTLP_HTTP_PORT=4318 # TEMPO_IMAGE_TAG=2.10.0 # PGADMIN_PORT=5050 # REDIS_COMMANDER_PORT=8081 # POSTGRES_EXPORTER_PORT=9187 # REDIS_EXPORTER_PORT=9121 # PGBOUNCER_EXPORTER_PORT=9127 # NGINX_EXPORTER_PORT=9113 # CADVISOR_PORT=8085 # OTEL trace controls for Langfuse and other OTLP backends # Langfuse-specific attributes auto-enable when LANGFUSE_OTEL_ENDPOINT points to # Langfuse. Set these explicitly only when you want to override that behavior. # OTEL_EMIT_LANGFUSE_ATTRIBUTES= # OTEL_CAPTURE_IDENTITY_ATTRIBUTES= # # Payload capture is allowlist-based. By default the gateway does not capture # observation input or output payloads unless the relevant span names are listed. # The local `docker-compose.with-langfuse.yml` overlay sets a dev-friendly input # allowlist for `tool.invoke,prompt.render,llm.proxy,a2a.invoke`. # OTEL_REDACT_FIELDS=password,secret,token,api_key,authorization,credential,auth_value,access_token,refresh_token,auth_token,client_secret,cookie,set-cookie,private_key # OTEL_MAX_TRACE_PAYLOAD_SIZE=32768 # OTEL_CAPTURE_INPUT_SPANS=tool.invoke,prompt.render # OTEL_CAPTURE_OUTPUT_SPANS= # --- Auxiliary tools and CLIs (non-gateway runtime) -------------------------- # These are used by helper tools, CLIs, and SDK wrappers (not the main gateway server). # Gateway CLI defaults (mcpgateway/cli.py) # MCG_HOST=127.0.0.1 # MCG_PORT=4444 # Export/import CLI auth (mcpgateway/cli_export_import.py) # MCPGATEWAY_BEARER_TOKEN= # MCP wrapper for tool calls (mcpgateway/wrapper.py) # MCP_SERVER_URL= # MCP_AUTH= # MCP_TOOL_CALL_TIMEOUT=60 # MCP_WRAPPER_LOG_LEVEL=INFO # CONCURRENCY=10 # Reverse proxy helper (mcpgateway/reverse_proxy.py) # REVERSE_PROXY_GATEWAY= # REVERSE_PROXY_TOKEN= # REVERSE_PROXY_RECONNECT_DELAY=1 # REVERSE_PROXY_MAX_RETRIES=0 # REVERSE_PROXY_LOG_LEVEL=INFO # DB readiness helper (mcpgateway/utils/check_schema_at_head.py) # DB_WAIT_MAX_TRIES=30 # DB_WAIT_INTERVAL=2 # DB_CONNECT_TIMEOUT=2 # Builder / deploy tooling (mcpgateway/tools/builder/*) # MCP_DEPLOY_DIR=./deploy # MCP_DEBUG= # CONTAINER=false # ToolOps LLM provider envs (mcpgateway/toolops/utils/llm_util.py) # LLM_PROVIDER=openai # NOTE: MAX_TOEKNS is misspelled in code; use the exact env name shown below. # NOTE: *TEMPERATURE envs are currently not read (commented in code) but listed for completeness. # # OpenAI # OPENAI_API_KEY= # OPENAI_BASE_URL=https://api.openai.com # OPENAI_MODEL= # OPENAI_TEMPERATURE=0.7 # OPENAI_MAX_RETRIES=2 # OPENAI_MAX_TOEKNS=600 # # Azure OpenAI # AZURE_OPENAI_API_KEY= # AZURE_OPENAI_ENDPOINT= # AZURE_OPENAI_API_VERSION= # AZURE_OPENAI_DEPLOYMENT= # AZURE_OPENAI_MODEL= # AZURE_OPENAI_TEMPERATURE=0.7 # AZURE_OPENAI_MAX_RETRIES=2 # AZURE_OPENAI_MAX_TOEKNS=600 # # Anthropic # ANTHROPIC_API_KEY= # ANTHROPIC_MODEL= # ANTHROPIC_TEMPERATURE=0.7 # ANTHROPIC_MAX_RETRIES=2 # ANTHROPIC_MAX_TOKENS=4096 # # AWS Bedrock # AWS_BEDROCK_MODEL_ID= # AWS_BEDROCK_REGION= # AWS_BEDROCK_TEMPERATURE=0.7 # AWS_BEDROCK_MAX_TOKENS=4096 # AWS_ACCESS_KEY_ID= # AWS_SECRET_ACCESS_KEY= # AWS_SESSION_TOKEN= # # Ollama # OLLAMA_BASE_URL=http://localhost:11434 # OLLAMA_MODEL= # OLLAMA_TEMPERATURE=0.7 # # Watsonx # WATSONX_APIKEY= # WATSONX_URL= # WATSONX_PROJECT_ID= # WATSONX_MODEL_ID= # WATSONX_MAX_NEW_TOKENS=1000 # WATSONX_DECODING_METHOD=greedy # ============================================================================= # 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 Jinja2 template auto-reload (for development) # Options: true, false (default) # Set to true for development to auto-detect template changes # Performance impact: Disabling reduces CPU usage for admin UI under load # Note: `make dev` automatically sets this to true # TEMPLATES_AUTO_RELOAD=false # Enable debug mode (verbose error messages, stack traces) # Options: true, false (default) # WARNING: May expose sensitive information! # DEBUG=false # Expose detailed error information in API error responses # Options: true, false (default) # WARNING: May leak internal stack traces or config details — never enable in production # EXPOSE_ERROR_DETAILS=false # Header Passthrough — quick ref (full docs → Header Passthrough section ~line 1840) # ENABLE_HEADER_PASSTHROUGH=false # ENABLE_OVERWRITE_BASE_HEADERS=false # DEFAULT_PASSTHROUGH_HEADERS=["X-Tenant-Id", "X-Trace-Id"] # Sensitive Header Passthrough (Phase 1 - Issue #3621) # Allow Authorization, X-API-Key, and other sensitive headers in passthrough_headers whitelist. # REQUIRES: ENABLE_HEADER_PASSTHROUGH=true (will fail startup if base feature is disabled) # Default: false for security. # When enabled, whitelisted sensitive headers bypass router-level filtering. # ENABLE_SENSITIVE_HEADER_PASSTHROUGH=false # Authorization Header Conflict Resolution: # When gateway uses auth, use X-Upstream-Authorization header to pass # authorization to upstream servers (automatically renamed to Authorization) # GlobalConfig In-Memory Cache TTL (Issue #1715) # Caches GlobalConfig (passthrough headers settings) in memory to reduce DB queries. # Under load (1000+ concurrent users), this eliminates 42,000+ redundant DB queries. # Trade-off: Config changes take up to TTL seconds to propagate (use admin API to force refresh). # Default: 60 seconds. Range: 5-3600 seconds. # Project defaults block sets GLOBAL_CONFIG_CACHE_TTL=300 for local dev # GLOBAL_CONFIG_CACHE_TTL=60 # A2A Stats In-Memory Cache TTL # Caches A2A agent counts (total, active) to avoid redundant COUNT queries on /metrics calls. # Trade-off: Agent count changes take up to TTL seconds to propagate (use admin API to force refresh). # Default: 30 seconds. Range: 5-3600 seconds. # A2A_STATS_CACHE_TTL=30 # Project defaults block sets MCPGATEWAY_UI_TOOL_TEST_TIMEOUT=120000 for local dev # MCPGATEWAY_UI_TOOL_TEST_TIMEOUT=60000 # ============================================================================= # Security Validation Settings # ============================================================================= # Minimum length for secret keys (JWT, encryption) # MIN_SECRET_LENGTH=32 # Minimum length for passwords # MIN_PASSWORD_LENGTH=12 # ------------------------------------------------------------------------------ # SECURITY-CRITICAL: Values below MUST be changed for production deployments! # Defaults are for LOCAL DEVELOPMENT ONLY. Using defaults in production # exposes your deployment to full compromise. # # Generate secure keys using: python -m mcpgateway.scripts.init_secrets # Manual fallback: python3 -c 'import secrets; print(secrets.token_urlsafe(32))' # ------------------------------------------------------------------------------ # REQUIRE_STRONG_SECRETS: # Defaults to TRUE if ENVIRONMENT=production. # If set to true, the gateway will FAIL TO START if critical secrets are weak or missing. # Set to false only for local testing. NOT RECOMMENDED for production! # REQUIRE_STRONG_SECRETS=true # ============================================================================= # ToolOps Configuration # ============================================================================= # Enable the ToolOps functionality (true/false) # When disabled, ToolOps features will be completely hidden from UI and APIs # Default: false (must be explicitly enabled) # TOOLOPS_ENABLED=false # ============================================================================= # LLM Chat MCP Client Configuration # ============================================================================= # Enable the LLM Chat functionality (true/false) # When disabled, LLM chat features will be completely hidden from UI and APIs # Default: true # LLMCHAT_ENABLED=true # Enable stdio transport for LLM Chat MCP server config. # Disabled by default; enable only when trusted stdio subprocess execution is required. # MCPGATEWAY_STDIO_TRANSPORT_ENABLED=false # LLM Provider Configuration # All LLM providers are now configured via Admin UI -> Settings -> LLM Settings. # Add providers (OpenAI, Azure OpenAI, Anthropic, AWS Bedrock, Ollama, watsonx) # and their models through the Admin UI. API keys and credentials are securely # stored in the database. # Redis Configuration for chat session storage and maintaining history # CACHE_TYPE should be set to "redis" and REDIS_URL configured appropriately as mentioned in the caching section. # Seconds for active_session key TTL # LLMCHAT_SESSION_TTL=300 # Seconds for lock expiry # LLMCHAT_SESSION_LOCK_TTL=30 # How many times to poll while waiting # LLMCHAT_SESSION_LOCK_RETRIES=10 # Seconds between polls # LLMCHAT_SESSION_LOCK_WAIT=0.2 # Seconds for chat history expiry # LLMCHAT_CHAT_HISTORY_TTL=3600 # Maximum message history to store per user # LLMCHAT_CHAT_HISTORY_MAX_MESSAGES=50 # ============================================================================= # LLM Settings (Internal API) # ============================================================================= # These settings control the internal LLM API that allows the gateway to # act as a unified LLM provider. Configure external providers in the Admin UI. # API prefix for internal LLM endpoints (OpenAI-compatible) # Default: /v1 # LLM_API_PREFIX=/v1 # Request timeout in seconds for LLM API calls # Default: 120 # LLM_REQUEST_TIMEOUT=120 # Enable streaming responses for LLM Chat # Default: true # LLM_STREAMING_ENABLED=true # Provider health check interval in seconds # Default: 300 (5 minutes) # LLM_HEALTH_CHECK_INTERVAL=300 # ============================================================================= # Pagination Configuration # ============================================================================= # Default number of items per page for paginated endpoints # Applies to: tools, resources, prompts, servers, gateways, users, teams, tokens, etc. # Default: 50, Min: 1, Max: 1000 # PAGINATION_DEFAULT_PAGE_SIZE=50 # Maximum allowed items per page (prevents abuse) # Default: 500, Min: 1, Max: 10000 # PAGINATION_MAX_PAGE_SIZE=500 # Minimum items per page # Default: 1 # PAGINATION_MIN_PAGE_SIZE=1 # Threshold for switching from offset to cursor-based pagination # When result set exceeds this count, use cursor-based pagination for performance # Default: 10000 # PAGINATION_CURSOR_THRESHOLD=10000 # Enable cursor-based pagination globally # Options: true (default), false # When false, only offset-based pagination is used # PAGINATION_CURSOR_ENABLED=true # Default sort field for paginated queries # Default: created_at # PAGINATION_DEFAULT_SORT_FIELD=created_at # Default sort order for paginated queries # Options: asc, desc (default) # PAGINATION_DEFAULT_SORT_ORDER=desc # Maximum offset allowed for offset-based pagination (prevents abuse) # Default: 100000 (100K records) # PAGINATION_MAX_OFFSET=100000 # Cache pagination counts for performance (seconds) # Set to 0 to disable caching # Default: 300 (5 minutes) # PAGINATION_COUNT_CACHE_TTL=300 # Enable pagination links in API responses # Options: true (default), false # PAGINATION_INCLUDE_LINKS=true # Base URL for pagination links (defaults to request URL) # PAGINATION_BASE_URL=https://api.example.com # ============================================================================= # gRPC Support Settings (EXPERIMENTAL) # ============================================================================= # Enable gRPC to MCP translation support (disabled by default) # Requires: pip install mcp-contextforge-gateway[grpc] # MCPGATEWAY_GRPC_ENABLED=false # Enable gRPC server reflection by default for service discovery # MCPGATEWAY_GRPC_REFLECTION_ENABLED=true # Maximum gRPC message size in bytes (4MB default) # MCPGATEWAY_GRPC_MAX_MESSAGE_SIZE=4194304 # Default gRPC call timeout in seconds # MCPGATEWAY_GRPC_TIMEOUT=30 # Enable TLS for gRPC connections by default # MCPGATEWAY_GRPC_TLS_ENABLED=false # ============================================================================= # Header Size Validation # ============================================================================= # Enable RFC 6585 header size validation (returns HTTP 431 on oversized headers) # Default: true # HEADER_SIZE_VALIDATION_ENABLED=true # Maximum total size of all request headers combined (default: 16KB) # MAX_HEADER_TOTAL_SIZE_BYTES=16384 # Maximum size of a single header field name+value (default: 8KB) # MAX_HEADER_FIELD_SIZE_BYTES=8192 # Maximum number of header fields per request (default: 100) # MAX_HEADER_COUNT=100 # ============================================================================= # Audit Trail Logging # ============================================================================= # Enable audit trail logging for compliance (CRUD operations on all resources) # Default: false (disabled for performance - causes a DB write on EVERY API request) # When enabled, logs all create, read, update, delete operations to the audit_trails table # WARNING: This can generate millions of rows during load testing! # Only enable for production compliance requirements (SOC2, HIPAA, etc.) # AUDIT_TRAIL_ENABLED=false # ============================================================================= # Permission Audit Logging # ============================================================================= # Enable permission audit logging for RBAC checks (one DB row per permission check) # Default: false (disabled for performance under load) # PERMISSION_AUDIT_ENABLED=false # ============================================================================= # Security Event Logging # ============================================================================= # Enable security event logging (authentication attempts, authorization failures, etc.) # Default: false (disabled for performance - can cause high DB write load) # When enabled, the AuthContextMiddleware will log authentication events to the database # This is INDEPENDENT of observability settings # SECURITY_LOGGING_ENABLED=false # Security logging level - controls what events are logged to the database # Options: # - "all" : Log ALL events including successful auth (WARNING: high DB load!) # - "failures_only" : Log only authentication/authorization failures (recommended) # - "high_severity" : Log only high/critical severity events # Default: failures_only # SECURITY_LOGGING_LEVEL=failures_only # Failed auth attempts before high severity alert # SECURITY_FAILED_AUTH_THRESHOLD=5 # Threat score threshold for alerts (0.0-1.0) # SECURITY_THREAT_SCORE_ALERT=0.7 # Time window for rate limit checks (minutes) # SECURITY_RATE_LIMIT_WINDOW_MINUTES=5 # ============================================================================= # Observability Settings # ============================================================================= # Enable observability tracing and metrics collection # When enabled, all HTTP requests will be traced with detailed timing, status codes, and context # OBSERVABILITY_ENABLED=false # Automatically trace HTTP requests # OBSERVABILITY_TRACE_HTTP_REQUESTS=true # Number of days to retain trace data # OBSERVABILITY_TRACE_RETENTION_DAYS=7 # Maximum number of traces to retain (prevents unbounded growth) # OBSERVABILITY_MAX_TRACES=100000 # Trace sampling rate (0.0-1.0) - 1.0 means trace everything, 0.1 means trace 10% # OBSERVABILITY_SAMPLE_RATE=1.0 # Paths to include for tracing (JSON array of regex patterns) # OBSERVABILITY_INCLUDE_PATHS=["^/rpc/?$","^/sse$","^/message$","^/mcp(?:/|$)","^/servers/[^/]+/mcp/?$","^/servers/[^/]+/sse$","^/servers/[^/]+/message$","^/a2a(?:/|$)"] # Paths to exclude from tracing (JSON array of regex patterns, applied after include patterns) # OBSERVABILITY_EXCLUDE_PATHS=["/health", "/healthz", "/ready", "/metrics", "/static/.*"] # Enable metrics collection # OBSERVABILITY_METRICS_ENABLED=true # Enable event logging within spans # OBSERVABILITY_EVENTS_ENABLED=true # ----------------------------------------------------------------------------- # Plugin metadata observability sinks # (G1: PluginResult.metadata → observability_spans) # ----------------------------------------------------------------------------- # Records a plugin.metrics. DB span for each plugin that returns metadata # on an invoke_hook() call. Both sinks are on by default; disable to reduce DB # write amplification if you have many active plugins per request. # Write a plugin.metrics. internal span per plugin metadata entry. # Default: true # PLUGIN_METRICS_DB_SPANS_ENABLED=true # Also record numeric plugin metadata fields (e.g. total_detections) as # ObservabilityMetric rows, making them queryable as metrics not just span attrs. # Default: true # PLUGIN_METRICS_DB_NUMERIC_ROWS_ENABLED=true # Max ObservabilityMetric rows written per invoke_hook() call across all plugins. # Caps DB write amplification when many plugins each report several numeric fields. # Default: 16 # PLUGIN_METRICS_MAX_NUMERIC_PER_CALL=16 # ============================================================================= # CPEX Control-Execution Telemetry # ============================================================================= # # Structured observability for CPEX plugin enforcement decisions on every tool # invocation. Requires CPEX >= 0.1.2. Silent no-op on older CPEX builds. # Also requires OBSERVABILITY_ENABLED=true to have an active trace to attach to. # # How it works: # - One cpex.control.summary span per tool call (aggregate counts + outcome) # - One cpex.control.result span per plugin evaluated (name, status, decision, # duration, reason, error_code, config key names) # - Both written to the internal observability_spans DB table and, when OTel # tracing is active, also exported via the OTel SDK sink. # # Master switch — enable to emit cpex.control.* spans. # DISABLED BY DEFAULT: each traced tool call creates up to 1 summary + # CPEX_CONTROL_TELEMETRY_MAX_RESULTS result DB spans. Review storage and # cardinality implications before enabling in production. # Default: false # CPEX_CONTROL_TELEMETRY_ENABLED=false # Independently toggle the DB sink (cpex.control.summary + cpex.control.result rows). # Default: true # CPEX_CONTROL_TELEMETRY_DB_ENABLED=true # Emit flattened cpex.control.results..* attributes on the summary span. # Off by default. Enable only when downstream tooling requires dynamic key names. # The fixed-schema cpex.control.result child spans remain the source of truth. # Default: false # CPEX_CONTROL_TELEMETRY_FLATTEN_RESULTS=false # Max per-control result records exported per tool invocation (0–128). # Controls how many cpex.control.result child spans are written per call. # Default: 32 # CPEX_CONTROL_TELEMETRY_MAX_RESULTS=32 # Informational attribute-count hint for external OTel-collector configuration # (e.g. transform/attributes processor limits). Not enforced gateway-side; # gateway-side enforcement is planned for Phase 5 attribute-policy wiring. # Default: 256 # CPEX_CONTROL_TELEMETRY_MAX_ATTRIBUTES=256 # Emit cpex.control.result.reason and cpex.control.result.error_code on per-control spans. # DISABLED BY DEFAULT: these fields may contain PII, tool argument values, or exception # content. Enable only when the observability sink is secured and a redaction boundary is # in place. # Default: false # CPEX_CONTROL_TELEMETRY_EMIT_REASON=false # Emit cpex.control.agent.id (authenticated caller email) on the summary span. # DISABLED BY DEFAULT: high-cardinality PII field with GDPR/data-residency implications. # Enable only when the observability sink is secured and a redaction boundary is in place. # Default: false # CPEX_CONTROL_TELEMETRY_EMIT_AGENT_ID=false # ============================================================================= # Performance Tracking Thresholds # ============================================================================= # Enable performance tracking and metrics (internal) # PERFORMANCE_TRACKING_ENABLED=true # Alert thresholds (milliseconds) # PERFORMANCE_THRESHOLD_DATABASE_QUERY_MS=100.0 # PERFORMANCE_THRESHOLD_TOOL_INVOCATION_MS=2000.0 # PERFORMANCE_THRESHOLD_RESOURCE_READ_MS=1000.0 # PERFORMANCE_THRESHOLD_HTTP_REQUEST_MS=500.0 # Alert if performance degrades by this multiplier vs baseline # PERFORMANCE_DEGRADATION_MULTIPLIER=1.5 # ============================================================================= # Performance Monitoring Settings # ============================================================================= # Enable performance tracking tab in admin UI (default: false) # Shows real-time CPU, memory, disk, network, worker, and request metrics # MCPGATEWAY_PERFORMANCE_TRACKING=false # Metric collection interval in seconds (default: 10) # How often to sample system metrics for historical data # MCPGATEWAY_PERFORMANCE_COLLECTION_INTERVAL=10 # Snapshot retention period in hours (default: 24) # How long to keep individual metric snapshots # MCPGATEWAY_PERFORMANCE_RETENTION_HOURS=24 # Aggregate retention period in days (default: 90) # How long to keep hourly/daily aggregated metrics # MCPGATEWAY_PERFORMANCE_RETENTION_DAYS=90 # Maximum performance snapshots to retain (default: 10000) # Prevents unbounded database growth # MCPGATEWAY_PERFORMANCE_MAX_SNAPSHOTS=10000 # Enable distributed mode for multi-container deployments (default: false) # Uses Redis to aggregate metrics from multiple workers/containers # MCPGATEWAY_PERFORMANCE_DISTRIBUTED=false # Enable network connections counting (default: true) # psutil.net_connections() can be CPU intensive under heavy load # Disable to skip network connection counting entirely # MCPGATEWAY_PERFORMANCE_NET_CONNECTIONS_ENABLED=true # Cache TTL for net_connections in seconds (default: 15) # Throttles expensive psutil.net_connections() calls # Higher values reduce CPU usage but report stale connection counts # MCPGATEWAY_PERFORMANCE_NET_CONNECTIONS_CACHE_TTL=15 # ============================================================================= # Ed25519 Key Support # ============================================================================= # Enable Ed25519 signing for certificates # ENABLE_ED25519_SIGNING=false # Previous Ed25519 private key for signing rotation # PREV_ED25519_PRIVATE_KEY="" # Previous Ed25519 public key (derived automatically if private key is set) # PREV_ED25519_PUBLIC_KEY= # Current Ed25519 private key for signing # ED25519_PRIVATE_KEY= # Current Ed25519 public key (derived automatically if private key is set) # ED25519_PUBLIC_KEY= # SSL context cache settings # SSL_CONTEXT_CACHE_MAX_SIZE=100 # SSL_CONTEXT_CACHE_TTL= # ============================================================================= # Bootstrap additional system roles # ============================================================================= # Enable Bootstrap additional system roles feature # Allows defining a set of roles to be added while bootstraping db # Options: false (default), true # MCPGATEWAY_BOOTSTRAP_ROLES_IN_DB_ENABLED=false # Path to the additional roles configuration file # JSON file contains an array of JSON objects as shown below # Example: # [{ # "name": "example_role_1", # "description": "Read-only access to resources", # "scope": "team", # "permissions": ["teams.join", "tools.read", "resources.read"], # "is_system_role": true # }, # { # "name": "example_role_2", # "description": "Read-only access to prompts", # "scope": "team", # "permissions": ["teams.join", "tools.read", "resources.read", "prompts.read"], # "is_system_role": true # }] # MCPGATEWAY_BOOTSTRAP_ROLES_IN_DB_FILE=additional_roles_in_db.json LEGACY_API_ENABLED=true LEGACY_API_SUNSET_DATE=Sat, 26 Sep 2026 00:00:00 GMT # ============================================================================= # ContextForge Web UI (docker-compose --profile testing or --profile experimental) # ============================================================================= # BFF-style frontend for the gateway API - see docker-compose.yml (web_ui, web_ui_redis) # Image to pull for the web_ui service # WEB_UI_IMAGE=ghcr.io/contextforge-org/contextforge-web-ui:latest # Host port the UI listens on (also used as the container port - see docker-compose.yml) # WEB_UI_PORT=3001 # Bind address inside the container - must be 0.0.0.0 in Docker or other containers cannot connect # WEB_UI_HOST=0.0.0.0 # Gateway API base URL the UI talks to (internal compose network address:port) # WEB_UI_CONTEXTFORGE_URL=http://gateway:4444 # Set true once the UI is served over HTTPS (marks the session cookie Secure) # WEB_UI_COOKIE_SECURE=false # Session store for the UI's BFF - dedicated Redis, separate from the gateway's cache redis # WEB_UI_REDIS_URL=redis://web_ui_redis:6379/0