Skip to content

how to contribute this code? #52

@ganbaaelmer

Description

@ganbaaelmer

i have created and make steps easy to Dremio MCP Server Setup Script for Windows and Claude Desktop

#!/usr/bin/env python3
"""
Dremio MCP Server Setup Script for Windows
This script automates the setup of Dremio MCP Server on Windows systems.
It handles Python/pip checks, dynamic path resolution, PAT generation, 
repository cloning, configuration, and Claude config management.

Features:
- Works with any Python installation (Windows Store, standard, conda, etc.)
- Dynamically finds executables regardless of installation method
- Handles all dependency installation automatically
- Works across different users without modification
"""

import os
import sys
import subprocess
import json
import platform
import shutil
from pathlib import Path
from urllib.parse import urlparse
import ctypes
import winreg
import tempfile
import traceback

# Global variable to store the uv executable path
UV_EXECUTABLE = None

def is_admin():
    """Check if the script is running with administrator privileges."""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

def find_python_scripts_dir():
    """Find the Scripts directory for the current Python installation."""
    possible_dirs = [
        # Standard installation
        Path(sys.executable).parent / "Scripts",
        # Virtual environment
        Path(sys.prefix) / "Scripts",
        # Windows Store Python
        Path(sys.executable).parent.parent / "Scripts",
        # User installation
        Path.home() / ".local" / "bin",
        # Alternative user installation
        Path(os.environ.get('APPDATA', '')) / "Python" / f"Python{sys.version_info.major}{sys.version_info.minor}" / "Scripts",
    ]
    
    # For Windows Store Python, check LocalCache paths
    if 'WindowsApps' in sys.executable:
        # Extract the package name from the executable path
        parts = Path(sys.executable).parts
        for i, part in enumerate(parts):
            if 'PythonSoftwareFoundation.Python' in part:
                package_name = part
                local_packages = Path(os.environ.get('LOCALAPPDATA', '')) / "Packages" / package_name / "LocalCache" / "local-packages"
                if local_packages.exists():
                    for subdir in local_packages.iterdir():
                        if subdir.is_dir() and subdir.name.startswith('Python'):
                            scripts_dir = subdir / "Scripts"
                            if scripts_dir.exists():
                                possible_dirs.insert(0, scripts_dir)
    
    # Return the first existing directory
    for dir_path in possible_dirs:
        if dir_path.exists():
            return dir_path
    
    # If none found, return the most likely one
    return Path(sys.executable).parent / "Scripts"

def add_to_path_temporarily(directory):
    """Add a directory to PATH for the current session."""
    if str(directory) not in os.environ.get('PATH', ''):
        os.environ['PATH'] = f"{directory};{os.environ.get('PATH', '')}"
        print(f"✅ Added {directory} to PATH for this session")

def check_python_installation():
    """Check if Python is properly installed and accessible."""
    print("\n=== Checking Python Installation ===")
    
    # Check current Python version
    python_version = sys.version_info
    print(f"Current Python version: {python_version.major}.{python_version.minor}.{python_version.micro}")
    print(f"Python executable: {sys.executable}")
    
    if python_version < (3, 7):
        print("❌ Python 3.7 or higher is required")
        print("Please download and install Python from: https://www.python.org/downloads/")
        return False
    
    print("✅ Python version is compatible")
    
    # Find and add Scripts directory to PATH
    scripts_dir = find_python_scripts_dir()
    print(f"Python Scripts directory: {scripts_dir}")
    add_to_path_temporarily(scripts_dir)
    
    return True

