""" Base module with common imports and utilities for Enhanced MCP Tools """ # Standard library imports import ast import asyncio import json import os import platform import re import shutil import subprocess import sys import time import uuid from collections import defaultdict from datetime import datetime from pathlib import Path from typing import Any, Dict, List, Literal, Optional, Union # Third-party imports with fallbacks try: import aiofiles except ImportError: aiofiles = None try: import psutil except ImportError: psutil = None try: import requests except ImportError: requests = None # FastMCP imports - these are REQUIRED for MCP functionality try: from mcp.types import ToolAnnotations from fastmcp import Context, FastMCP from fastmcp.contrib.mcp_mixin import MCPMixin, mcp_prompt, mcp_resource, mcp_tool # Verify that MCPMixin has the required register_all method if not hasattr(MCPMixin, 'register_all'): raise ImportError("MCPMixin is missing register_all method - FastMCP version may be incompatible") FASTMCP_AVAILABLE = True except ImportError as e: # FastMCP is REQUIRED - no silent fallbacks that break functionality import sys print(f"🚨 CRITICAL: FastMCP import failed: {e}") print("📋 Enhanced MCP Tools requires FastMCP to function.") print("🔧 Please install with: pip install fastmcp") print(" Or check your FastMCP installation and version compatibility.") # Still define the imports to prevent NameError, but mark as unavailable Context = None FastMCP = None MCPMixin = object # This will cause clear errors instead of silent failures mcp_tool = lambda **kwargs: lambda func: func mcp_resource = lambda **kwargs: lambda func: func mcp_prompt = lambda **kwargs: lambda func: func ToolAnnotations = None FASTMCP_AVAILABLE = False # Don't exit here - let individual modules handle the error appropriately # Common utility functions that multiple modules will use class MCPBase: """Base class with common functionality for all MCP tool classes""" def __init__(self): # Check if FastMCP is properly available when instantiating if not FASTMCP_AVAILABLE: raise RuntimeError( "🚨 Enhanced MCP Tools requires FastMCP but it's not available.\n" "Please install with: pip install fastmcp" ) def verify_mcp_ready(self) -> bool: """Verify that this instance is ready for MCP registration""" if not FASTMCP_AVAILABLE: return False if not hasattr(self, 'register_all'): return False return True def safe_register_all(self, app: 'FastMCP', prefix: str = None) -> bool: """Safely register all tools with better error handling""" if not self.verify_mcp_ready(): print(f"❌ Cannot register {self.__class__.__name__}: FastMCP not available or class not properly configured") return False try: if prefix: self.register_all(app, prefix=prefix) print(f"✅ Registered {self.__class__.__name__} tools with prefix '{prefix}'") else: self.register_all(app) print(f"✅ Registered {self.__class__.__name__} tools") return True except Exception as e: print(f"❌ Failed to register {self.__class__.__name__}: {e}") return False async def log_info(self, message: str, ctx: Optional[Context] = None): """Helper to log info messages""" if ctx: await ctx.info(message) else: print(f"INFO: {message}") async def log_warning(self, message: str, ctx: Optional[Context] = None): """Helper to log warning messages""" if ctx: await ctx.warning(message) else: print(f"WARNING: {message}") async def log_error(self, message: str, ctx: Optional[Context] = None): """Helper to log error messages""" if ctx: await ctx.error(message) else: print(f"ERROR: {message}") async def log_critical_error(self, message: str, exception: Exception = None, ctx: Optional[Context] = None): """Helper to log critical error messages with enhanced detail For critical tool failures that prevent completion but don't corrupt data. Uses ctx.error() as the highest severity in current FastMCP. """ if exception: error_detail = f"CRITICAL: {message} | Exception: {type(exception).__name__}: {str(exception)}" else: error_detail = f"CRITICAL: {message}" if ctx: await ctx.error(error_detail) else: print(f"CRITICAL ERROR: {error_detail}") async def log_emergency(self, message: str, exception: Exception = None, ctx: Optional[Context] = None): """Helper to log emergency-level errors RESERVED FOR TRUE EMERGENCIES: data corruption, security breaches, system instability. Currently uses ctx.error() with EMERGENCY prefix since FastMCP doesn't have emergency(). If FastMCP adds emergency() method in future, this will be updated. """ if exception: error_detail = f"EMERGENCY: {message} | Exception: {type(exception).__name__}: {str(exception)}" else: error_detail = f"EMERGENCY: {message}" if ctx: # Check if emergency method exists (future-proofing) if hasattr(ctx, 'emergency'): await ctx.emergency(error_detail) else: # Fallback to error with EMERGENCY prefix await ctx.error(error_detail) else: print(f"🚨 EMERGENCY: {error_detail}") # Could also implement additional emergency actions here: # - Write to emergency log file # - Send alerts # - Trigger backup/recovery procedures # Export common dependencies for use by other modules __all__ = [ # Standard library "os", "sys", "re", "ast", "json", "time", "uuid", "shutil", "asyncio", "subprocess", # Typing "Optional", "Any", "Union", "Literal", "Dict", "List", # Path and datetime "Path", "datetime", "defaultdict", # Third-party "aiofiles", "psutil", "requests", # FastMCP "MCPMixin", "mcp_tool", "mcp_resource", "mcp_prompt", "FastMCP", "Context", "ToolAnnotations", "FASTMCP_AVAILABLE", # Base class "MCPBase", ]