initial mcp server setup

This commit is contained in:
OwusuBlessing
2025-09-11 23:13:58 +01:00
commit 20f96c0f30
141 changed files with 14444 additions and 0 deletions
@@ -0,0 +1,7 @@
# Configuration management
from .config_manager import ConfigManager
from .server_config import ServerConfig
from .client_config import ClientConfig
from .transport_config import TransportConfig
__all__ = ['ConfigManager', 'ServerConfig', 'ClientConfig', 'TransportConfig']
@@ -0,0 +1,70 @@
"""
Client Configuration
"""
from typing import Dict, Any, Optional
from dataclasses import dataclass
@dataclass
class ClientConfig:
"""Configuration class for MCP clients"""
provider: str = "openai"
model: str = "gpt-4o"
api_key: Optional[str] = None
# Model parameters
temperature: float = 0.7
max_tokens: int = 1000
top_p: float = 1.0
# Connection settings
timeout: int = 30
max_retries: int = 3
retry_delay: float = 1.0
# MCP-specific settings
enable_tool_calling: bool = True
enable_resource_access: bool = True
enable_prompts: bool = True
# Transport settings
transport_host: str = "localhost"
transport_port: int = 8050
transport_endpoint: str = "/sse"
@classmethod
def from_dict(cls, config_dict: Dict[str, Any]) -> 'ClientConfig':
"""Create ClientConfig from dictionary"""
return cls(**config_dict)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
return self.__dict__.copy()
def get_provider_config(self) -> Dict[str, Any]:
"""Get provider-specific configuration"""
return {
"model_name": self.model,
"api_key": self.api_key,
"temperature": self.temperature,
"max_tokens": self.max_tokens,
"top_p": self.top_p,
"timeout": self.timeout,
"max_retries": self.max_retries,
"retry_delay": self.retry_delay,
}
def validate(self) -> bool:
"""Validate configuration"""
if not self.provider:
return False
if not self.model:
return False
if not self.api_key:
return False
if self.temperature < 0 or self.temperature > 2:
return False
if self.max_tokens < 1:
return False
return True
@@ -0,0 +1,133 @@
"""
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
@@ -0,0 +1,72 @@
"""
Server Configuration
"""
from typing import Dict, Any, Optional
from dataclasses import dataclass
from ..core.types import TransportType
@dataclass
class ServerConfig:
"""Configuration class for MCP servers"""
name: str = "MCP Server"
version: str = "1.0.0"
transport: TransportType = TransportType.STDIO
host: str = "0.0.0.0"
port: int = 8050
stateless_http: bool = True
# Tool configurations
enable_default_tools: bool = True
custom_tools: Optional[Dict[str, Any]] = None
# Resource configurations
enable_file_resources: bool = False
resource_paths: Optional[list[str]] = None
# Prompt configurations
enable_default_prompts: bool = False
custom_prompts: Optional[Dict[str, Any]] = None
# Performance settings
max_concurrent_requests: int = 10
request_timeout: int = 30
def __post_init__(self):
"""Initialize optional fields"""
if self.custom_tools is None:
self.custom_tools = {}
if self.resource_paths is None:
self.resource_paths = []
if self.custom_prompts is None:
self.custom_prompts = {}
@classmethod
def from_dict(cls, config_dict: Dict[str, Any]) -> 'ServerConfig':
"""Create ServerConfig from dictionary"""
# Convert transport string to enum
if 'transport' in config_dict and isinstance(config_dict['transport'], str):
config_dict['transport'] = TransportType(config_dict['transport'].lower())
return cls(**config_dict)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
result = self.__dict__.copy()
result['transport'] = self.transport.value
return result
def get_transport_config(self) -> Dict[str, Any]:
"""Get transport-specific configuration"""
if self.transport == TransportType.SSE:
return {
"host": self.host,
"port": self.port,
"endpoint": "/sse",
}
elif self.transport == TransportType.STDIO:
return {}
else:
return {}
@@ -0,0 +1,89 @@
"""
Transport Configuration
"""
from typing import Dict, Any, Optional
from dataclasses import dataclass
from ..core.types import TransportType
@dataclass
class TransportConfig:
"""Configuration class for MCP transport"""
transport_type: TransportType = TransportType.STDIO
# SSE-specific settings
sse_host: str = "localhost"
sse_port: int = 8050
sse_endpoint: str = "/sse"
sse_timeout: int = 30
sse_reconnect_delay: float = 1.0
sse_max_reconnects: int = 5
# STDIO-specific settings
stdio_command: Optional[str] = None
stdio_args: Optional[list[str]] = None
stdio_env: Optional[Dict[str, str]] = None
stdio_cwd: Optional[str] = None
# General transport settings
buffer_size: int = 8192
encoding: str = "utf-8"
enable_compression: bool = False
enable_ssl: bool = False
@classmethod
def from_dict(cls, config_dict: Dict[str, Any]) -> 'TransportConfig':
"""Create TransportConfig from dictionary"""
# Convert transport string to enum
if 'transport_type' in config_dict and isinstance(config_dict['transport_type'], str):
config_dict['transport_type'] = TransportType(config_dict['transport_type'].lower())
return cls(**config_dict)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
result = self.__dict__.copy()
result['transport_type'] = self.transport_type.value
return result
def get_sse_config(self) -> Dict[str, Any]:
"""Get SSE-specific configuration"""
return {
"host": self.sse_host,
"port": self.sse_port,
"endpoint": self.sse_endpoint,
"timeout": self.sse_timeout,
"reconnect_delay": self.sse_reconnect_delay,
"max_reconnects": self.sse_max_reconnects,
}
def get_stdio_config(self) -> Dict[str, Any]:
"""Get STDIO-specific configuration"""
config = {}
if self.stdio_command:
config["command"] = self.stdio_command
if self.stdio_args:
config["args"] = self.stdio_args
if self.stdio_env:
config["env"] = self.stdio_env
if self.stdio_cwd:
config["cwd"] = self.stdio_cwd
return config
def get_transport_config(self) -> Dict[str, Any]:
"""Get transport configuration based on type"""
config = {
"buffer_size": self.buffer_size,
"encoding": self.encoding,
"enable_compression": self.enable_compression,
"enable_ssl": self.enable_ssl,
}
if self.transport_type == TransportType.SSE:
config.update(self.get_sse_config())
elif self.transport_type == TransportType.STDIO:
config.update(self.get_stdio_config())
return config