def check_pip_installation():
    """Check if pip is installed and working."""
    print("\n=== Checking pip Installation ===")
    
    try:
        # Check pip version
        result = subprocess.run([sys.executable, "-m", "pip", "--version"], 
                              capture_output=True, text=True)
        
        if result.returncode == 0:
            print(f"✅ pip is installed: {result.stdout.strip()}")
            
            # Check if pip is up to date
            print("Checking for pip updates...")
            update_result = subprocess.run(
                [sys.executable, "-m", "pip", "install", "--upgrade", "pip"],
                capture_output=True, text=True
            )
            
            if "Successfully installed" in update_result.stdout:
                print("✅ pip has been updated to the latest version")
            else:
                print("✅ pip is already up to date")
            
            return True
        else:
            print("❌ pip is not installed")
            return install_pip()
            
    except Exception as e:
        print(f"❌ Error checking pip: {e}")
        return install_pip()

def install_pip():
    """Install pip if it's not available."""
    print("\nAttempting to install pip...")
    
    try:
        # Try ensurepip first
        print("Trying ensurepip module...")
        result = subprocess.run([sys.executable, "-m", "ensurepip", "--default-pip"], 
                              capture_output=True, text=True)
        
        if result.returncode == 0:
            print("✅ pip installed successfully using ensurepip")
            return True
        
    except:
        pass
    
    # If ensurepip fails, try downloading get-pip.py
    print("Downloading get-pip.py...")
    try:
        import urllib.request
        get_pip_url = "https://bootstrap.pypa.io/get-pip.py"
        get_pip_path = Path(tempfile.gettempdir()) / "get-pip.py"
        
        urllib.request.urlretrieve(get_pip_url, get_pip_path)
        print("✅ Downloaded get-pip.py")
        
        # Run get-pip.py
        result = subprocess.run([sys.executable, str(get_pip_path)], 
                              capture_output=True, text=True)
        
        if result.returncode == 0:
            print("✅ pip installed successfully")
            # Clean up
            get_pip_path.unlink()
            return True
        else:
            print(f"❌ Failed to install pip: {result.stderr}")
            return False
            
    except Exception as e:
        print(f"❌ Error installing pip: {e}")
        print("\nPlease install pip manually:")
        print("1. Download https://bootstrap.pypa.io/get-pip.py")
        print("2. Run: python get-pip.py")
        return False

def ensure_dependencies():
    """Ensure all required dependencies are installed."""
    required_packages = ['requests']
    missing_packages = []
    
    for package in required_packages:
        try:
            __import__(package)
        except ImportError:
            missing_packages.append(package)
    
    if missing_packages:
        print(f"\nInstalling required dependencies: {', '.join(missing_packages)}...")
        for package in missing_packages:
            try:
                subprocess.check_call([sys.executable, "-m", "pip", "install", "--user", package])
                print(f"✅ {package} installed successfully")
            except subprocess.CalledProcessError:
                print(f"❌ Failed to install {package}")
                print(f"Please install manually with: pip install {package}")
                return False
        
        # Refresh the module path
        import site
        site.main()
        
        # Verify installation
        for package in missing_packages:
            try:
                __import__(package)
            except ImportError:
                print(f"❌ {package} is still not available after installation")
                return False
    
    return True

def find_executable(name, additional_paths=None):
    """Find an executable in PATH or common locations."""
    # First check if it's in PATH
    exe_path = shutil.which(name)
    if exe_path:
        return exe_path
    
    # Windows executable names
    exe_names = [name, f"{name}.exe", f"{name}.cmd", f"{name}.bat"]
    
    # Common installation locations
    search_paths = [
        # Current Python Scripts directory
        find_python_scripts_dir(),
        # System Python scripts
        Path(sys.prefix) / "Scripts",
        # User local
        Path.home() / ".local" / "bin",
        # Cargo/Rust installations
        Path.home() / ".cargo" / "bin",
        # Program Files
        Path(os.environ.get('PROGRAMFILES', 'C:\\Program Files')),
        Path(os.environ.get('PROGRAMFILES(X86)', 'C:\\Program Files (x86)')),
        # AppData locations
        Path(os.environ.get('LOCALAPPDATA', '')) / "Programs",
    ]
    
    if additional_paths:
        search_paths.extend(additional_paths)
    
    # Search for the executable
    for path in search_paths:
        if path.exists():
            for exe_name in exe_names:
                exe_path = path / exe_name
                if exe_path.exists() and exe_path.is_file():
                    return str(exe_path)
                
                # Also check in subdirectories one level deep
                try:
                    for subdir in path.iterdir():
                        if subdir.is_dir():
                            sub_exe_path = subdir / exe_name
                            if sub_exe_path.exists() and sub_exe_path.is_file():
                                return str(sub_exe_path)
                except:
                    pass
    
    return None

