"""
Configuration management for the MCP server.
Supports:
- Environment variables (primary)
- Sensible defaults
Environment Variables:
MCP_SERVER_NAME: Server display name (default: "mcp-server")
MCP_LOG_LEVEL: Logging level (default: "INFO")
MCP_ALLOWED_PATHS: Comma-separated paths for file operations
MCP_CUSTOM_VAR_*: Any custom variables prefixed with MCP_CUSTOM_VAR_
"""
import os
from dataclasses import dataclass, field
from typing import Dict, List, Optional
@dataclass
class ServerConfig:
"""
Server configuration loaded from environment variables.
Attributes:
server_name: Display name shown to MCP clients
log_level: Logging level (DEBUG, INFO, WARNING, ERROR)
allowed_paths: List of file system paths accessible to file tools
custom_vars: Dictionary of custom variables from MCP_CUSTOM_VAR_* env vars
"""
server_name: str = "mcp-server"
log_level: str = "INFO"
allowed_paths: List[str] = field(default_factory=list)
custom_vars: Dict[str, str] = field(default_factory=dict)
@classmethod
def from_env(cls) -> "ServerConfig":
"""
Load configuration from environment variables.
Returns:
ServerConfig instance with values from environment
"""
# Parse allowed paths from comma-separated string
allowed_paths_str = os.getenv("MCP_ALLOWED_PATHS", "")
allowed_paths = [p.strip() for p in allowed_paths_str.split(",") if p.strip()]
# Collect custom variables (MCP_CUSTOM_VAR_*)
custom_vars = {}
for key, value in os.environ.items():
if key.startswith("MCP_CUSTOM_VAR_"):
var_name = key.replace("MCP_CUSTOM_VAR_", "").lower()
custom_vars[var_name] = value
return cls(
server_name=os.getenv("MCP_SERVER_NAME", "mcp-server"),
log_level=os.getenv("MCP_LOG_LEVEL", "INFO"),
allowed_paths=allowed_paths,
custom_vars=custom_vars,
)
def get_custom_var(self, name: str, default: Optional[str] = None) -> Optional[str]:
"""
Get a custom variable by name.
Args:
name: Variable name (case-insensitive)
default: Default value if not found
Returns:
Variable value or default
"""
return self.custom_vars.get(name.lower(), default)
def is_path_allowed(self, path: str) -> bool:
"""
Check if a path is within allowed directories.
Args:
path: Path to check
Returns:
True if path is allowed, False otherwise
"""
if not self.allowed_paths:
return False
from pathlib import Path
target = Path(path).resolve()
return any(
target.is_relative_to(Path(allowed).resolve()) for allowed in self.allowed_paths
)