initial mcp server setup
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user