def find_uv_executable():
    """Find the uv executable with comprehensive search."""
    global UV_EXECUTABLE
    
    # Check if already found
    if UV_EXECUTABLE:
        return UV_EXECUTABLE
    
    print("Searching for uv executable...")
    
    # First try standard search
    uv_path = find_executable("uv")
    if uv_path:
        UV_EXECUTABLE = uv_path
        return uv_path
    
    # Check if uv is installed as a Python module
    try:
        import uv
        # If we can import it, we can run it with python -m
        UV_EXECUTABLE = "python -m uv"
        print("✅ Found uv as Python module")
        return UV_EXECUTABLE
    except ImportError:
        pass
    
    return None

def run_uv_command(args, cwd=None):
    """Run a uv command with proper executable handling."""
    global UV_EXECUTABLE
    
    if not UV_EXECUTABLE:
        raise Exception("uv executable not found")
    
    if UV_EXECUTABLE == "python -m uv":
        cmd = [sys.executable, "-m", "uv"] + args
    else:
        cmd = [UV_EXECUTABLE] + args
    
    return subprocess.run(cmd, capture_output=True, text=True, cwd=cwd)

def install_uv():
    """Install uv package manager using multiple methods."""
    print("\n=== Installing uv Package Manager ===")
    
    # Method 1: Try pip install
    print("Method 1: Installing via pip...")
    try:
        result = subprocess.run(
            [sys.executable, "-m", "pip", "install", "--user", "uv"],
            capture_output=True, text=True
        )
        
        if result.returncode == 0:
            print("✅ uv installed via pip")
            
            # Try to find it again
            if find_uv_executable():
                print(f"✅ uv executable found at: {UV_EXECUTABLE}")
                return True
            else:
                print("⚠️  uv installed but executable not found, will use 'python -m uv'")
                return True
    except Exception as e:
        print(f"❌ pip install failed: {e}")
    
    # Method 2: Try PowerShell installer (if admin or execution policy allows)
    print("\nMethod 2: Trying PowerShell installer...")
    try:
        # First set execution policy for current process
        subprocess.run([
            "powershell", "-Command",
            "Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force"
        ], capture_output=True)
        
        # Download and run installer
        ps_command = """
        $ErrorActionPreference = 'Stop'
        try {
            Invoke-WebRequest -UseBasicParsing -Uri 'https://astral.sh/uv/install.ps1' | Invoke-Expression
        } catch {
            Write-Error $_.Exception.Message
            exit 1
        }
        """
        
        result = subprocess.run(
            ["powershell", "-Command", ps_command],
            capture_output=True, text=True
        )
        
        if result.returncode == 0:
            print("✅ uv installed via PowerShell")
            # Add common uv install location to PATH
            uv_install_dir = Path.home() / ".cargo" / "bin"
            add_to_path_temporarily(uv_install_dir)
            
            if find_uv_executable():
                print(f"✅ uv executable found at: {UV_EXECUTABLE}")
                return True
    except Exception as e:
        print(f"❌ PowerShell install failed: {e}")
    
    # Method 3: Direct download (last resort)
    print("\nMethod 3: Direct download method...")
    try:
        import urllib.request
        import zipfile
        
        # Determine architecture
        import platform
        machine = platform.machine().lower()
        if machine in ['amd64', 'x86_64']:
            arch = 'x86_64'
        else:
            arch = 'i686'
        
        # Download URL for Windows
        download_url = f"https://github.com/astral-sh/uv/releases/latest/download/uv-{arch}-pc-windows-msvc.zip"
        
        # Download to temp directory
        temp_dir = Path(tempfile.gettempdir()) / "uv_install"
        temp_dir.mkdir(exist_ok=True)
        zip_path = temp_dir / "uv.zip"
        
        print(f"Downloading from: {download_url}")
        urllib.request.urlretrieve(download_url, zip_path)
        
        # Extract
        with zipfile.ZipFile(zip_path, 'r') as zip_ref:
            zip_ref.extractall(temp_dir)
        
        # Find uv.exe in extracted files
        for root, dirs, files in os.walk(temp_dir):
            if 'uv.exe' in files:
                uv_exe = Path(root) / 'uv.exe'
                # Copy to Python Scripts directory
                scripts_dir = find_python_scripts_dir()
                target_path = scripts_dir / 'uv.exe'
                shutil.copy2(uv_exe, target_path)
                print(f"✅ uv installed to: {target_path}")
                UV_EXECUTABLE = str(target_path)
                return True
        
    except Exception as e:
        print(f"❌ Direct download failed: {e}")
    
    return False

