"""Configuration management for MCP Server."""
import os
from pathlib import Path
from typing import Optional
import yaml
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class WebSocketConfig(BaseSettings):
"""WebSocket configuration."""
ping_interval: int = Field(default=300, description="WebSocket ping interval in seconds")
ping_timeout: int = Field(default=300, description="WebSocket ping timeout in seconds")
class GrpcConfig(BaseSettings):
"""gRPC server configuration."""
host: str = Field(default="localhost", description="gRPC server host")
port: int = Field(default=50051, description="gRPC server port")
timeout: int = Field(default=300, description="gRPC timeout in seconds")
class HttpConfig(BaseSettings):
"""HTTP client configuration."""
base_url: str = Field(
default="http://localhost:8901", description="Base URL for HTTP API"
)
ws_url: str = Field(
default="ws://localhost:8765/chatsocket", description="WebSocket URL"
)
token_url: str = Field(
default="https://app.lab0.signalfx.com/v2/jwt/token",
description="Token endpoint URL",
)
class Settings(BaseSettings):
"""Main application settings."""
model_config = SettingsConfigDict(
env_prefix="MCP_",
env_nested_delimiter="__",
case_sensitive=False,
)
log_dir: str = Field(default="./log", description="Directory for logs and artifacts")
session_timeout_sec: int = Field(default=300, description="Session timeout in seconds")
ws: WebSocketConfig = Field(default_factory=WebSocketConfig)
grpc: GrpcConfig = Field(default_factory=GrpcConfig)
http: HttpConfig = Field(default_factory=HttpConfig)
@field_validator("log_dir")
@classmethod
def validate_log_dir(cls, v: str) -> str:
"""Validate and create log directory."""
log_path = Path(v)
log_path.mkdir(parents=True, exist_ok=True)
return str(log_path.absolute())
def load_settings() -> Settings:
"""
Load settings from config file and environment variables.
Environment variables override config file values.
Priority: ENV vars > config.yaml > defaults
Environment variable examples:
- MCP_LOG_DIR
- MCP_GRPC__HOST
- MCP_GRPC__PORT
- MCP_HTTP__BASE_URL
Returns:
Settings: Loaded and validated settings
"""
# Check for config file path
config_path = os.getenv("CONFIG_PATH", "./config.yaml")
config_file = Path(config_path)
config_data = {}
# Load from YAML if exists
if config_file.exists():
with open(config_file, "r") as f:
config_data = yaml.safe_load(f) or {}
# Create settings - pydantic-settings will merge env vars automatically
settings = Settings(**config_data)
return settings
# Global settings instance (can be imported)
_settings: Optional[Settings] = None
def get_settings() -> Settings:
"""Get or create global settings instance."""
global _settings
if _settings is None:
_settings = load_settings()
return _settings