Skip to main content
Glama
config.py3.65 kB
"""Configuration management for Listmonk MCP server using pydantic-settings.""" from pathlib import Path from pydantic import Field, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Config(BaseSettings): """Main configuration class with automatic environment variable loading.""" # Listmonk configuration url: str = Field(..., description="Listmonk server URL") username: str = Field(..., description="API username") password: str = Field(..., description="API token") timeout: int = Field(default=30, description="Request timeout in seconds") max_retries: int = Field(default=3, description="Maximum retry attempts") # Server configuration debug: bool = Field(default=False, description="Enable debug mode") log_level: str = Field(default="INFO", description="Logging level") server_name: str = Field(default="Listmonk MCP Server", description="Server name") model_config = SettingsConfigDict( env_prefix='LISTMONK_MCP_', env_file='.env', env_file_encoding='utf-8', case_sensitive=False, extra='ignore' ) @field_validator('url') @classmethod def validate_url(cls, v: str) -> str: """Validate URL format.""" if not v.startswith(('http://', 'https://')): raise ValueError("URL must start with http:// or https://") return v.rstrip('/') @field_validator('timeout') @classmethod def validate_timeout(cls, v: int) -> int: """Validate timeout is positive.""" if v <= 0: raise ValueError("Timeout must be positive") return v @field_validator('max_retries') @classmethod def validate_max_retries(cls, v: int) -> int: """Validate max_retries is non-negative.""" if v < 0: raise ValueError("Max retries must be non-negative") return v @field_validator('log_level') @classmethod def validate_log_level(cls, v: str) -> str: """Validate log level.""" valid_levels = {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'} v_upper = v.upper() if v_upper not in valid_levels: raise ValueError(f"Log level must be one of: {valid_levels}") return v_upper # Global configuration instance _config: Config | None = None def load_config(env_file: str | None = None) -> Config: """Load configuration from environment variables and optional .env file.""" global _config # Pydantic-settings automatically loads from .env file # If a custom env_file is provided, we need to update the model config if env_file and Path(env_file).exists(): # Create a temporary config class with custom env_file class TempConfig(Config): model_config = Config.model_config.copy() model_config['env_file'] = env_file _config = TempConfig() # type: ignore[call-arg] else: _config = Config() # type: ignore[call-arg] return _config def get_config() -> Config: """Get the global configuration instance.""" global _config if _config is None: _config = load_config() return _config def validate_config() -> None: """Validate that all required configuration is present.""" config = get_config() if not config.url: raise ValueError("Listmonk URL is required (set LISTMONK_MCP_URL)") if not config.username: raise ValueError("Listmonk API username is required (set LISTMONK_MCP_USERNAME)") if not config.password: raise ValueError("Listmonk API token is required (set LISTMONK_MCP_PASSWORD)")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rhnvrm/listmonk-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server