def check_prerequisites():
    """Check if required tools are installed."""
    print("\n=== Checking Prerequisites ===")
    
    # Check for Git
    git_path = find_executable("git", [
        Path(os.environ.get('PROGRAMFILES', 'C:\\Program Files')) / "Git" / "bin",
        Path(os.environ.get('PROGRAMFILES(X86)', 'C:\\Program Files (x86)')) / "Git" / "bin",
    ])
    
    if not git_path:
        print("❌ Git is not installed.")
        print("\nGit is required for this setup. Would you like to:")
        print("1. Download Git installer (recommended)")
        print("2. Continue without Git (manual setup required)")
        
        choice = input("\nEnter your choice (1 or 2): ").strip()
        
        if choice == "1":
            print("\nOpening Git download page...")
            import webbrowser
            webbrowser.open("https://git-scm.com/download/win")
            print("\nPlease install Git and run this script again.")
            return False
        else:
            print("⚠️  Continuing without Git. You'll need to download the repository manually.")
    else:
        print(f"✅ Git is installed at: {git_path}")
    
    # Check for uv
    if not find_uv_executable():
        if not install_uv():
            print("\n❌ Failed to install uv package manager")
            print("Please install uv manually from https://docs.astral.sh/uv/")
            return False
    else:
        print(f"✅ uv is available: {UV_EXECUTABLE}")
    
    return True

def fix_claude_config():
    """Fix the Claude desktop configuration JSON file if it has syntax errors."""
    print("\n=== Checking Claude Configuration ===")
    
    # Path to Claude config file
    config_path = Path(os.environ['APPDATA']) / 'Claude' / 'claude_desktop_config.json'
    
    print(f"Checking Claude config at: {config_path}")
    
    if not config_path.exists():
        print("Claude configuration file not found. Will create a new one.")
        return create_minimal_claude_config()
    
    try:
        # Try to read and parse the existing config
        with open(config_path, 'r', encoding='utf-8') as f:
            content = f.read()
        
        # Test if JSON is valid
        json.loads(content)
        print("✅ Claude configuration file is valid")
        return True
        
    except json.JSONDecodeError as e:
        print(f"❌ JSON syntax error in Claude config: {e}")
        print(f"Error at line {e.lineno}, column {e.colno}")
        
        # Create backup before fixing
        backup_path = config_path.with_suffix('.json.backup')
        shutil.copy2(config_path, backup_path)
        print(f"✅ Created backup at: {backup_path}")
        
        # Attempt to fix common JSON issues
        return attempt_json_fix(config_path, content)
        
    except Exception as e:
        print(f"❌ Error reading Claude config: {e}")
        return False

def attempt_json_fix(config_path, content):
    """Attempt to automatically fix common JSON syntax errors."""
    print("Attempting to fix JSON syntax errors...")
    
    try:
        # Common fixes for JSON syntax errors
        lines = content.split('\n')
        fixed_lines = []
        
        for i, line in enumerate(lines):
            # Remove comments (not valid in JSON)
            if '//' in line:
                comment_pos = line.find('//')
                line = line[:comment_pos].rstrip()
            
            # Fix missing commas between objects/arrays
            stripped = line.strip()
            if i < len(lines) - 1:  # Not the last line
                next_line = lines[i + 1].strip()
                
                # Check if we need a comma
                needs_comma = (
                    (stripped.endswith('}') or stripped.endswith(']') or stripped.endswith('"')) and
                    (next_line.startswith('"') or next_line.startswith('{') or next_line.startswith('[')) and
                    not stripped.endswith(',')
                )
                
                if needs_comma:
                    # Add comma to the original line (preserving indentation)
                    line = line.rstrip() + ','
            
            # Remove trailing commas before closing braces/brackets
            if stripped.endswith(',}'):
                line = line.replace(',}', '}')
            elif stripped.endswith(',]'):
                line = line.replace(',]', ']')
            
            fixed_lines.append(line)
        
        fixed_content = '\n'.join(fixed_lines)
        
        # Try to parse the fixed content
        parsed = json.loads(fixed_content)
        
        # Pretty print and save
        formatted_json = json.dumps(parsed, indent=2)
        
        with open(config_path, 'w', encoding='utf-8') as f:
            f.write(formatted_json)
        
        print("✅ JSON file fixed and reformatted successfully!")
        return True
        
    except json.JSONDecodeError as e:
        print(f"❌ Auto-fix failed: {e}")
        print("Creating a fresh minimal configuration...")
        return create_minimal_claude_config()
    except Exception as e:
        print(f"❌ Error during fix attempt: {e}")
        return False

def create_minimal_claude_config():
    """Create a minimal valid Claude configuration."""
    config_path = Path(os.environ['APPDATA']) / 'Claude' / 'claude_desktop_config.json'
    
    minimal_config = {
        "mcpServers": {}
    }
    
    try:
        # Create directory if it doesn't exist
        config_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(config_path, 'w', encoding='utf-8') as f:
            json.dump(minimal_config, f, indent=2)
        
        print(f"✅ Created minimal Claude configuration at: {config_path}")
        return True
        
    except Exception as e:
        print(f"❌ Error creating minimal config: {e}")
        return False

def get_user_inputs():
    """Get Dremio connection details from user."""
    print("\n=== Dremio Connection Details ===")
    
    # Get server IP/hostname
    server_ip = input("Enter Dremio server IP address or hostname (e.g., localhost or 192.168.1.100): ").strip()
    if not server_ip:
        server_ip = "localhost"
    
    # Get port
    port = input("Enter Dremio port (default: 9047): ").strip()
    if not port:
        port = "9047"
    
    # Get protocol
    print("\nDoes your Dremio server use HTTPS? (Usually 'no' for local/development servers)")
    use_https = input("Use HTTPS? (y/N): ").strip().lower()
    protocol = "https" if use_https == 'y' else "http"
    
    # Get username
    username = input("\nEnter Dremio username: ").strip()
    if not username:
        print("❌ Username is required")
        sys.exit(1)
    
    # Get password
    import getpass
    password = getpass.getpass("Enter Dremio password: ")
    if not password:
        print("❌ Password is required")
        sys.exit(1)
    
    # Construct URI
    uri = f"{protocol}://{server_ip}:{port}"
    print(f"\nUsing Dremio server at: {uri}")
    
    return {
        "uri": uri,
        "username": username,
        "password": password
    }

