diff --git a/mcp_server/config/config-docker-kuzu.yaml b/mcp_server/config/config-docker-kuzu.yaml deleted file mode 100644 index 68e27478..00000000 --- a/mcp_server/config/config-docker-kuzu.yaml +++ /dev/null @@ -1,101 +0,0 @@ -# Graphiti MCP Server Configuration for Docker with KuzuDB -# This configuration is optimized for running with docker-compose-kuzu.yml -# It uses persistent KuzuDB storage at /data/graphiti.kuzu - -server: - transport: "http" # HTTP transport (SSE is deprecated) - host: "0.0.0.0" - port: 8000 - -llm: - provider: "openai" # Options: openai, azure_openai, anthropic, gemini, groq - model: "gpt-5-mini" - max_tokens: 4096 - - providers: - openai: - api_key: ${OPENAI_API_KEY} - api_url: ${OPENAI_API_URL:https://api.openai.com/v1} - organization_id: ${OPENAI_ORGANIZATION_ID:} - - azure_openai: - api_key: ${AZURE_OPENAI_API_KEY} - api_url: ${AZURE_OPENAI_ENDPOINT} - api_version: ${AZURE_OPENAI_API_VERSION:2024-10-21} - deployment_name: ${AZURE_OPENAI_DEPLOYMENT} - use_azure_ad: ${USE_AZURE_AD:false} - - anthropic: - api_key: ${ANTHROPIC_API_KEY} - api_url: ${ANTHROPIC_API_URL:https://api.anthropic.com} - max_retries: 3 - - gemini: - api_key: ${GOOGLE_API_KEY} - project_id: ${GOOGLE_PROJECT_ID:} - location: ${GOOGLE_LOCATION:us-central1} - - groq: - api_key: ${GROQ_API_KEY} - api_url: ${GROQ_API_URL:https://api.groq.com/openai/v1} - -embedder: - provider: "openai" # Options: openai, azure_openai, gemini, voyage - model: "text-embedding-3-small" - dimensions: 1536 - - providers: - openai: - api_key: ${OPENAI_API_KEY} - api_url: ${OPENAI_API_URL:https://api.openai.com/v1} - organization_id: ${OPENAI_ORGANIZATION_ID:} - - azure_openai: - api_key: ${AZURE_OPENAI_API_KEY} - api_url: ${AZURE_OPENAI_EMBEDDINGS_ENDPOINT} - api_version: ${AZURE_OPENAI_API_VERSION:2024-10-21} - deployment_name: ${AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT} - use_azure_ad: ${USE_AZURE_AD:false} - - gemini: - api_key: ${GOOGLE_API_KEY} - project_id: ${GOOGLE_PROJECT_ID:} - location: ${GOOGLE_LOCATION:us-central1} - - voyage: - api_key: ${VOYAGE_API_KEY} - api_url: ${VOYAGE_API_URL:https://api.voyageai.com/v1} - model: "voyage-3" - -database: - provider: "kuzu" # Using KuzuDB for this configuration - - providers: - kuzu: - # Use environment variable if set, otherwise use persistent storage at /data - db: ${KUZU_DB:/data/graphiti.kuzu} - max_concurrent_queries: ${KUZU_MAX_CONCURRENT_QUERIES:10} - -graphiti: - group_id: ${GRAPHITI_GROUP_ID:main} - episode_id_prefix: ${EPISODE_ID_PREFIX:} - user_id: ${USER_ID:mcp_user} - entity_types: - - name: "Preference" - description: "User preferences, choices, opinions, or selections (PRIORITIZE over most other types except User/Assistant)" - - name: "Requirement" - description: "Specific needs, features, or functionality that must be fulfilled" - - name: "Procedure" - description: "Standard operating procedures and sequential instructions" - - name: "Location" - description: "Physical or virtual places where activities occur" - - name: "Event" - description: "Time-bound activities, occurrences, or experiences" - - name: "Organization" - description: "Companies, institutions, groups, or formal entities" - - name: "Document" - description: "Information content in various forms (books, articles, reports, etc.)" - - name: "Topic" - description: "Subject of conversation, interest, or knowledge domain (use as last resort)" - - name: "Object" - description: "Physical items, tools, devices, or possessions (use as last resort)" \ No newline at end of file diff --git a/mcp_server/docker/README.md b/mcp_server/docker/README.md index f27c2d68..dae26966 100644 --- a/mcp_server/docker/README.md +++ b/mcp_server/docker/README.md @@ -1,18 +1,15 @@ # Docker Deployment for Graphiti MCP Server -This directory contains Docker Compose configurations for running the Graphiti MCP server with different graph database backends: KuzuDB, Neo4j, and FalkorDB. +This directory contains Docker Compose configurations for running the Graphiti MCP server with graph database backends: FalkorDB (combined image) and Neo4j. ## Quick Start ```bash -# Default configuration (KuzuDB) +# Default configuration (FalkorDB combined image) docker-compose up -# Neo4j +# Neo4j (separate containers) docker-compose -f docker-compose-neo4j.yml up - -# FalkorDB -docker-compose -f docker-compose-falkordb.yml up ``` ## Environment Variables @@ -32,56 +29,55 @@ SEMAPHORE_LIMIT=10 ## Database Configurations -### KuzuDB +### FalkorDB (Combined Image) **File:** `docker-compose.yml` (default) -KuzuDB is an embedded graph database that runs within the application container. +The default configuration uses a combined Docker image that bundles both FalkorDB and the MCP server together for simplified deployment. #### Configuration ```bash # Environment variables -KUZU_DB=/data/graphiti.kuzu # Database file path (default: /data/graphiti.kuzu) -KUZU_MAX_CONCURRENT_QUERIES=10 # Maximum concurrent queries (default: 10) +FALKORDB_URI=redis://localhost:6379 # Connection URI (services run in same container) +FALKORDB_PASSWORD= # Password (default: empty) +FALKORDB_DATABASE=default_db # Database name (default: default_db) ``` -#### Storage Options +#### Accessing Services -**Persistent Storage (default):** -Data is stored in the `kuzu_data` Docker volume at `/data/graphiti.kuzu`. - -**In-Memory Mode:** -```bash -KUZU_DB=:memory: -``` -Note: Data will be lost when the container stops. +- **FalkorDB (Redis):** redis://localhost:6379 +- **FalkorDB Web UI:** http://localhost:3000 +- **MCP Server:** http://localhost:8000 #### Data Management **Backup:** ```bash -docker run --rm -v docker_kuzu_data:/data -v $(pwd):/backup alpine \ - tar czf /backup/kuzu-backup.tar.gz -C /data . +docker run --rm -v mcp_server_falkordb_data:/var/lib/falkordb/data -v $(pwd):/backup alpine \ + tar czf /backup/falkordb-backup.tar.gz -C /var/lib/falkordb/data . ``` **Restore:** ```bash -docker run --rm -v docker_kuzu_data:/data -v $(pwd):/backup alpine \ - tar xzf /backup/kuzu-backup.tar.gz -C /data +docker run --rm -v mcp_server_falkordb_data:/var/lib/falkordb/data -v $(pwd):/backup alpine \ + tar xzf /backup/falkordb-backup.tar.gz -C /var/lib/falkordb/data ``` **Clear Data:** ```bash docker-compose down -docker volume rm docker_kuzu_data -docker-compose up # Creates fresh volume +docker volume rm mcp_server_falkordb_data +docker-compose up ``` #### Gotchas -- KuzuDB data is stored in a single file/directory -- The database file can grow large with extensive data -- In-memory mode provides faster performance but no persistence +- Both FalkorDB and MCP server run in the same container +- FalkorDB uses Redis persistence mechanisms (AOF/RDB) +- Default configuration has no password - add one for production +- Health check only monitors FalkorDB; MCP server startup visible in logs + +See [README-falkordb-combined.md](README-falkordb-combined.md) for detailed information about the combined image. ### Neo4j @@ -142,66 +138,22 @@ docker-compose -f docker-compose-neo4j.yml up - Page cache is set to 512MB - Enterprise features like parallel runtime require a license -### FalkorDB - -**File:** `docker-compose-falkordb.yml` - -FalkorDB is a Redis-based graph database that runs as a separate container. - -#### Configuration - -```bash -# Environment variables -FALKORDB_URI=redis://falkordb:6379 # Connection URI (default: redis://falkordb:6379) -FALKORDB_PASSWORD= # Password (default: empty) -FALKORDB_DATABASE=default_db # Database name (default: default_db) -``` - -#### Accessing FalkorDB - -- **Redis Protocol:** redis://localhost:6379 -- **MCP Server:** http://localhost:8000 - -#### Data Management - -**Backup:** -```bash -docker run --rm -v docker_falkordb_data:/data -v $(pwd):/backup alpine \ - tar czf /backup/falkordb-backup.tar.gz -C /data . -``` - -**Restore:** -```bash -docker run --rm -v docker_falkordb_data:/data -v $(pwd):/backup alpine \ - tar xzf /backup/falkordb-backup.tar.gz -C /data -``` - -**Clear Data:** -```bash -docker-compose -f docker-compose-falkordb.yml down -docker volume rm docker_falkordb_data -docker-compose -f docker-compose-falkordb.yml up -``` - -#### Gotchas -- FalkorDB uses Redis persistence mechanisms (AOF/RDB) -- Default configuration has no password - add one for production -- Database name is created automatically if it doesn't exist -- Redis commands can be used for debugging: `redis-cli -h localhost` - ## Switching Between Databases -To switch from one database to another: +To switch from FalkorDB to Neo4j (or vice versa): 1. **Stop current setup:** ```bash - docker-compose down # or docker-compose -f docker-compose-[db].yml down + docker-compose down # Stop FalkorDB combined image + # or + docker-compose -f docker-compose-neo4j.yml down # Stop Neo4j ``` 2. **Start new database:** ```bash - docker-compose -f docker-compose-[neo4j|falkordb].yml up - # or just docker-compose up for KuzuDB + docker-compose up # Start FalkorDB combined image + # or + docker-compose -f docker-compose-neo4j.yml up # Start Neo4j ``` Note: Data is not automatically migrated between different database types. You'll need to export from one and import to another using the MCP API. @@ -235,9 +187,10 @@ lsof -i :8000 ### Database Connection Issues -**KuzuDB:** -- Check volume permissions: `docker exec graphiti-mcp ls -la /data` -- Verify database file isn't corrupted +**FalkorDB:** +- Test Redis connectivity: `docker compose exec graphiti-falkordb redis-cli ping` +- Check FalkorDB logs: `docker compose logs graphiti-falkordb` +- Verify both services started: Look for "FalkorDB is ready!" and "Starting MCP server..." in logs **Neo4j:** - Wait for health check to pass (can take 30+ seconds) @@ -246,7 +199,6 @@ lsof -i :8000 **FalkorDB:** - Test Redis connectivity: `redis-cli -h localhost ping` -- Check FalkorDB logs: `docker-compose -f docker-compose-falkordb.yml logs falkordb` ### Data Not Persisting @@ -267,29 +219,23 @@ lsof -i :8000 ### Performance Issues -**KuzuDB:** -- Increase `KUZU_MAX_CONCURRENT_QUERIES` -- Consider using SSD for database file storage -- Monitor with: `docker stats graphiti-mcp` +**FalkorDB:** +- Adjust `SEMAPHORE_LIMIT` environment variable +- Monitor with: `docker stats graphiti-falkordb` +- Check Redis memory: `docker compose exec graphiti-falkordb redis-cli info memory` **Neo4j:** - Increase heap memory in docker-compose-neo4j.yml - Adjust page cache size based on data size - Check query performance in Neo4j browser -**FalkorDB:** -- Adjust Redis max memory policy -- Monitor with: `redis-cli -h localhost info memory` -- Consider Redis persistence settings (AOF vs RDB) - ## Docker Resources ### Volumes Each database configuration uses named volumes for data persistence: -- KuzuDB: `kuzu_data` +- FalkorDB (combined): `falkordb_data` - Neo4j: `neo4j_data`, `neo4j_logs` -- FalkorDB: `falkordb_data` ### Networks @@ -312,8 +258,7 @@ services: ## Configuration Files Each database has a dedicated configuration file in `../config/`: -- `config-docker-kuzu.yaml` - KuzuDB configuration +- `config-docker-falkordb-combined.yaml` - FalkorDB combined image configuration - `config-docker-neo4j.yaml` - Neo4j configuration -- `config-docker-falkordb.yaml` - FalkorDB configuration -These files are mounted read-only into the container at `/app/config/config.yaml`. \ No newline at end of file +These files are mounted read-only into the container at `/app/mcp/config/config.yaml` (for combined image) or `/app/config/config.yaml` (for Neo4j). \ No newline at end of file diff --git a/mcp_server/src/config/schema.py b/mcp_server/src/config/schema.py index a7554df9..eae275f3 100644 --- a/mcp_server/src/config/schema.py +++ b/mcp_server/src/config/schema.py @@ -194,25 +194,17 @@ class FalkorDBProviderConfig(BaseModel): database: str = 'default_db' -class KuzuProviderConfig(BaseModel): - """KuzuDB provider configuration.""" - - db: str = ':memory:' - max_concurrent_queries: int = 1 - - class DatabaseProvidersConfig(BaseModel): """Database providers configuration.""" neo4j: Neo4jProviderConfig | None = None falkordb: FalkorDBProviderConfig | None = None - kuzu: KuzuProviderConfig | None = None class DatabaseConfig(BaseModel): """Database configuration.""" - provider: str = Field(default='kuzu', description='Database provider') + provider: str = Field(default='falkordb', description='Database provider') providers: DatabaseProvidersConfig = Field(default_factory=DatabaseProvidersConfig) diff --git a/mcp_server/src/graphiti_mcp_server.py b/mcp_server/src/graphiti_mcp_server.py index 994ad0ae..1be3f9b1 100644 --- a/mcp_server/src/graphiti_mcp_server.py +++ b/mcp_server/src/graphiti_mcp_server.py @@ -185,22 +185,7 @@ class GraphitiService: self.entity_types = custom_types # Initialize Graphiti client with appropriate driver - if self.config.database.provider.lower() == 'kuzu': - # For KuzuDB, create a KuzuDriver instance directly - from graphiti_core.driver.kuzu_driver import KuzuDriver - - kuzu_driver = KuzuDriver( - db=db_config['db'], - max_concurrent_queries=db_config['max_concurrent_queries'], - ) - - self.client = Graphiti( - graph_driver=kuzu_driver, - llm_client=llm_client, - embedder=embedder_client, - max_coroutines=self.semaphore_limit, - ) - elif self.config.database.provider.lower() == 'falkordb': + if self.config.database.provider.lower() == 'falkordb': # For FalkorDB, create a FalkorDriver instance directly from graphiti_core.driver.falkordb_driver import FalkorDriver @@ -742,7 +727,7 @@ async def initialize_server() -> ServerConfig: ) parser.add_argument( '--database-provider', - choices=['neo4j', 'falkordb', 'kuzu'], + choices=['neo4j', 'falkordb'], help='Database provider to use', ) diff --git a/mcp_server/src/services/factories.py b/mcp_server/src/services/factories.py index c8ccd5fa..e84901f5 100644 --- a/mcp_server/src/services/factories.py +++ b/mcp_server/src/services/factories.py @@ -16,13 +16,7 @@ try: except ImportError: HAS_FALKOR = False -# Try to import KuzuDriver if available -try: - from graphiti_core.driver.kuzu_driver import KuzuDriver # noqa: F401 - - HAS_KUZU = True -except ImportError: - HAS_KUZU = False +# Kuzu support removed - FalkorDB is now the default from graphiti_core.embedder import EmbedderClient, OpenAIEmbedder from graphiti_core.llm_client import LLMClient, OpenAIClient from graphiti_core.llm_client.config import LLMConfig as GraphitiLLMConfig @@ -447,34 +441,5 @@ class DatabaseDriverFactory: 'database': falkor_config.database, } - case 'kuzu': - if not HAS_KUZU: - raise ValueError('KuzuDB driver not available in current graphiti-core version') - - # Use KuzuDB config if provided, otherwise use defaults - if config.providers.kuzu: - kuzu_config = config.providers.kuzu - else: - # Create default KuzuDB configuration - from config.schema import KuzuProviderConfig - - kuzu_config = KuzuProviderConfig() - - # Check for environment variable overrides (for CI/CD compatibility) - import os - - db = os.environ.get('KUZU_DB', kuzu_config.db) - max_concurrent_queries = int( - os.environ.get( - 'KUZU_MAX_CONCURRENT_QUERIES', kuzu_config.max_concurrent_queries - ) - ) - - return { - 'driver': 'kuzu', - 'db': db, - 'max_concurrent_queries': max_concurrent_queries, - } - case _: raise ValueError(f'Unsupported Database provider: {provider}') diff --git a/mcp_server/tests/README.md b/mcp_server/tests/README.md index d798b9e8..e0a3c9bb 100644 --- a/mcp_server/tests/README.md +++ b/mcp_server/tests/README.md @@ -26,7 +26,6 @@ Tests are organized with pytest markers: - `slow` - Long-running tests (stress/load tests) - `requires_neo4j` - Tests requiring Neo4j - `requires_falkordb` - Tests requiring FalkorDB -- `requires_kuzu` - Tests requiring KuzuDB - `requires_openai` - Tests requiring OpenAI API key ## Installation @@ -69,7 +68,7 @@ Suites: all - All tests Options: - --database - Database backend (neo4j, falkordb, kuzu) + --database - Database backend (neo4j, falkordb) --mock-llm - Use mock LLM for faster testing --parallel N - Run tests in parallel with N workers --coverage - Generate coverage report @@ -81,8 +80,8 @@ Options: ### Examples ```bash -# Quick smoke test with KuzuDB -python tests/run_tests.py smoke --database kuzu +# Quick smoke test with FalkorDB (default) +python tests/run_tests.py smoke # Full integration test with Neo4j python tests/run_tests.py integration --database neo4j @@ -132,12 +131,11 @@ python tests/run_tests.py all --check-only ```bash # Database configuration -export DATABASE_PROVIDER=kuzu # or neo4j, falkordb +export DATABASE_PROVIDER=falkordb # or neo4j export NEO4J_URI=bolt://localhost:7687 export NEO4J_USER=neo4j export NEO4J_PASSWORD=graphiti export FALKORDB_URI=redis://localhost:6379 -export KUZU_PATH=./test_kuzu.db # LLM configuration export OPENAI_API_KEY=your_key_here # or use --mock-llm @@ -178,7 +176,7 @@ Simplified client creation: ```python from test_fixtures import graphiti_test_client -async with graphiti_test_client(database="kuzu") as (session, group_id): +async with graphiti_test_client(database="falkordb") as (session, group_id): # Use session for testing result = await session.call_tool('add_memory', {...}) ``` diff --git a/mcp_server/tests/pytest.ini b/mcp_server/tests/pytest.ini index c024df39..88bc73b2 100644 --- a/mcp_server/tests/pytest.ini +++ b/mcp_server/tests/pytest.ini @@ -17,7 +17,6 @@ markers = stress: marks tests as stress/load tests requires_neo4j: test requires Neo4j database requires_falkordb: test requires FalkorDB - requires_kuzu: test requires KuzuDB requires_openai: test requires OpenAI API key # Test output options diff --git a/mcp_server/tests/run_tests.py b/mcp_server/tests/run_tests.py index e6c15576..1e0c6cd9 100644 --- a/mcp_server/tests/run_tests.py +++ b/mcp_server/tests/run_tests.py @@ -53,8 +53,6 @@ class TestRunner: checks['neo4j'] = self._check_neo4j() elif self.args.database == 'falkordb': checks['falkordb'] = self._check_falkordb() - elif self.args.database == 'kuzu': - checks['kuzu'] = True # KuzuDB is embedded # Check Python dependencies checks['mcp'] = self._check_python_package('mcp') @@ -107,7 +105,7 @@ class TestRunner: # Add database marker if self.args.database: - for db in ['neo4j', 'falkordb', 'kuzu']: + for db in ['neo4j', 'falkordb']: if db != self.args.database: pytest_args.extend(['-m', f'not requires_{db}']) @@ -257,9 +255,9 @@ Examples: parser.add_argument( '--database', - choices=['neo4j', 'falkordb', 'kuzu'], - default='kuzu', - help='Database backend to test (default: kuzu)', + choices=['neo4j', 'falkordb'], + default='falkordb', + help='Database backend to test (default: falkordb)', ) parser.add_argument('--mock-llm', action='store_true', help='Use mock LLM for faster testing') diff --git a/mcp_server/tests/test_comprehensive_integration.py b/mcp_server/tests/test_comprehensive_integration.py index b39ec485..5e0eaf49 100644 --- a/mcp_server/tests/test_comprehensive_integration.py +++ b/mcp_server/tests/test_comprehensive_integration.py @@ -51,7 +51,6 @@ class GraphitiTestClient: 'NEO4J_USER': os.environ.get('NEO4J_USER', 'neo4j'), 'NEO4J_PASSWORD': os.environ.get('NEO4J_PASSWORD', 'graphiti'), 'OPENAI_API_KEY': os.environ.get('OPENAI_API_KEY', 'test_key_for_mock'), - 'KUZU_PATH': os.environ.get('KUZU_PATH', './test_kuzu.db'), 'FALKORDB_URI': os.environ.get('FALKORDB_URI', 'redis://localhost:6379'), }, ) @@ -592,7 +591,7 @@ class TestDatabaseBackends: """Test different database backend configurations.""" @pytest.mark.asyncio - @pytest.mark.parametrize('database', ['neo4j', 'falkordb', 'kuzu']) + @pytest.mark.parametrize('database', ['neo4j', 'falkordb']) async def test_database_operations(self, database): """Test operations with different database backends.""" env_vars = { @@ -610,10 +609,6 @@ class TestDatabaseBackends: ) elif database == 'falkordb': env_vars['FALKORDB_URI'] = os.environ.get('FALKORDB_URI', 'redis://localhost:6379') - elif database == 'kuzu': - env_vars['KUZU_PATH'] = os.environ.get( - 'KUZU_PATH', f'./test_kuzu_{int(time.time())}.db' - ) # This test would require setting up server with specific database # Implementation depends on database availability diff --git a/mcp_server/tests/test_fixtures.py b/mcp_server/tests/test_fixtures.py index 68284a74..04c7eadb 100644 --- a/mcp_server/tests/test_fixtures.py +++ b/mcp_server/tests/test_fixtures.py @@ -150,7 +150,7 @@ class MockLLMProvider: @asynccontextmanager async def graphiti_test_client( group_id: str | None = None, - database: str = 'kuzu', + database: str = 'falkordb', use_mock_llm: bool = False, config_overrides: dict[str, Any] | None = None, ): @@ -159,7 +159,7 @@ async def graphiti_test_client( Args: group_id: Test group identifier - database: Database backend (neo4j, falkordb, kuzu) + database: Database backend (neo4j, falkordb) use_mock_llm: Whether to use mock LLM for faster tests config_overrides: Additional config overrides """ @@ -181,8 +181,6 @@ async def graphiti_test_client( ) elif database == 'falkordb': env['FALKORDB_URI'] = os.environ.get('FALKORDB_URI', 'redis://localhost:6379') - elif database == 'kuzu': - env['KUZU_PATH'] = os.environ.get('KUZU_PATH', f'./test_kuzu_{test_group_id}.db') # Apply config overrides if config_overrides: