diff --git a/.env.example b/.env.example index 44ef9b936..4e9bf7b02 100644 --- a/.env.example +++ b/.env.example @@ -49,5 +49,11 @@ CYPHER_ENDPOINT=http://localhost:11434/v1 MEMGRAPH_HOST=localhost MEMGRAPH_PORT=7687 MEMGRAPH_HTTP_PORT=7444 +# Memgraph authentication credentials +# Leave MEMGRAPH_USERNAME empty (or omit it) if your Memgraph instance doesn't require authentication +# If authentication is enabled, provide both username and password +# Common defaults: username=neo4j, password=password (or your custom credentials) +MEMGRAPH_USERNAME= +MEMGRAPH_PASSWORD= LAB_PORT=3000 TARGET_REPO_PATH=. diff --git a/codebase_rag/config.py b/codebase_rag/config.py index eccf7bab5..ee34df4b3 100644 --- a/codebase_rag/config.py +++ b/codebase_rag/config.py @@ -42,6 +42,8 @@ class AppConfig(BaseSettings): MEMGRAPH_HOST: str = "localhost" MEMGRAPH_PORT: int = 7687 MEMGRAPH_HTTP_PORT: int = 7444 + MEMGRAPH_USERNAME: str | None = None + MEMGRAPH_PASSWORD: str | None = None LAB_PORT: int = 3000 MEMGRAPH_BATCH_SIZE: int = 1000 diff --git a/codebase_rag/main.py b/codebase_rag/main.py index 0586a9f3c..a70eea106 100644 --- a/codebase_rag/main.py +++ b/codebase_rag/main.py @@ -785,6 +785,8 @@ async def main_async(repo_path: str, batch_size: int) -> None: host=settings.MEMGRAPH_HOST, port=settings.MEMGRAPH_PORT, batch_size=batch_size, + username=settings.MEMGRAPH_USERNAME, + password=settings.MEMGRAPH_PASSWORD, ) as ingestor: console.print("[bold green]Successfully connected to Memgraph.[/bold green]") console.print( @@ -870,6 +872,8 @@ def start( host=settings.MEMGRAPH_HOST, port=settings.MEMGRAPH_PORT, batch_size=effective_batch_size, + username=settings.MEMGRAPH_USERNAME, + password=settings.MEMGRAPH_PASSWORD, ) as ingestor: if clean: console.print("[bold yellow]Cleaning database...[/bold yellow]") @@ -930,6 +934,8 @@ def export( host=settings.MEMGRAPH_HOST, port=settings.MEMGRAPH_PORT, batch_size=effective_batch_size, + username=settings.MEMGRAPH_USERNAME, + password=settings.MEMGRAPH_PASSWORD, ) as ingestor: console.print("[bold cyan]Exporting graph data...[/bold cyan]") if not _export_graph_to_file(ingestor, output): @@ -970,6 +976,8 @@ async def main_optimize_async( host=settings.MEMGRAPH_HOST, port=settings.MEMGRAPH_PORT, batch_size=effective_batch_size, + username=settings.MEMGRAPH_USERNAME, + password=settings.MEMGRAPH_PASSWORD, ) as ingestor: console.print("[bold green]Successfully connected to Memgraph.[/bold green]") diff --git a/codebase_rag/mcp/server.py b/codebase_rag/mcp/server.py index 9e05e36e0..0004318e1 100644 --- a/codebase_rag/mcp/server.py +++ b/codebase_rag/mcp/server.py @@ -103,6 +103,8 @@ def create_server() -> tuple[Server, MemgraphIngestor]: host=settings.MEMGRAPH_HOST, port=settings.MEMGRAPH_PORT, batch_size=settings.MEMGRAPH_BATCH_SIZE, + username=settings.MEMGRAPH_USERNAME, + password=settings.MEMGRAPH_PASSWORD, ) # CypherGenerator gets config from settings automatically diff --git a/codebase_rag/services/graph_service.py b/codebase_rag/services/graph_service.py index ad4c69e76..146174cb7 100644 --- a/codebase_rag/services/graph_service.py +++ b/codebase_rag/services/graph_service.py @@ -9,9 +9,24 @@ class MemgraphIngestor: """Handles all communication and query execution with the Memgraph database.""" - def __init__(self, host: str, port: int, batch_size: int = 1000): + def __init__( + self, + host: str, + port: int, + batch_size: int = 1000, + username: str | None = None, + password: str | None = None, + ): self._host = host self._port = port + self._username = username + self._password = password + # Validate authentication: both username and password must be provided together + if (self._username is None) != (self._password is None): + raise ValueError( + "Both username and password are required for authentication. " + "Either provide both or neither." + ) if batch_size < 1: raise ValueError("batch_size must be a positive integer") self.batch_size = batch_size @@ -32,7 +47,19 @@ def __init__(self, host: str, port: int, batch_size: int = 1000): def __enter__(self) -> "MemgraphIngestor": logger.info(f"Connecting to Memgraph at {self._host}:{self._port}...") - self.conn = mgclient.connect(host=self._host, port=self._port) + # Build connection parameters - only include username/password if provided + connect_params = { + "host": self._host, + "port": self._port, + } + + # Only add authentication parameters if both username and password are provided + # This allows connection to Memgraph instances without authentication + if self._username is not None: + connect_params["username"] = self._username + connect_params["password"] = self._password + + self.conn = mgclient.connect(**connect_params) self.conn.autocommit = True logger.info("Successfully connected to Memgraph.") return self diff --git a/realtime_updater.py b/realtime_updater.py index 9267b1819..53bc290d5 100644 --- a/realtime_updater.py +++ b/realtime_updater.py @@ -94,6 +94,8 @@ def start_watcher( host=host, port=port, batch_size=effective_batch_size, + username=settings.MEMGRAPH_USERNAME, + password=settings.MEMGRAPH_PASSWORD, ) as ingestor: updater = GraphUpdater(ingestor, repo_path_obj, parsers, queries)