def generate_pat(uri, username, password):
    """Generate Personal Access Token from Dremio."""
    print("\n=== Generating Personal Access Token ===")
    
    # Import requests here after ensuring it's installed
    import requests
    
    login_endpoint = f"{uri}/apiv2/login"
    
    try:
        # Disable SSL warnings for self-signed certificates
        import urllib3
        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
        
        # Make login request
        response = requests.post(
            login_endpoint,
            json={"userName": username, "password": password},
            headers={"Content-Type": "application/json"},
            verify=False,  # For self-signed certificates
            timeout=10  # Add timeout
        )
        
        if response.status_code == 200:
            token = response.json().get("token")
            if token:
                print("✅ PAT generated successfully")
                return token
            else:
                print("❌ Failed to extract token from response")
                return None
        else:
            print(f"❌ Login failed with status code: {response.status_code}")
            print(f"Response: {response.text}")
            return None
            
    except requests.exceptions.SSLError as e:
        print(f"❌ SSL Error: {str(e)}")
        print("\nThis usually means the server is using HTTP, not HTTPS.")
        print("Please run the script again and choose 'N' when asked about HTTPS.")
        return None
    except requests.exceptions.ConnectionError as e:
        print(f"❌ Connection Error: {str(e)}")
        print("\nPlease check:")
        print("1. The server IP address is correct")
        print("2. The port is correct (default is 9047)")
        print("3. The Dremio server is running")
        print("4. Your firewall allows the connection")
        return None
    except Exception as e:
        print(f"❌ Error generating PAT: {str(e)}")
        return None

def clone_mcp_repository(target_dir=None):
    """Clone the Dremio MCP Server repository."""
    print("\n=== Setting up Dremio MCP Server Repository ===")
    
    if target_dir is None:
        target_dir = Path.home() / "dremio-mcp"
    else:
        target_dir = Path(target_dir)
    
    # Check if directory already exists
    if target_dir.exists():
        response = input(f"Directory {target_dir} already exists. Remove and re-clone? (y/n): ")
        if response.lower() == 'y':
            shutil.rmtree(target_dir)
        else:
            print("Using existing directory")
            return str(target_dir)
    
    # Check if Git is available
    git_path = find_executable("git")
    if git_path:
        try:
            subprocess.run([
                git_path, "clone", "https://github.com/dremio/dremio-mcp", str(target_dir)
            ], check=True)
            print("✅ Repository cloned successfully")
            return str(target_dir)
        except subprocess.CalledProcessError as e:
            print(f"❌ Failed to clone repository: {str(e)}")
            return None
    else:
        # Manual download option
        print("\n⚠️  Git is not available. Would you like to:")
        print("1. Download the repository as ZIP (manual)")
        print("2. Cancel setup")
        
        choice = input("\nEnter your choice (1 or 2): ").strip()
        
        if choice == "1":
            print("\nOpening repository page...")
            import webbrowser
            webbrowser.open("https://github.com/dremio/dremio-mcp/archive/refs/heads/main.zip")
            print(f"\nPlease extract the downloaded ZIP to: {target_dir}")
            input("Press Enter after extracting the files...")
            
            # Check if files were extracted properly
            if target_dir.exists():
                # Look for main directory structure
                possible_dirs = [
                    target_dir,
                    target_dir / "dremio-mcp-main",
                    target_dir / "dremio-mcp"
                ]
                
                for dir_check in possible_dirs:
                    if (dir_check / "pyproject.toml").exists():
                        if dir_check != target_dir:
                            # Move contents to target directory
                            print(f"Moving files from {dir_check} to {target_dir}")
                            for item in dir_check.iterdir():
                                shutil.move(str(item), str(target_dir))
                            dir_check.rmdir()
                        print("✅ Repository files found and organized")
                        return str(target_dir)
                
                print("❌ Repository files not found in expected structure")
                return None
            else:
                print("❌ Repository files not found at expected location")
                return None
        else:
            return None

