MCP Memory Service
by doobidoo
#!/usr/bin/env python3
"""
Enhanced Wrapper script for MCP Memory Service on Windows.
This script ensures that PyTorch is properly installed before running the memory server,
with improved debugging, error handling, and dependency management.
"""
# Disable sitecustomize.py and other import hooks to prevent recursion issues
# These must be set before importing any other modules
import os
os.environ["PYTHONNOUSERSITE"] = "1" # Disable user site-packages
os.environ["PYTHONPATH"] = "" # Clear PYTHONPATH
import sys
import subprocess
import importlib.util
import argparse
import traceback
import site
import platform
import time
from pathlib import Path
# Configure logging level
DEBUG = False
def parse_args():
"""Parse command line arguments."""
parser = argparse.ArgumentParser(description="MCP Memory Service Wrapper")
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
parser.add_argument("--no-auto-install", action="store_true", help="Disable automatic PyTorch installation")
parser.add_argument("--force-cpu", action="store_true", help="Force CPU-only mode even if GPU is available")
parser.add_argument("--chroma-path", type=str, help="Path to ChromaDB storage")
parser.add_argument("--backups-path", type=str, help="Path to backups storage")
return parser.parse_args()
def print_debug(text):
"""Print formatted debug text to stderr."""
if DEBUG:
print(f"[DEBUG] {text}", file=sys.stderr)
def print_info(text):
"""Print formatted info text to stderr."""
print(f"[INFO] {text}", file=sys.stderr)
def print_error(text):
"""Print formatted error text to stderr."""
print(f"[ERROR] {text}", file=sys.stderr)
def print_success(text):
"""Print formatted success text to stderr."""
print(f"[SUCCESS] {text}", file=sys.stderr)
def print_warning(text):
"""Print formatted warning text to stderr."""
print(f"[WARNING] {text}", file=sys.stderr)
def print_environment_info():
"""Print detailed environment information for debugging."""
print_debug("=== Environment Information ===")
print_debug(f"Python version: {sys.version}")
print_debug(f"Python executable: {sys.executable}")
print_debug(f"Platform: {platform.platform()}")
print_debug(f"System: {platform.system()} {platform.release()}")
print_debug(f"Architecture: {platform.machine()}")
print_debug(f"Current working directory: {os.getcwd()}")
print_debug(f"Script directory: {os.path.dirname(os.path.abspath(__file__))}")
# Print Python path
print_debug("Python path:")
for path in sys.path:
print_debug(f" - {path}")
# Print environment variables
print_debug("Environment variables:")
for key, value in os.environ.items():
if key.startswith("PYTHON") or key.startswith("PATH") or key.startswith("MCP") or "TORCH" in key:
print_debug(f" - {key}={value}")
# Check if running in virtual environment
in_venv = sys.prefix != sys.base_prefix
print_debug(f"Running in virtual environment: {in_venv}")
if in_venv:
print_debug(f"Virtual environment path: {sys.prefix}")
def check_installed_packages():
"""Check and print information about installed packages."""
print_debug("=== Installed Packages ===")
try:
import pkg_resources
for package in pkg_resources.working_set:
if package.key in ["torch", "torchvision", "torchaudio", "sentence-transformers",
"transformers", "chromadb", "mcp", "mcp-memory-service"]:
print_debug(f" - {package.key}=={package.version}")
except ImportError:
print_debug("Could not import pkg_resources to check installed packages")
def check_pytorch():
"""Check if PyTorch is installed and properly configured."""
print_info("Checking PyTorch installation")
# Save original sys.path
original_sys_path = sys.path.copy()
# Temporarily disable import hooks
original_meta_path = sys.meta_path
sys.meta_path = [finder for finder in sys.meta_path
if not hasattr(finder, 'find_spec') or
not hasattr(finder, 'blocked_packages')]
try:
# Add site-packages to sys.path
site_packages = os.path.join(sys.prefix, 'Lib', 'site-packages')
if site_packages not in sys.path:
sys.path.insert(0, site_packages)
# Try to find torch directly in site-packages
torch_path = os.path.join(site_packages, 'torch')
if os.path.exists(torch_path) and torch_path not in sys.path:
sys.path.insert(0, torch_path)
# Try to import torch directly
print_debug("Attempting to import torch directly")
# Use importlib to import torch
import importlib.util
import importlib.machinery
# Find the torch module spec
torch_spec = importlib.machinery.PathFinder.find_spec('torch', [site_packages])
if torch_spec is None:
print_error("Could not find torch module spec")
return False
# Load the torch module
torch = importlib.util.module_from_spec(torch_spec)
torch_spec.loader.exec_module(torch)
print_success(f"PyTorch is installed (version {torch.__version__})")
print_debug(f"PyTorch location: {torch.__file__}")
# Check if PyTorch C extensions are available
if hasattr(torch, '_C'):
print_debug("PyTorch C extensions are available")
else:
print_warning("PyTorch C extensions might not be properly installed")
# Check if CUDA is available
if torch.cuda.is_available():
print_success(f"CUDA is available (version {torch.version.cuda})")
print_info(f"GPU: {torch.cuda.get_device_name(0)}")
print_debug(f"GPU Memory: {torch.cuda.get_device_properties(0).total_memory / (1024**3):.2f} GB")
# Test a simple CUDA operation
try:
x = torch.rand(5, 3).cuda()
y = torch.rand(5, 3).cuda()
z = x + y
print_debug("Basic CUDA tensor operations work correctly")
except Exception as e:
print_warning(f"CUDA tensor operations failed: {e}")
print_warning("Falling back to CPU mode")
os.environ["CUDA_VISIBLE_DEVICES"] = ""
else:
print_info("CUDA is not available, using CPU-only mode")
# Check if DirectML is available
try:
directml_spec = importlib.machinery.PathFinder.find_spec('torch_directml', [site_packages])
if directml_spec is not None:
torch_directml = importlib.util.module_from_spec(directml_spec)
directml_spec.loader.exec_module(torch_directml)
print_success(f"DirectML is available (version {torch_directml.__version__})")
except Exception:
print_debug("DirectML is not available")
# Test a simple tensor operation
try:
x = torch.rand(5, 3)
y = torch.rand(5, 3)
z = x + y
print_debug("Basic tensor operations work correctly")
except Exception as e:
print_error(f"Failed to perform basic tensor operations: {e}")
return False
# Add torch to sys.modules to make it available to other modules
sys.modules['torch'] = torch
return True
except ImportError as e:
print_error(f"PyTorch is not installed: {e}")
return False
except Exception as e:
print_error(f"Error checking PyTorch installation: {e}")
print_debug(traceback.format_exc())
return False
finally:
# Restore original sys.path and meta_path
sys.path = original_sys_path
sys.meta_path = original_meta_path
def check_sentence_transformers():
"""Check if sentence-transformers is installed and properly configured."""
print_info("Checking sentence-transformers installation")
try:
import sentence_transformers
print_success(f"sentence-transformers is installed (version {sentence_transformers.__version__})")
print_debug(f"sentence-transformers location: {sentence_transformers.__file__}")
return True
except ImportError as e:
print_error(f"sentence-transformers is not installed: {e}")
return False
except Exception as e:
print_error(f"Error checking sentence-transformers installation: {e}")
print_debug(traceback.format_exc())
return False
def check_chromadb():
"""Check if ChromaDB is installed and properly configured."""
print_info("Checking ChromaDB installation")
try:
import chromadb
print_success(f"ChromaDB is installed (version {chromadb.__version__})")
print_debug(f"ChromaDB location: {chromadb.__file__}")
return True
except ImportError as e:
print_error(f"ChromaDB is not installed: {e}")
return False
except Exception as e:
print_error(f"Error checking ChromaDB installation: {e}")
print_debug(traceback.format_exc())
return False
def check_mcp():
"""Check if MCP is installed and properly configured."""
print_info("Checking MCP installation")
try:
import mcp
print_success(f"MCP is installed (version {mcp.__version__})")
print_debug(f"MCP location: {mcp.__file__}")
return True
except ImportError as e:
print_error(f"MCP is not installed: {e}")
return False
except Exception as e:
print_error(f"Error checking MCP installation: {e}")
print_debug(traceback.format_exc())
return False
def prevent_pip_auto_install():
"""Prevent pip from automatically installing packages."""
print_debug("Setting up environment variables to prevent automatic PyTorch installation")
# Set environment variables to prevent pip from installing dependencies
os.environ["PIP_NO_DEPENDENCIES"] = "1"
os.environ["PIP_NO_INSTALL"] = "1"
# Create a .pth file in site-packages to prevent automatic installation
try:
site_packages = site.getsitepackages()[0]
pth_path = os.path.join(site_packages, 'no_auto_install.pth')
if not os.path.exists(pth_path):
with open(pth_path, 'w') as f:
f.write("""
# Prevent automatic installation of packages
import os
os.environ["PIP_NO_DEPENDENCIES"] = "1"
os.environ["PIP_NO_INSTALL"] = "1"
""")
print_debug(f"Created .pth file at {pth_path}")
except Exception as e:
print_debug(f"Could not create .pth file: {e}")
# Monkey patch pip to prevent automatic installation
try:
# Try to import pip without triggering auto-installation
pip_spec = importlib.util.find_spec("pip")
if pip_spec:
import pip._internal.resolve
original_get_requirement = pip._internal.resolve.Resolver.get_installation_candidate
def patched_get_installation_candidate(self, requirement_set, candidate):
# Block torch installations from PyPI
if candidate.name in ['torch', 'torchvision', 'torchaudio'] and 'pypi.org' in str(candidate.link):
print_warning(f"Blocked automatic installation attempt for {candidate.name} from {candidate.link}")
return None
return original_get_requirement(self, requirement_set, candidate)
pip._internal.resolve.Resolver.get_installation_candidate = patched_get_installation_candidate
print_debug("Successfully patched pip to prevent automatic PyTorch installation")
except (ImportError, AttributeError) as e:
print_debug(f"Could not patch pip: {e}")
def install_pytorch(no_auto_install=False):
"""Install PyTorch using the Windows-specific installation script."""
if no_auto_install:
print_warning("Automatic PyTorch installation is disabled")
return False
print_info("Installing PyTorch using the Windows-specific installation script")
# Get the directory of this script
script_dir = os.path.dirname(os.path.abspath(__file__))
# Run the Windows-specific installation script
install_script = os.path.join(script_dir, "scripts", "install_windows.py")
if os.path.exists(install_script):
try:
print_debug(f"Running installation script: {install_script}")
subprocess.check_call([sys.executable, install_script])
print_success("PyTorch installed successfully")
# Reload sys.path to include newly installed packages
print_debug("Reloading sys.path to include newly installed packages")
site.main()
return True
except subprocess.SubprocessError as e:
print_error(f"Failed to install PyTorch: {e}")
return False
else:
print_error(f"Installation script not found: {install_script}")
return False
def setup_environment(args):
"""Set up environment variables for the memory server."""
print_info("Setting up environment variables")
# Set environment variables to prevent PyTorch from trying to update
os.environ["PIP_NO_DEPENDENCIES"] = "1"
os.environ["PIP_NO_INSTALL"] = "1"
# Set environment variables for better cross-platform compatibility
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
# For Windows with limited GPU memory, use smaller chunks
if platform.system().lower() == "windows":
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128"
# Force CPU mode if requested
if args.force_cpu:
print_info("Forcing CPU-only mode")
os.environ["CUDA_VISIBLE_DEVICES"] = ""
os.environ["MCP_MEMORY_FORCE_CPU"] = "1"
# Set ChromaDB path if provided
if args.chroma_path:
print_info(f"Using custom ChromaDB path: {args.chroma_path}")
os.environ["MCP_MEMORY_CHROMA_PATH"] = args.chroma_path
# Set backups path if provided
if args.backups_path:
print_info(f"Using custom backups path: {args.backups_path}")
os.environ["MCP_MEMORY_BACKUPS_PATH"] = args.backups_path
# Print all environment variables for debugging
if DEBUG:
print_debug("Environment variables after setup:")
for key, value in sorted(os.environ.items()):
if key.startswith("PYTHON") or key.startswith("PATH") or key.startswith("MCP") or "TORCH" in key:
print_debug(f" - {key}={value}")
def run_memory_server():
"""Run the MCP Memory Service with enhanced error handling."""
print_info("Starting MCP Memory Service")
# Save original sys.path and meta_path
original_sys_path = sys.path.copy()
original_meta_path = sys.meta_path
# Temporarily disable import hooks
sys.meta_path = [finder for finder in sys.meta_path
if not hasattr(finder, 'find_spec') or
not hasattr(finder, 'blocked_packages')]
try:
# Get the directory of this script
script_dir = os.path.dirname(os.path.abspath(__file__))
# Add src directory to path if it exists
src_dir = os.path.join(script_dir, "src")
if os.path.exists(src_dir) and src_dir not in sys.path:
print_debug(f"Adding {src_dir} to sys.path")
sys.path.insert(0, src_dir)
# Add site-packages to sys.path
site_packages = os.path.join(sys.prefix, 'Lib', 'site-packages')
if site_packages not in sys.path:
sys.path.insert(0, site_packages)
# Try to import using importlib
print_debug("Attempting to import mcp_memory_service.server using importlib")
import importlib.util
import importlib.machinery
# First try to find the module in site-packages
server_spec = importlib.machinery.PathFinder.find_spec('mcp_memory_service.server', [site_packages])
# If not found, try to find it in src directory
if server_spec is None and os.path.exists(src_dir):
server_spec = importlib.machinery.PathFinder.find_spec('mcp_memory_service.server', [src_dir])
if server_spec is None:
print_error("Could not find mcp_memory_service.server module spec")
sys.exit(1)
# Load the server module
server = importlib.util.module_from_spec(server_spec)
server_spec.loader.exec_module(server)
print_debug("Successfully imported mcp_memory_service.server")
# Run the memory server with error handling
try:
print_debug("Calling mcp_memory_service.server.main()")
server.main()
except Exception as e:
print_error(f"Error running memory server: {e}")
print_debug(traceback.format_exc())
sys.exit(1)
except ImportError as e:
print_error(f"Failed to import mcp_memory_service.server: {e}")
print_debug(traceback.format_exc())
sys.exit(1)
except Exception as e:
print_error(f"Error setting up memory server: {e}")
print_debug(traceback.format_exc())
sys.exit(1)
finally:
# Restore original sys.path and meta_path
sys.path = original_sys_path
sys.meta_path = original_meta_path
def main():
"""Main function with enhanced error handling and debugging."""
# Parse command line arguments
args = parse_args()
# Set global debug flag
global DEBUG
DEBUG = args.debug
print_info("Enhanced Memory wrapper script starting")
# Print detailed environment information for debugging
if DEBUG:
print_environment_info()
check_installed_packages()
# Set up environment variables
setup_environment(args)
# Prevent automatic PyTorch installation
prevent_pip_auto_install()
# Check if PyTorch is installed
pytorch_ok = check_pytorch()
if not pytorch_ok:
print_info("PyTorch is not installed or not working properly")
if not args.no_auto_install:
print_info("Attempting to install PyTorch")
if not install_pytorch(args.no_auto_install):
print_error("Failed to install PyTorch, cannot continue")
sys.exit(1)
else:
print_error("Automatic PyTorch installation is disabled, cannot continue")
sys.exit(1)
# Check other dependencies
st_ok = check_sentence_transformers()
chroma_ok = check_chromadb()
mcp_ok = check_mcp()
if not (st_ok and chroma_ok and mcp_ok):
print_warning("Some dependencies are missing or not working properly")
print_warning("Will attempt to continue anyway, but errors may occur")
# Add a small delay to ensure all imports are properly initialized
print_debug("Waiting 1 second before starting the memory server")
time.sleep(1)
# Run the memory server
run_memory_server()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print_info("Script interrupted by user")
sys.exit(0)
except Exception as e:
print_error(f"Unhandled exception: {e}")
print_debug(traceback.format_exc())
sys.exit(1)