Skip to main content
Glama

CodeGraphContext

setup_wizard.py23.3 kB
from InquirerPy import prompt from rich.console import Console import subprocess import platform import os from pathlib import Path import time import json import sys import shutil console = Console() def _generate_mcp_json(creds): """Generates and prints the MCP JSON configuration.""" cgc_path = shutil.which("cgc") or sys.executable if "python" in Path(cgc_path).name: # fallback to running as module if no cgc binary is found command = cgc_path args = ["-m", "cgc", "start"] else: command = cgc_path args = ["start"] mcp_config = { "mcpServers": { "CodeGraphContext": { "command": command, "args": args, "env": { "NEO4J_URI": creds.get("uri", ""), "NEO4J_USERNAME": creds.get("username", "neo4j"), "NEO4J_PASSWORD": creds.get("password", "") }, "tools": { "alwaysAllow": [ "list_imports", "add_code_to_graph", "add_package_to_graph", "check_job_status", "list_jobs", "find_code", "analyze_code_relationships", "watch_directory", "find_dead_code", "execute_cypher_query", "calculate_cyclomatic_complexity", "find_most_complex_functions", "list_indexed_repositories", "delete_repository" ], "disabled": False }, "disabled": False, "alwaysAllow": [] } } } console.print("\n[bold green]Configuration successful![/bold green]") console.print("Copy the following JSON and add it to your MCP server configuration file:") console.print(json.dumps(mcp_config, indent=2)) # Also save to a file for convenience mcp_file = Path.cwd() / "mcp.json" with open(mcp_file, "w") as f: json.dump(mcp_config, f, indent=2) console.print(f"\n[cyan]For your convenience, the configuration has also been saved to: {mcp_file}[/cyan]") # Also save to a .env file for convenience env_file = Path.home() / ".codegraphcontext" / ".env" env_file.parent.mkdir(parents=True, exist_ok=True) with open(env_file, "w") as f: f.write(f"NEO4J_URI={creds.get('uri', '')}\n") f.write(f"NEO4J_USERNAME={creds.get('username', 'neo4j')}\n") f.write(f"NEO4J_PASSWORD={creds.get('password', '')}\n") console.print(f"[cyan]Neo4j credentials also saved to: {env_file}[/cyan]") _configure_ide(mcp_config) def _configure_ide(mcp_config): """Asks user for their IDE and configures it automatically.""" questions = [ { "type": "confirm", "message": "Automatically configure your IDE/CLI (VS Code, Cursor, Claude, Gemini)?", "name": "configure_ide", "default": True, } ] result = prompt(questions) if not result or not result.get("configure_ide"): console.print("\n[cyan]Skipping automatic IDE configuration. You can add the MCP server manually.[/cyan]") return ide_questions = [ { "type": "list", "message": "Choose your IDE/CLI to configure:", "choices": ["VS Code", "Cursor", "Claude code", "Gemini CLI", "None of the above"], "name": "ide_choice", } ] ide_result = prompt(ide_questions) ide_choice = ide_result.get("ide_choice") if not ide_choice or ide_choice == "None of the above": console.print("\n[cyan]You can add the MCP server manually to your IDE/CLI.[/cyan]") return if ide_choice in ["VS Code", "Cursor", "Claude code", "Gemini CLI"]: console.print(f"\n[bold cyan]Configuring for {ide_choice}...[/bold cyan]") config_paths = { "VS Code": [ Path.home() / ".config" / "Code" / "User" / "settings.json", Path.home() / "Library" / "Application Support" / "Code" / "User" / "settings.json", Path.home() / "AppData" / "Roaming" / "Code" / "User" / "settings.json" ], "Cursor": [ Path.home() / ".cursor" / "settings.json", Path.home() / ".config" / "cursor" / "settings.json", Path.home() / "Library" / "Application Support" / "cursor" / "settings.json", Path.home() / "AppData" / "Roaming" / "cursor" / "settings.json", Path.home() / ".config" / "Cursor" / "User" / "settings.json", ], "Claude code": [ Path.home() / ".claude.json" ], "Gemini CLI": [ Path.home() / ".gemini" / "settings.json" ] } target_path = None paths_to_check = config_paths.get(ide_choice, []) for path in paths_to_check: if path.exists(): target_path = path break if not target_path: # If file doesn't exist, check if parent directory exists for path in paths_to_check: if path.parent.exists(): target_path = path break if not target_path: console.print(f"[yellow]Could not automatically find or create the configuration directory for {ide_choice}.[/yellow]") console.print("Please add the MCP configuration manually from the `mcp.json` file generated above.") return console.print(f"Using configuration file at: {target_path}") try: with open(target_path, "r") as f: try: settings = json.load(f) except json.JSONDecodeError: settings = {} except FileNotFoundError: settings = {} if not isinstance(settings, dict): console.print(f"[red]Error: Configuration file at {target_path} is not a valid JSON object.[/red]") return if "mcpServers" not in settings: settings["mcpServers"] = {} settings["mcpServers"].update(mcp_config["mcpServers"]) try: with open(target_path, "w") as f: json.dump(settings, f, indent=2) console.print(f"[green]Successfully updated {ide_choice} configuration.[/green]") except Exception as e: console.print(f"[red]Failed to write to configuration file: {e}[/red]") def get_project_root() -> Path: """Always return the directory where the user runs `cgc` (CWD).""" return Path.cwd() def run_command(command, console, shell=False, check=True, input_text=None): """ Runs a command, captures its output, and handles execution. Returns the completed process object on success, None on failure. """ cmd_str = command if isinstance(command, str) else ' '.join(command) console.print(f"[cyan]$ {cmd_str}[/cyan]") try: process = subprocess.run( command, shell=shell, check=check, capture_output=True, # Always capture to control what gets displayed text=True, timeout=300, input=input_text ) return process except subprocess.CalledProcessError as e: console.print(f"[bold red]Error executing command:[/bold red] {cmd_str}") if e.stdout: console.print(f"[red]STDOUT: {e.stdout}[/red]") if e.stderr: console.print(f"[red]STDERR: {e.stderr}[/red]") return None except subprocess.TimeoutExpired: console.print(f"[bold red]Command timed out:[/bold red] {cmd_str}") return None def run_setup_wizard(): """Guides the user through setting up CodeGraphContext.""" console.print("[bold cyan]Welcome to the CodeGraphContext Setup Wizard![/bold cyan]") questions = [ { "type": "list", "message": "Where is your Neo4j database located? We can help you get one, if you don't have.", "choices": [ "Local (Recommended: I'll help you run it on this machine)", "Hosted (Connect to a remote database like AuraDB)", ], "name": "db_location", } ] result = prompt(questions) db_location = result.get("db_location") if db_location and "Hosted" in db_location: setup_hosted_db() elif db_location: setup_local_db() def find_latest_neo4j_creds_file(): """Finds the latest Neo4j credentials file in the Downloads folder.""" downloads_path = Path.home() / "Downloads" if not downloads_path.exists(): return None cred_files = list(downloads_path.glob("Neo4j*.txt")) if not cred_files: return None latest_file = max(cred_files, key=lambda f: f.stat().st_mtime) return latest_file def setup_hosted_db(): """Guides user to configure a remote Neo4j instance.""" console.print("\nTo connect to a hosted Neo4j database, you'll need your connection credentials.") console.print("[yellow]Warning: You are configuring to connect to a remote/hosted Neo4j database. Ensure your credentials are secure.[/yellow]") console.print("If you don't have a hosted database, you can create a free one at [bold blue]https://neo4j.com/product/auradb/[/bold blue] (click 'Start free').") questions = [ { "type": "list", "message": "How would you like to add your Neo4j credentials?", "choices": ["Add credentials from file", "Add credentials manually"], "name": "cred_method", } ] result = prompt(questions) cred_method = result.get("cred_method") creds = {} if cred_method and "file" in cred_method: latest_file = find_latest_neo4j_creds_file() file_to_parse = None if latest_file: confirm_questions = [ { "type": "confirm", "message": f"Found a credentials file: {latest_file}. Use this file?", "name": "use_latest", "default": True, } ] if prompt(confirm_questions).get("use_latest"): file_to_parse = latest_file if not file_to_parse: path_questions = [ {"type": "input", "message": "Please enter the path to your credentials file:", "name": "cred_file_path"} ] file_path_str = prompt(path_questions).get("cred_file_path", "") file_path = Path(file_path_str.strip()) if file_path.exists() and file_path.is_file(): file_to_parse = file_path else: console.print("[red]❌ The specified file path does not exist or is not a file.[/red]") return if file_to_parse: try: with open(file_to_parse, "r") as f: for line in f: if "=" in line: key, value = line.strip().split("=", 1) if key == "NEO4J_URI": creds["uri"] = value elif key == "NEO4J_USERNAME": creds["username"] = value elif key == "NEO4J_PASSWORD": creds["password"] = value except Exception as e: console.print(f"[red]❌ Failed to parse credentials file: {e}[/red]") return elif cred_method: # Manual entry console.print("Please enter your remote Neo4j connection details.") questions = [ {"type": "input", "message": "URI (e.g., neo4j+s://xxxx.databases.neo4j.io):", "name": "uri"}, {"type": "input", "message": "Username:", "name": "username", "default": "neo4j"}, {"type": "password", "message": "Password:", "name": "password"}, ] manual_creds = prompt(questions) if not manual_creds: return # User cancelled creds = manual_creds if creds.get("uri") and creds.get("password"): _generate_mcp_json(creds) else: console.print("[red]❌ Incomplete credentials. Please try again.[/red]") def setup_local_db(): """Guides user to set up a local Neo4j instance.""" questions = [ { "type": "list", "message": "How would you like to run Neo4j locally?", "choices": ["Docker (Easiest)", "Local Binary (Advanced)"], "name": "local_method", } ] result = prompt(questions) local_method = result.get("local_method") if local_method and "Docker" in local_method: setup_docker() elif local_method: setup_local_binary() def setup_docker(): """Creates Docker files and runs docker-compose for Neo4j.""" console.print("\n[bold cyan]Setting up Neo4j with Docker...[/bold cyan]") # Prompt for password first console.print("Please set a secure password for your Neo4j database:") password_questions = [ {"type": "password", "message": "Enter Neo4j password:", "name": "password"}, {"type": "password", "message": "Confirm password:", "name": "password_confirm"}, ] while True: passwords = prompt(password_questions) if not passwords: return # User cancelled password = passwords.get("password", "") if password and password == passwords.get("password_confirm"): break console.print("[red]Passwords do not match or are empty. Please try again.[/red]") # Create data directories neo4j_dir = Path.cwd() / "neo4j_data" for subdir in ["data", "logs", "conf", "plugins"]: (neo4j_dir / subdir).mkdir(parents=True, exist_ok=True) # Fixed docker-compose.yml content docker_compose_content = f""" services: neo4j: image: neo4j:5.21 container_name: neo4j-cgc restart: unless-stopped ports: - "7474:7474" - "7687:7687" environment: - NEO4J_AUTH=neo4j/{password} - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes volumes: - neo4j_data:/data - neo4j_logs:/logs volumes: neo4j_data: neo4j_logs: """ # Write docker-compose.yml compose_file = Path.cwd() / "docker-compose.yml" with open(compose_file, "w") as f: f.write(docker_compose_content) console.print("[green]✅ docker-compose.yml created with secure password.[/green]") # Check if Docker is running docker_check = run_command(["docker", "--version"], console, check=False) if not docker_check: console.print("[red]❌ Docker is not installed or not running. Please install Docker first.[/red]") return # Check if docker-compose is available compose_check = run_command(["docker", "compose", "version"], console, check=False) if not compose_check: console.print("[red]❌ Docker Compose is not available. Please install Docker Compose.[/red]") return confirm_q = [{"type": "confirm", "message": "Ready to launch Neo4j in Docker?", "name": "proceed", "default": True}] if not prompt(confirm_q).get("proceed"): return try: # Pull the image first console.print("[cyan]Pulling Neo4j Docker image...[/cyan]") pull_process = run_command(["docker", "pull", "neo4j:5.21"], console, check=True) if not pull_process: console.print("[yellow]⚠️ Could not pull image, but continuing anyway...[/yellow]") # Start containers console.print("[cyan]Starting Neo4j container...[/cyan]") docker_process = run_command(["docker", "compose", "up", "-d"], console, check=True) if docker_process: console.print("[bold green]🚀 Neo4j Docker container started successfully![/bold green]") # Wait for Neo4j to be ready console.print("[cyan]Waiting for Neo4j to be ready (this may take 30-60 seconds)...[/cyan]") # Try to connect for up to 2 minutes max_attempts = 24 # 24 * 5 seconds = 2 minutes for attempt in range(max_attempts): time.sleep(5) # Check if container is still running status_check = run_command(["docker", "compose", "ps", "-q", "neo4j"], console, check=False) if not status_check or not status_check.stdout.strip(): console.print("[red]❌ Neo4j container stopped unexpectedly. Check logs with: docker compose logs neo4j[/red]") return # Try to connect health_check = run_command([ "docker", "exec", "neo4j-cgc", "cypher-shell", "-u", "neo4j", "-p", password, "RETURN 'Connection successful' as status" ], console, check=False) if health_check and health_check.returncode == 0: console.print("[bold green]✅ Neo4j is ready and accepting connections![/bold green]") break if attempt < max_attempts - 1: console.print(f"[yellow]Still waiting... (attempt {attempt + 1}/{max_attempts})[/yellow]") else: console.print("[red]❌ Neo4j did not become ready within 2 minutes. Check logs with: docker compose logs neo4j[/red]") return # Generate MCP configuration creds = { "uri": "neo4j://localhost:7687", # Use neo4j:// protocol for Neo4j 5.x "username": "neo4j", "password": password } _generate_mcp_json(creds) console.print("\n[bold green]🎉 Setup complete![/bold green]") console.print("Neo4j is running at:") console.print(" • Web interface: http://localhost:7474") console.print(" • Bolt connection: neo4j://localhost:7687") console.print("\n[cyan]Useful commands:[/cyan]") console.print(" • Stop: docker compose down") console.print(" • Restart: docker compose restart") console.print(" • View logs: docker compose logs neo4j") except Exception as e: console.print(f"[bold red]❌ Failed to start Neo4j Docker container:[/bold red] {e}") console.print("[cyan]Try checking the logs with: docker compose logs neo4j[/cyan]") def setup_local_binary(): """Automates the installation and configuration of Neo4j on Ubuntu/Debian.""" os_name = platform.system() console.print(f"Detected Operating System: [bold yellow]{os_name}[/bold yellow]") if os_name != "Linux" or not os.path.exists("/etc/debian_version"): console.print("[yellow]Automated installer is designed for Debian-based systems (like Ubuntu).[/yellow]") console.print(f"For other systems, please follow the manual installation guide: [bold blue]https://neo4j.com/docs/operations-manual/current/installation/[/bold blue]") return console.print("[bold]Starting automated Neo4j installation for Ubuntu/Debian.[/bold]") console.print("[yellow]This will run several commands with 'sudo'. You will be prompted for your password.[/yellow]") confirm_q = [{"type": "confirm", "message": "Do you want to proceed?", "name": "proceed", "default": True}] if not prompt(confirm_q).get("proceed"): return NEO4J_VERSION = "1:5.21.0" install_commands = [ ("Creating keyring directory", ["sudo", "mkdir", "-p", "/etc/apt/keyrings"]), ("Adding Neo4j GPG key", "wget -qO- https://debian.neo4j.com/neotechnology.gpg.key | sudo gpg --dearmor --yes -o /etc/apt/keyrings/neotechnology.gpg", True), ("Adding Neo4j repository", "echo 'deb [signed-by=/etc/apt/keyrings/neotechnology.gpg] https://debian.neo4j.com stable 5' | sudo tee /etc/apt/sources.list.d/neo4j.list > /dev/null", True), ("Updating apt sources", ["sudo", "apt-get", "-qq", "update"]), (f"Installing Neo4j ({NEO4J_VERSION}) and Cypher Shell", ["sudo", "apt-get", "install", "-qq", "-y", f"neo4j={NEO4J_VERSION}", "cypher-shell"]) ] for desc, cmd, use_shell in [(c[0], c[1], c[2] if len(c) > 2 else False) for c in install_commands]: console.print(f"\n[bold]Step: {desc}...[/bold]") if not run_command(cmd, console, shell=use_shell): console.print(f"[bold red]Failed on step: {desc}. Aborting installation.[/bold]") return console.print("\n[bold green]Neo4j installed successfully![/bold green]") console.print("\n[bold]Please set the initial password for the 'neo4j' user.""") new_password = "" while True: questions = [ {"type": "password", "message": "Enter a new password for Neo4j:", "name": "password"}, {"type": "password", "message": "Confirm the new password:", "name": "password_confirm"}, ] passwords = prompt(questions) if not passwords: return # User cancelled new_password = passwords.get("password") if new_password and new_password == passwords.get("password_confirm"): break console.print("[red]Passwords do not match or are empty. Please try again.[/red]") console.print("\n[bold]Stopping Neo4j to set the password...""") if not run_command(["sudo", "systemctl", "stop", "neo4j"], console): console.print("[bold red]Could not stop Neo4j service. Aborting.[/bold red]") return console.print("\n[bold]Setting initial password using neo4j-admin...""") pw_command = ["sudo", "-u", "neo4j", "neo4j-admin", "dbms", "set-initial-password", new_password] if not run_command(pw_command, console, check=True): console.print("[bold red]Failed to set the initial password. Please check the logs.[/bold red]") run_command(["sudo", "systemctl", "start", "neo4j"], console) return console.print("\n[bold]Starting Neo4j service...""") if not run_command(["sudo", "systemctl", "start", "neo4j"], console): console.print("[bold red]Failed to start Neo4j service after setting password.[/bold red]") return console.print("\n[bold]Enabling Neo4j service to start on boot...""") if not run_command(["sudo", "systemctl", "enable", "neo4j"], console): console.print("[bold yellow]Could not enable Neo4j service. You may need to start it manually after reboot.[/bold yellow]") console.print("[bold green]Password set and service started.[/bold green]") console.print("\n[yellow]Waiting 10 seconds for the database to become available...""") time.sleep(10) creds = { "uri": "neo4j://localhost:7687", "username": "neo4j", "password": new_password } _generate_mcp_json(creds) console.print("\n[bold green]All done! Your local Neo4j instance is ready to use.[/bold green]")

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/Shashankss1205/CodeGraphContext'

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