def setup_mcp_server(repo_dir, uri, pat):
    """Configure the Dremio MCP Server."""
    print("\n=== Configuring Dremio MCP Server ===")
    
    os.chdir(repo_dir)
    
    # Create dremioai configuration
    print("Creating dremioai configuration...")
    try:
        result = run_uv_command([
            "run", "dremio-mcp-server", "config", "create", "dremioai",
            "--uri", uri,
            "--pat", pat
        ], cwd=repo_dir)
        
        if result.returncode == 0:
            print("✅ Dremioai configuration created")
        else:
            print(f"❌ Failed to create dremioai configuration: {result.stderr}")
            return False
            
    except Exception as e:
        print(f"❌ Failed to create dremioai configuration: {str(e)}")
        return False
    
    # Create Claude configuration
    print("Creating Claude configuration...")
    try:
        result = run_uv_command([
            "run", "dremio-mcp-server", "config", "create", "claude"
        ], cwd=repo_dir)
        
        if result.returncode == 0:
            print("✅ Claude configuration created")
        else:
            print(f"❌ Failed to create Claude configuration.")
            print(f"Error output: {result.stderr}")
            
            # Try manual approach
            print("Attempting manual Claude configuration...")
            if create_manual_claude_mcp_config(repo_dir):
                print("✅ Manual Claude configuration successful")
            else:
                return False
                
    except Exception as e:
        print(f"❌ Failed to create Claude configuration: {str(e)}")
        print("Attempting manual Claude configuration...")
        if not create_manual_claude_mcp_config(repo_dir):
            return False
    
    # Verify configurations
    print("\n=== Verifying Configurations ===")
    try:
        # Verify dremioai config
        result = run_uv_command([
            "run", "dremio-mcp-server", "config", "list", "--type", "dremioai"
        ], cwd=repo_dir)
        print("Dremioai configuration:")
        print(result.stdout)
        
        # Verify Claude config
        result = run_uv_command([
            "run", "dremio-mcp-server", "config", "list", "--type", "claude"
        ], cwd=repo_dir)
        print("\nClaude configuration:")
        print(result.stdout)
        
    except Exception as e:
        print(f"❌ Failed to verify configurations: {str(e)}")
        # Continue anyway as configs might still work
    
    return True

def create_manual_claude_mcp_config(repo_dir):
    """Manually create Claude MCP configuration if automated method fails."""
    try:
        config_path = Path(os.environ['APPDATA']) / 'Claude' / 'claude_desktop_config.json'
        
        # Read existing config
        if config_path.exists():
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
        else:
            config = {}
        
        # Ensure mcpServers section exists
        if 'mcpServers' not in config:
            config['mcpServers'] = {}
        
        # Determine the command to use
        if UV_EXECUTABLE == "python -m uv":
            command = sys.executable
            args = ["-m", "uv", "run", "--directory", str(repo_dir), "dremio-mcp-server", "run"]
        else:
            command = UV_EXECUTABLE
            args = ["run", "--directory", str(repo_dir), "dremio-mcp-server", "run"]
        
        # Add Dremio MCP server configuration
        config['mcpServers']['Dremio'] = {
            "command": command,
            "args": args
        }
        
        # Write updated config
        with open(config_path, 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=2)
        
        print("✅ Manually added Dremio MCP configuration to Claude")
        return True
        
    except Exception as e:
        print(f"❌ Error creating manual Claude configuration: {e}")
        return False

def create_startup_script(repo_dir):
    """Create a batch file to easily start the MCP server."""
    print("\n=== Creating Startup Script ===")
    
    # Determine the command to use
    if UV_EXECUTABLE == "python -m uv":
        command = f'"{sys.executable}" -m uv'
    else:
        command = f'"{UV_EXECUTABLE}"'
    
    batch_content = f"""@echo off
cd /d "{repo_dir}"
echo Starting Dremio MCP Server...
{command} run dremio-mcp-server run
pause
"""
    
    # Save to desktop
    desktop = Path.home() / "Desktop"
    batch_file = desktop / "Start_Dremio_MCP_Server.bat"
    
    try:
        with open(batch_file, 'w') as f:
            f.write(batch_content)
        print(f"✅ Startup script created at: {batch_file}")
        return str(batch_file)
    except Exception as e:
        print(f"❌ Failed to create startup script: {str(e)}")
        return None

