Skip to main content
Glama

HopperPyMCP

by dflatline
MIT License
3
install.py11.5 kB
#!/usr/bin/env python3 """ HopperPyMCP Installation Script Automatically detects your Python environment (conda, uv, venv, or system Python) and installs the FastMCP server script to your Hopper disassembler Scripts directory. Usage: python install.py [--force] [--dry-run] """ import sys import os import platform import shutil import subprocess import argparse from pathlib import Path def detect_python_environment(): """Detect current Python environment and return environment information.""" env_info = { 'type': None, # 'conda', 'venv', 'uv', 'system' 'python_executable': sys.executable, 'site_packages': [], 'lib_path': None, 'lib_dynload': None, 'package_manager': None, # 'conda', 'pip', 'uv' 'environment_path': None } print("🔍 Detecting Python environment...") # Priority detection order: if os.environ.get('CONDA_PREFIX'): env_info['type'] = 'conda' env_info['package_manager'] = 'conda' env_info['environment_path'] = os.environ['CONDA_PREFIX'] print(f" ✅ Detected conda environment: {env_info['environment_path']}") elif os.environ.get('VIRTUAL_ENV'): env_info['type'] = 'venv' env_info['package_manager'] = 'pip' env_info['environment_path'] = os.environ['VIRTUAL_ENV'] print(f" ✅ Detected virtual environment: {env_info['environment_path']}") elif sys.prefix != sys.base_prefix: env_info['type'] = 'virtualenv' env_info['package_manager'] = 'pip' env_info['environment_path'] = sys.prefix print(f" ✅ Detected virtual environment: {env_info['environment_path']}") else: env_info['type'] = 'system' env_info['package_manager'] = 'pip' env_info['environment_path'] = sys.prefix print(f" ✅ Detected system Python: {env_info['environment_path']}") # Check for uv if present if shutil.which('uv') and (os.path.exists('.venv') or os.path.exists('uv.lock')): env_info['package_manager'] = 'uv' print(" ✅ uv package manager detected") return env_info def get_python_paths(env_info): """Get the three critical paths needed for sys.path.insert().""" print("🔍 Determining Python paths...") if env_info['type'] == 'conda': # Use CONDA_PREFIX for conda environments conda_prefix = os.environ['CONDA_PREFIX'] python_version = f"python{sys.version_info.major}.{sys.version_info.minor}" paths = { 'lib_dynload': os.path.join(conda_prefix, 'lib', python_version, 'lib-dynload'), 'lib_path': os.path.join(conda_prefix, 'lib', python_version), 'site_packages': os.path.join(conda_prefix, 'lib', python_version, 'site-packages') } print(f" 📁 Using conda paths based on: {conda_prefix}") else: # Use site.getsitepackages() and sys.path analysis for other environments import site # Get site packages - handle multiple possible locations try: site_packages_list = site.getsitepackages() if site_packages_list: site_packages = site_packages_list[0] else: # Fallback for some virtualenvs site_packages = site.getusersitepackages() except AttributeError: # Some older virtualenv versions don't have getsitepackages site_packages = os.path.join(sys.prefix, 'lib', f'python{sys.version_info.major}.{sys.version_info.minor}', 'site-packages') lib_path = os.path.dirname(site_packages) lib_dynload = os.path.join(lib_path, 'lib-dynload') paths = { 'lib_dynload': lib_dynload, 'lib_path': lib_path, 'site_packages': site_packages } print(f" 📁 Using site-packages paths based on: {site_packages}") # Validate paths exist for path_name, path_value in paths.items(): if os.path.exists(path_value): print(f" ✅ {path_name}: {path_value}") else: print(f" ⚠️ {path_name}: {path_value} (does not exist)") return paths def get_hopper_script_dir(): """Get Hopper script directory for current platform.""" print("🔍 Determining Hopper Scripts directory...") system = platform.system().lower() home = os.path.expanduser('~') if system == 'darwin': # macOS hopper_dir = os.path.join(home, 'Library', 'Application Support', 'Hopper', 'Scripts') print(f" 📁 macOS detected: {hopper_dir}") elif system == 'linux': hopper_dir = os.path.join(home, 'GNUstep', 'Library', 'ApplicationSupport', 'Hopper', 'Scripts') print(f" 📁 Linux detected: {hopper_dir}") else: raise OSError(f"❌ Unsupported platform: {system}. Only macOS and Linux are supported.") return hopper_dir def install_dependencies(env_info, dry_run=False): """Install requirements using appropriate package manager.""" print("📦 Installing dependencies...") if not os.path.exists('requirements.txt'): print(" ⚠️ requirements.txt not found, skipping dependency installation") return cmd = [] if env_info['package_manager'] == 'conda': # Try pip within conda environment (more reliable than conda install) cmd = [sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt'] print(" 🔧 Using pip within conda environment") elif env_info['package_manager'] == 'uv': cmd = ['uv', 'pip', 'install', '-r', 'requirements.txt'] print(" 🔧 Using uv pip") else: # pip cmd = [sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt'] print(" 🔧 Using pip") if dry_run: print(f" 🔍 Would run: {' '.join(cmd)}") return try: result = subprocess.run(cmd, check=True, capture_output=True, text=True) print(" ✅ Dependencies installed successfully") if result.stdout: print(f" 📝 Output: {result.stdout.strip()}") except subprocess.CalledProcessError as e: print(f" ❌ Failed to install dependencies: {e}") if e.stderr: print(f" 📝 Error: {e.stderr.strip()}") raise except FileNotFoundError: print(f" ❌ Package manager '{env_info['package_manager']}' not found") raise def substitute_template(template_path, output_path, substitutions, dry_run=False): """Replace placeholders in template with actual paths.""" print(f"🔧 Creating configured script from template...") if not os.path.exists(template_path): raise FileNotFoundError(f"Template file not found: {template_path}") with open(template_path, 'r', encoding='utf-8') as f: content = f.read() print(" 🔍 Applying substitutions:") for placeholder, value in substitutions.items(): print(f" {placeholder} -> {value}") content = content.replace(placeholder, value) if dry_run: print(f" 🔍 Would write configured script to: {output_path}") return with open(output_path, 'w', encoding='utf-8') as f: f.write(content) print(f" ✅ Configured script created: {output_path}") def validate_installation(): """Validate that FastMCP can be imported after installation.""" print("🔍 Validating installation...") try: import fastmcp print(" ✅ FastMCP import successful") return True except ImportError as e: print(f" ❌ FastMCP import failed: {e}") print(" 💡 You may need to install dependencies manually") return False def main(): """Main installation process.""" parser = argparse.ArgumentParser(description='Install HopperPyMCP to Hopper Scripts directory') parser.add_argument('--force', action='store_true', help='Overwrite existing installation without prompting') parser.add_argument('--dry-run', action='store_true', help='Show what would be done without actually doing it') args = parser.parse_args() print("🚀 HopperPyMCP Installation Script") print("=" * 50) try: # 1. Detect environment env_info = detect_python_environment() # 2. Get Python paths paths = get_python_paths(env_info) # 3. Validate template exists template_path = 'fastmcp_server_template.py' if not os.path.exists(template_path): print(f"❌ Template file not found: {template_path}") print("💡 Make sure you're running this script from the HopperPyMCP directory") sys.exit(1) # 4. Install dependencies install_dependencies(env_info, dry_run=args.dry_run) # 5. Validate FastMCP is available if not args.dry_run: if not validate_installation(): print("⚠️ Warning: FastMCP validation failed, but continuing with installation") # 6. Create script from template configured_script = 'fastmcp_server_configured.py' substitutions = { '{{PYTHON_LIB_DYNLOAD}}': paths['lib_dynload'], '{{PYTHON_LIB_PATH}}': paths['lib_path'], '{{PYTHON_SITE_PACKAGES}}': paths['site_packages'] } substitute_template(template_path, configured_script, substitutions, dry_run=args.dry_run) # 7. Install to Hopper directory hopper_dir = get_hopper_script_dir() target_path = os.path.join(hopper_dir, 'fastmcp_server.py') if args.dry_run: print(f"🔍 Would create directory: {hopper_dir}") print(f"🔍 Would copy script to: {target_path}") else: # Check if target already exists if os.path.exists(target_path) and not args.force: response = input(f"Script already exists at {target_path}. Overwrite? (y/N): ") if response.lower() not in ['y', 'yes']: print("❌ Installation cancelled") sys.exit(1) # Create directory and copy file os.makedirs(hopper_dir, exist_ok=True) shutil.copy2(configured_script, target_path) print(f"✅ Successfully installed to: {target_path}") print("\n" + "=" * 50) print("🎉 Installation completed successfully!") print(f"📁 Script location: {target_path}") print("💡 You can now use the script from within Hopper!") print("💡 Run 'python uninstall.py' to remove the installation") # Clean up temporary file if not args.dry_run and os.path.exists(configured_script): os.remove(configured_script) except KeyboardInterrupt: print("\n❌ Installation cancelled by user") sys.exit(1) except Exception as e: print(f"\n❌ Installation failed: {e}") sys.exit(1) if __name__ == '__main__': main()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dflatline/HopperPyMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server