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,14 @@
# AI Client implementations
from .base_client import BaseAIClient
from .openai_client import OpenAIClient
from .claude_client import ClaudeClient
from .grok_client import GrokClient
from .client_factory import AIClientFactory
__all__ = [
'BaseAIClient',
'OpenAIClient',
'ClaudeClient',
'GrokClient',
'AIClientFactory'
]
@@ -0,0 +1,150 @@
"""
Base AI Client with common MCP integration functionality
"""
import json
import os
import sys
from typing import Any, Dict, List, Optional
from abc import ABC
# Add the project root to the path to import config
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if project_root not in sys.path:
sys.path.insert(0, project_root)
try:
from config import Config
CONFIG_AVAILABLE = True
except ImportError:
CONFIG_AVAILABLE = False
from ..core.interfaces import IAIClient, IMCPClient
class BaseAIClient(IAIClient, ABC):
"""Base class for AI clients with MCP integration"""
def __init__(self, model_name: str, provider: str, api_key: Optional[str] = None, **kwargs):
self._model_name = model_name
self._provider = provider
self._client = None
self._initialized = False
self._extra_config = kwargs
# Get API key from config if not provided
if api_key is None and CONFIG_AVAILABLE:
api_key = self._get_api_key_from_config()
if not api_key:
raise ValueError(f"API key not provided and could not be loaded from config for provider: {provider}")
self._api_key = api_key
def _get_api_key_from_config(self) -> Optional[str]:
"""Get API key from config based on provider"""
if not CONFIG_AVAILABLE:
return None
provider_key_map = {
"openai": Config.OPENAI_API_KEY,
"claude": Config.CLAUDE_API_KEY,
"grok": Config.GROK_API_KEY
}
return provider_key_map.get(self._provider)
@property
def model_name(self) -> str:
"""Get the AI model name"""
return self._model_name
async def initialize(self) -> None:
"""Initialize the AI client - to be implemented by subclasses"""
if self._initialized:
return
await self._initialize_client()
self._initialized = True
async def _initialize_client(self) -> None:
"""Initialize the specific AI client - to be implemented by subclasses"""
pass
async def process_with_tools(
self,
query: str,
available_tools: List[Dict[str, Any]],
mcp_client: IMCPClient
) -> str:
"""Process a query with MCP tools using a common pattern"""
# Format tools for the specific AI provider
formatted_tools = self._format_tools_for_provider(available_tools)
# Create initial messages
messages = [{"role": "user", "content": query}]
# Get AI response with tool calling
response = await self.chat_completion(
messages=messages,
tools=formatted_tools,
tool_choice="auto"
)
# Extract assistant message
assistant_message = response["choices"][0]["message"]
# Check if tools were called
if "tool_calls" in assistant_message and assistant_message["tool_calls"]:
# Add assistant message to conversation
messages.append(assistant_message)
# Process each tool call
for tool_call in assistant_message["tool_calls"]:
try:
# Extract tool call details
tool_name = tool_call["function"]["name"]
tool_args = json.loads(tool_call["function"]["arguments"])
# Call the tool via MCP client
tool_result = await mcp_client.call_tool(tool_name, tool_args)
# Add tool response to conversation
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"content": str(tool_result),
})
except Exception as e:
# Handle tool call errors
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"content": f"Error calling tool: {str(e)}",
})
# Get final response from AI with tool results
final_response = await self.chat_completion(
messages=messages,
tools=formatted_tools,
tool_choice="none" # Don't allow more tool calls
)
return final_response["choices"][0]["message"]["content"]
# No tools called, return direct response
return assistant_message["content"]
def _format_tools_for_provider(self, tools: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Format tools for the specific AI provider - to be implemented by subclasses"""
return tools
async def cleanup(self) -> None:
"""Clean up resources"""
if self._client:
await self._cleanup_client()
self._initialized = False
async def _cleanup_client(self) -> None:
"""Clean up the specific AI client - to be implemented by subclasses"""
pass
@@ -0,0 +1,56 @@
"""
Claude Client Implementation (Placeholder)
"""
from typing import Any, Dict, List, Optional
# Placeholder for Claude/Anthropic client
# This would need the actual Anthropic SDK when implemented
from .base_client import BaseAIClient
class ClaudeClient(BaseAIClient):
"""Claude client with MCP integration (Placeholder Implementation)"""
def __init__(
self,
model_name: str = "claude-3-opus-20240229",
api_key: Optional[str] = None,
**kwargs
):
# Note: This is a placeholder. You'll need to install the Anthropic SDK
# pip install anthropic
super().__init__(model_name, "claude", api_key, **kwargs)
# Claude specific configuration
self._temperature = kwargs.get("temperature", 0.7)
self._max_tokens = kwargs.get("max_tokens", 1000)
async def _initialize_client(self) -> None:
"""Initialize the Claude client"""
# TODO: Implement with actual Anthropic SDK
# self._client = Anthropic(api_key=self._api_key)
raise NotImplementedError("Claude client not yet implemented. Install Anthropic SDK and implement.")
async def chat_completion(
self,
messages: List[Dict[str, Any]],
tools: Optional[List[Dict[str, Any]]] = None,
**kwargs
) -> Dict[str, Any]:
"""Perform Claude chat completion"""
if not self._initialized:
await self.initialize()
# TODO: Implement Claude API call
raise NotImplementedError("Claude chat completion not yet implemented")
def _format_tools_for_provider(self, tools: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Format tools for Claude's expected format"""
# TODO: Implement Claude tool formatting
return tools
async def _cleanup_client(self) -> None:
"""Clean up Claude client"""
# TODO: Implement cleanup
pass
@@ -0,0 +1,104 @@
"""
AI Client Factory for easy client creation and management
"""
import os
import sys
from typing import Optional, Dict, Any
# Add the project root to the path to import config
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if project_root not in sys.path:
sys.path.insert(0, project_root)
try:
from config import Config
CONFIG_AVAILABLE = True
except ImportError:
CONFIG_AVAILABLE = False
from .base_client import BaseAIClient
from .openai_client import OpenAIClient
from .claude_client import ClaudeClient
from .grok_client import GrokClient
class AIClientFactory:
"""Factory class for creating AI clients with different providers"""
@staticmethod
def create_client(
provider: str,
model_name: Optional[str] = None,
api_key: Optional[str] = None,
**kwargs
) -> BaseAIClient:
"""Create an AI client for the specified provider"""
# Set default model names if not provided
if model_name is None:
if provider.lower() == "openai":
model_name = "gpt-4o"
elif provider.lower() == "claude":
model_name = "claude-3-opus-20240229"
elif provider.lower() == "grok":
model_name = "grok-1"
# Get API key from config if not provided
if api_key is None and CONFIG_AVAILABLE:
provider_key_map = {
"openai": Config.OPENAI_API_KEY,
"claude": Config.CLAUDE_API_KEY,
"grok": Config.GROK_API_KEY
}
api_key = provider_key_map.get(provider.lower())
if not api_key:
raise ValueError(f"API key not provided and could not be loaded from config for provider: {provider}")
# Create the appropriate client
provider_lower = provider.lower()
if provider_lower == "openai":
return OpenAIClient(model_name, api_key, **kwargs)
elif provider_lower == "claude":
return ClaudeClient(model_name, api_key, **kwargs)
elif provider_lower == "grok":
return GrokClient(model_name, api_key, **kwargs)
else:
raise ValueError(f"Unsupported AI provider: {provider}")
@staticmethod
def create_openai_client(
model_name: str = "gpt-4o",
api_key: Optional[str] = None,
**kwargs
) -> OpenAIClient:
"""Create an OpenAI client"""
return AIClientFactory.create_client("openai", model_name, api_key, **kwargs)
@staticmethod
def create_claude_client(
model_name: str = "claude-3-opus-20240229",
api_key: Optional[str] = None,
**kwargs
) -> ClaudeClient:
"""Create a Claude client"""
return AIClientFactory.create_client("claude", model_name, api_key, **kwargs)
@staticmethod
def create_grok_client(
model_name: str = "grok-1",
api_key: Optional[str] = None,
**kwargs
) -> GrokClient:
"""Create a Grok client"""
return AIClientFactory.create_client("grok", model_name, api_key, **kwargs)
@staticmethod
def get_available_providers() -> list[str]:
"""Get list of available AI providers"""
return ["openai", "claude", "grok"]
@staticmethod
def validate_provider(provider: str) -> bool:
"""Validate if a provider is supported"""
return provider.lower() in AIClientFactory.get_available_providers()
@@ -0,0 +1,55 @@
"""
Grok Client Implementation (Placeholder)
"""
from typing import Any, Dict, List, Optional
# Placeholder for Grok/xAI client
# This would need the xAI SDK or direct API integration when implemented
from .base_client import BaseAIClient
class GrokClient(BaseAIClient):
"""Grok client with MCP integration (Placeholder Implementation)"""
def __init__(
self,
model_name: str = "grok-1",
api_key: Optional[str] = None,
**kwargs
):
# Note: This is a placeholder. You'll need xAI API integration
super().__init__(model_name, "grok", api_key, **kwargs)
# Grok specific configuration
self._temperature = kwargs.get("temperature", 0.7)
self._max_tokens = kwargs.get("max_tokens", 1000)
async def _initialize_client(self) -> None:
"""Initialize the Grok client"""
# TODO: Implement with xAI API or SDK
# This might require direct HTTP calls to xAI API
raise NotImplementedError("Grok client not yet implemented. Implement xAI API integration.")
async def chat_completion(
self,
messages: List[Dict[str, Any]],
tools: Optional[List[Dict[str, Any]]] = None,
**kwargs
) -> Dict[str, Any]:
"""Perform Grok chat completion"""
if not self._initialized:
await self.initialize()
# TODO: Implement Grok API call
raise NotImplementedError("Grok chat completion not yet implemented")
def _format_tools_for_provider(self, tools: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Format tools for Grok's expected format"""
# TODO: Implement Grok tool formatting
return tools
async def _cleanup_client(self) -> None:
"""Clean up Grok client"""
# TODO: Implement cleanup
pass
@@ -0,0 +1,106 @@
"""
OpenAI Client Implementation
"""
from typing import Any, Dict, List, Optional
try:
from openai import AsyncOpenAI
OPENAI_AVAILABLE = True
except ImportError:
OPENAI_AVAILABLE = False
from .base_client import BaseAIClient
from config import Config
class OpenAIClient(BaseAIClient):
"""OpenAI client with MCP integration"""
def __init__(
self,
model_name: str = "gpt-4o",
api_key: Optional[str] = None,
**kwargs
):
if not OPENAI_AVAILABLE:
raise ImportError("OpenAI package not installed. Install with: pip install openai")
super().__init__(model_name, "openai", api_key, **kwargs)
# OpenAI specific configuration
self._temperature = kwargs.get("temperature", 0.7)
self._max_tokens = kwargs.get("max_tokens", 1000)
self._api_key = api_key or Config.OPENAI_API_KEY
async def _initialize_client(self) -> None:
"""Initialize the OpenAI client"""
self._client = AsyncOpenAI(api_key=self._api_key)
async def chat_completion(
self,
messages: List[Dict[str, Any]],
tools: Optional[List[Dict[str, Any]]] = None,
**kwargs
) -> Dict[str, Any]:
"""Perform OpenAI chat completion"""
if not self._initialized:
await self.initialize()
# Prepare request parameters
request_params = {
"model": self._model_name,
"messages": messages,
"temperature": self._temperature,
"max_tokens": self._max_tokens,
}
# Add tools if provided
if tools:
request_params["tools"] = tools
request_params["tool_choice"] = kwargs.get("tool_choice", "auto")
# Make the API call
response = await self._client.chat.completions.create(**request_params)
# Convert to standard format
return {
"choices": [
{
"message": {
"role": choice.message.role,
"content": choice.message.content,
"tool_calls": [
{
"id": tool_call.id,
"type": "function",
"function": {
"name": tool_call.function.name,
"arguments": tool_call.function.arguments,
}
}
for tool_call in (choice.message.tool_calls or [])
] if choice.message.tool_calls else None,
}
}
for choice in response.choices
]
}
def _format_tools_for_provider(self, tools: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""Format tools for OpenAI's expected format"""
formatted_tools = []
for tool in tools:
formatted_tool = {
"type": "function",
"function": {
"name": tool["name"],
"description": tool["description"],
"parameters": tool["inputSchema"],
}
}
formatted_tools.append(formatted_tool)
return formatted_tools
async def _cleanup_client(self) -> None:
"""Clean up OpenAI client"""
if self._client:
await self._client.close()