def main():
    """Main execution function."""
    print("=== Dremio MCP Server Setup for Windows ===")
    print("This script will set up the Dremio MCP Server on your Windows system.")
    print("Version: 3.0 (Fully Dynamic Multi-User Support)")
    print(f"Current user: {os.environ.get('USERNAME', 'Unknown')}")
    
    # Check if running as admin
    if not is_admin():
        print("\n⚠️  Running without administrator privileges")
        print("   Some features may be limited")
        print("   For best results, run this script as Administrator")
        response = input("\nContinue anyway? (y/n): ").strip().lower()
        if response != 'y':
            sys.exit(0)
    
    # Check Python installation
    if not check_python_installation():
        print("\n❌ Python installation check failed.")
        input("Press Enter to exit...")
        sys.exit(1)
    
    # Check pip installation
    if not check_pip_installation():
        print("\n❌ pip installation check failed.")
        input("Press Enter to exit...")
        sys.exit(1)
    
    # Ensure required Python packages
    if not ensure_dependencies():
        print("\n❌ Failed to install required Python packages.")
        input("Press Enter to exit...")
        sys.exit(1)
    
    # Check and fix Claude configuration
    if not fix_claude_config():
        print("\n❌ Failed to prepare Claude configuration. Please fix manually.")
        input("Press Enter to exit...")
        sys.exit(1)
    
    # Check other prerequisites (Git, uv)
    if not check_prerequisites():
        print("\n❌ Prerequisites check failed. Please install missing components.")
        input("Press Enter to exit...")
        sys.exit(1)
    
    # Get user inputs
    config = get_user_inputs()
    
    # Generate PAT
    pat = generate_pat(config["uri"], config["username"], config["password"])
    if not pat:
        print("\n❌ Failed to generate PAT. Please check your credentials and server connection.")
        input("Press Enter to exit...")
        sys.exit(1)
    
    # Clone repository
    repo_dir = clone_mcp_repository()
    if not repo_dir:
        print("\n❌ Failed to set up repository.")
        input("Press Enter to exit...")
        sys.exit(1)
    
    # Setup MCP server
    if not setup_mcp_server(repo_dir, config["uri"], pat):
        print("\n❌ Failed to configure MCP server.")
        input("Press Enter to exit...")
        sys.exit(1)
    
    # Create startup script
    startup_script = create_startup_script(repo_dir)
    
    # Final instructions
    print("\n=== Setup Complete! ===")
    print("\n✅ Dremio MCP Server has been successfully configured!")
    print("\nNext steps:")
    print("1. Restart Claude Desktop (close and reopen it)")
    print("2. The Dremio MCP Server should automatically appear in Claude")
    print("3. Start a new chat and ask about available tables in Dremio")
    
    if startup_script:
        print(f"\nYou can also manually start the MCP server by running: {startup_script}")
    
    print("\nFor more information, visit: https://docs.dremio.com/current/mcp-server/")
    
    # Summary of installation
    print("\n=== Installation Summary ===")
    print(f"Python: {sys.executable}")
    print(f"uv: {UV_EXECUTABLE}")
    print(f"Repository: {repo_dir}")
    print(f"Claude config: {Path(os.environ['APPDATA']) / 'Claude' / 'claude_desktop_config.json'}")
    
    input("\nPress Enter to exit...")

if __name__ == "__main__":
    # Ensure we're running on Windows
    if platform.system() != "Windows":
        print("This script is designed for Windows systems.")
        sys.exit(1)
    
    try:
        main()
    except KeyboardInterrupt:
        print("\n\nSetup cancelled by user.")
        sys.exit(0)
    except Exception as e:
        print(f"\n❌ Unexpected error: {str(e)}")
        print("\n=== Debug Information ===")
        traceback.print_exc()
        print(f"\nPython: {sys.executable}")
        print(f"Platform: {platform.platform()}")
        print(f"Working directory: {os.getcwd()}")
        input("\nPress Enter to exit...")
        sys.exit(1)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions