134 lines
4.6 KiB
Python
134 lines
4.6 KiB
Python
|
|
"""
|
||
|
|
Centralized Configuration Manager
|
||
|
|
"""
|
||
|
|
import os
|
||
|
|
import json
|
||
|
|
from typing import Dict, Any, Optional
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
try:
|
||
|
|
from dotenv import load_dotenv
|
||
|
|
DOTENV_AVAILABLE = True
|
||
|
|
except ImportError:
|
||
|
|
DOTENV_AVAILABLE = False
|
||
|
|
|
||
|
|
|
||
|
|
class ConfigManager:
|
||
|
|
"""Centralized configuration manager for MCP components"""
|
||
|
|
|
||
|
|
def __init__(self, config_file: Optional[str] = None, env_file: Optional[str] = None):
|
||
|
|
self._config_file = config_file or "config.json"
|
||
|
|
self._env_file = env_file or ".env"
|
||
|
|
self._config: Dict[str, Any] = {}
|
||
|
|
self._loaded = False
|
||
|
|
|
||
|
|
async def load_config(self) -> Dict[str, Any]:
|
||
|
|
"""Load configuration from all sources"""
|
||
|
|
if self._loaded:
|
||
|
|
return self._config
|
||
|
|
|
||
|
|
# Load environment variables
|
||
|
|
await self._load_env_vars()
|
||
|
|
|
||
|
|
# Load JSON configuration file
|
||
|
|
await self._load_json_config()
|
||
|
|
|
||
|
|
# Merge configurations
|
||
|
|
self._config = await self._merge_configs()
|
||
|
|
|
||
|
|
self._loaded = True
|
||
|
|
return self._config
|
||
|
|
|
||
|
|
async def _load_env_vars(self) -> None:
|
||
|
|
"""Load environment variables"""
|
||
|
|
if DOTENV_AVAILABLE and Path(self._env_file).exists():
|
||
|
|
load_dotenv(self._env_file)
|
||
|
|
|
||
|
|
async def _load_json_config(self) -> Dict[str, Any]:
|
||
|
|
"""Load JSON configuration file"""
|
||
|
|
config_path = Path(self._config_file)
|
||
|
|
|
||
|
|
if config_path.exists():
|
||
|
|
with open(config_path, 'r') as f:
|
||
|
|
return json.load(f)
|
||
|
|
|
||
|
|
return {}
|
||
|
|
|
||
|
|
async def _merge_configs(self) -> Dict[str, Any]:
|
||
|
|
"""Merge all configuration sources"""
|
||
|
|
merged = {}
|
||
|
|
|
||
|
|
# Start with JSON config as base
|
||
|
|
json_config = await self._load_json_config()
|
||
|
|
merged.update(json_config)
|
||
|
|
|
||
|
|
# Override with environment variables
|
||
|
|
env_config = await self._get_env_config()
|
||
|
|
self._deep_update(merged, env_config)
|
||
|
|
|
||
|
|
return merged
|
||
|
|
|
||
|
|
async def _get_env_config(self) -> Dict[str, Any]:
|
||
|
|
"""Get configuration from environment variables"""
|
||
|
|
env_config = {}
|
||
|
|
|
||
|
|
# Server configuration
|
||
|
|
if os.getenv("MCP_SERVER_NAME"):
|
||
|
|
env_config.setdefault("server", {})["name"] = os.getenv("MCP_SERVER_NAME")
|
||
|
|
if os.getenv("MCP_SERVER_HOST"):
|
||
|
|
env_config.setdefault("server", {})["host"] = os.getenv("MCP_SERVER_HOST")
|
||
|
|
if os.getenv("MCP_SERVER_PORT"):
|
||
|
|
env_config.setdefault("server", {})["port"] = int(os.getenv("MCP_SERVER_PORT"))
|
||
|
|
if os.getenv("MCP_TRANSPORT"):
|
||
|
|
env_config.setdefault("server", {})["transport"] = os.getenv("MCP_TRANSPORT")
|
||
|
|
|
||
|
|
# Client configuration
|
||
|
|
if os.getenv("MCP_AI_PROVIDER"):
|
||
|
|
env_config.setdefault("client", {})["provider"] = os.getenv("MCP_AI_PROVIDER")
|
||
|
|
if os.getenv("MCP_AI_MODEL"):
|
||
|
|
env_config.setdefault("client", {})["model"] = os.getenv("MCP_AI_MODEL")
|
||
|
|
|
||
|
|
# API keys
|
||
|
|
for provider in ["OPENAI", "CLAUDE", "GROK"]:
|
||
|
|
api_key = os.getenv(f"{provider}_API_KEY")
|
||
|
|
if api_key:
|
||
|
|
env_config.setdefault("api_keys", {})[provider.lower()] = api_key
|
||
|
|
|
||
|
|
return env_config
|
||
|
|
|
||
|
|
def _deep_update(self, base_dict: Dict[str, Any], update_dict: Dict[str, Any]) -> None:
|
||
|
|
"""Deep update dictionary"""
|
||
|
|
for key, value in update_dict.items():
|
||
|
|
if isinstance(value, dict) and key in base_dict and isinstance(base_dict[key], dict):
|
||
|
|
self._deep_update(base_dict[key], value)
|
||
|
|
else:
|
||
|
|
base_dict[key] = value
|
||
|
|
|
||
|
|
async def get_server_config(self) -> Dict[str, Any]:
|
||
|
|
"""Get server-specific configuration"""
|
||
|
|
config = await self.load_config()
|
||
|
|
return config.get("server", {})
|
||
|
|
|
||
|
|
async def get_client_config(self) -> Dict[str, Any]:
|
||
|
|
"""Get client-specific configuration"""
|
||
|
|
config = await self.load_config()
|
||
|
|
return config.get("client", {})
|
||
|
|
|
||
|
|
async def get_api_key(self, provider: str) -> Optional[str]:
|
||
|
|
"""Get API key for a specific provider"""
|
||
|
|
config = await self.load_config()
|
||
|
|
api_keys = config.get("api_keys", {})
|
||
|
|
return api_keys.get(provider.lower())
|
||
|
|
|
||
|
|
async def save_config(self, config: Dict[str, Any]) -> None:
|
||
|
|
"""Save configuration to file"""
|
||
|
|
with open(self._config_file, 'w') as f:
|
||
|
|
json.dump(config, f, indent=2)
|
||
|
|
|
||
|
|
async def update_config(self, updates: Dict[str, Any]) -> None:
|
||
|
|
"""Update configuration and save"""
|
||
|
|
config = await self.load_config()
|
||
|
|
self._deep_update(config, updates)
|
||
|
|
await self.save_config(config)
|
||
|
|
self._loaded = False # Force reload on next access
|