Skip to main content
Glama
config.py8 kB
"""Configuration handling for the 4get MCP server.""" from __future__ import annotations from os import environ from dataclasses import dataclass from typing import TypeVar from urllib.parse import urlparse from src import __version__ DEFAULT_BASE_URL = 'https://4get.ca' DEFAULT_TIMEOUT_SECONDS = 20.0 DEFAULT_CACHE_TTL_SECONDS = 600.0 DEFAULT_CACHE_MAXSIZE = 128 DEFAULT_MAX_RETRIES = 3 DEFAULT_RETRY_BASE_DELAY = 1.0 DEFAULT_RETRY_MAX_DELAY = 60.0 DEFAULT_CONNECTION_POOL_MAXSIZE = 10 DEFAULT_CONNECTION_POOL_MAX_KEEPALIVE = 5 @dataclass(slots=True) class Config: """Configuration settings for the 4get MCP server. This class encapsulates all configuration options with sensible defaults. Values can be customized via environment variables or direct instantiation. Environment Variables: FOURGET_BASE_URL: Base URL of the 4get instance (default: https://4get.ca) FOURGET_PASS: Optional pass token for rate-limited instances FOURGET_USER_AGENT: Custom User-Agent header FOURGET_TIMEOUT: Request timeout in seconds (default: 20.0) FOURGET_CACHE_TTL: Cache lifetime in seconds (default: 600.0) FOURGET_CACHE_MAXSIZE: Maximum cached responses (default: 128) FOURGET_MAX_RETRIES: Maximum retry attempts (default: 3) FOURGET_RETRY_BASE_DELAY: Base retry delay in seconds (default: 1.0) FOURGET_RETRY_MAX_DELAY: Maximum retry delay in seconds (default: 60.0) FOURGET_CONNECTION_POOL_MAXSIZE: Max concurrent connections (default: 10) FOURGET_CONNECTION_POOL_MAX_KEEPALIVE: Max persistent connections (default: 5) Example: >>> # Use environment variables >>> config = Config.from_env() >>> >>> # Or configure directly >>> config = Config( >>> base_url="https://my-4get-instance.com", >>> pass_token="my-secret-token", >>> timeout=30.0, >>> max_retries=5 >>> ) """ base_url: str = DEFAULT_BASE_URL pass_token: str | None = None user_agent: str = f'mcp-4get/{__version__}' timeout: float = DEFAULT_TIMEOUT_SECONDS cache_ttl: float = DEFAULT_CACHE_TTL_SECONDS cache_maxsize: int = DEFAULT_CACHE_MAXSIZE max_retries: int = DEFAULT_MAX_RETRIES retry_base_delay: float = DEFAULT_RETRY_BASE_DELAY retry_max_delay: float = DEFAULT_RETRY_MAX_DELAY connection_pool_maxsize: int = DEFAULT_CONNECTION_POOL_MAXSIZE connection_pool_max_keepalive: int = DEFAULT_CONNECTION_POOL_MAX_KEEPALIVE @classmethod def from_env(cls) -> Config: """Create a configuration instance from environment variables. Reads configuration values from environment variables with fallback to sensible defaults. All numeric values are validated for correctness. Returns: Validated Config instance with values from environment or defaults. Raises: ValueError: If any configuration value is invalid. Example: >>> from os import environ >>> environ['FOURGET_TIMEOUT'] = '30.0' >>> environ['FOURGET_MAX_RETRIES'] = '5' >>> config = Config.from_env() >>> print(f"Timeout: {config.timeout}s, Retries: {config.max_retries}") """ base_url = environ.get('FOURGET_BASE_URL', DEFAULT_BASE_URL).rstrip('/') pass_token = environ.get('FOURGET_PASS') user_agent = environ.get('FOURGET_USER_AGENT') or f'mcp-4get/{__version__}' timeout = _read_number('FOURGET_TIMEOUT', float, DEFAULT_TIMEOUT_SECONDS) cache_ttl = _read_number('FOURGET_CACHE_TTL', float, DEFAULT_CACHE_TTL_SECONDS) cache_maxsize = int( _read_number('FOURGET_CACHE_MAXSIZE', int, DEFAULT_CACHE_MAXSIZE, minimum=1) ) max_retries = int(_read_number('FOURGET_MAX_RETRIES', int, DEFAULT_MAX_RETRIES, minimum=0)) retry_base_delay = _read_number( 'FOURGET_RETRY_BASE_DELAY', float, DEFAULT_RETRY_BASE_DELAY, minimum=0.1 ) retry_max_delay = _read_number( 'FOURGET_RETRY_MAX_DELAY', float, DEFAULT_RETRY_MAX_DELAY, minimum=1.0 ) connection_pool_maxsize = int( _read_number( 'FOURGET_CONNECTION_POOL_MAXSIZE', int, DEFAULT_CONNECTION_POOL_MAXSIZE, minimum=1 ) ) connection_pool_max_keepalive = int( _read_number( 'FOURGET_CONNECTION_POOL_MAX_KEEPALIVE', int, DEFAULT_CONNECTION_POOL_MAX_KEEPALIVE, minimum=1, ) ) return cls( base_url=base_url, pass_token=pass_token, user_agent=user_agent, timeout=timeout, cache_ttl=cache_ttl, cache_maxsize=cache_maxsize, max_retries=max_retries, retry_base_delay=retry_base_delay, retry_max_delay=retry_max_delay, connection_pool_maxsize=connection_pool_maxsize, connection_pool_max_keepalive=connection_pool_max_keepalive, )._validate() def _validate(self) -> Config: """Validate all configuration values for correctness. Performs comprehensive validation including: - URL format validation - Numeric range validation - Logical consistency checks Returns: Self for method chaining. Raises: ValueError: If any configuration value is invalid with descriptive message. """ # Validate base_url is a valid URL parsed = urlparse(self.base_url) if not parsed.scheme or not parsed.netloc: raise ValueError(f'Invalid base_url: {self.base_url}') if parsed.scheme not in ('http', 'https'): raise ValueError(f'base_url must use http or https scheme: {self.base_url}') # Validate numeric ranges if self.timeout <= 0: raise ValueError(f'timeout must be positive: {self.timeout}') if self.cache_ttl < 0: raise ValueError(f'cache_ttl must be non-negative: {self.cache_ttl}') if self.cache_maxsize < 1: raise ValueError(f'cache_maxsize must be at least 1: {self.cache_maxsize}') if self.max_retries < 0: raise ValueError(f'max_retries must be non-negative: {self.max_retries}') if self.retry_base_delay <= 0: raise ValueError(f'retry_base_delay must be positive: {self.retry_base_delay}') if self.retry_max_delay <= 0: raise ValueError(f'retry_max_delay must be positive: {self.retry_max_delay}') if self.retry_base_delay > self.retry_max_delay: raise ValueError( f'retry_base_delay ({self.retry_base_delay}) must not exceed ' f'retry_max_delay ({self.retry_max_delay})' ) if self.connection_pool_maxsize < 1: raise ValueError( f'connection_pool_maxsize must be at least 1: {self.connection_pool_maxsize}' ) if self.connection_pool_max_keepalive < 1: raise ValueError( f'connection_pool_max_keepalive must be at least 1: {self.connection_pool_max_keepalive}' ) if self.connection_pool_max_keepalive > self.connection_pool_maxsize: raise ValueError( f'connection_pool_max_keepalive ({self.connection_pool_max_keepalive}) must not exceed ' f'connection_pool_maxsize ({self.connection_pool_maxsize})' ) return self T = TypeVar('T', bound=float | int) def _read_number( name: str, cast: type[T], default: T, *, minimum: T | None = None, ) -> T: raw = environ.get(name) if not raw: return default try: value = cast(raw) except (TypeError, ValueError): return default if minimum is not None and value < minimum: return default return value

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/yshalsager/mcp-4get'

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