fix: Resolve CI/CD issues for MCP server

- Fixed code formatting with ruff (removed trailing whitespace)
- Fixed linting issues (removed unused imports)
- Updated Dockerfile for new directory structure
- Improved FalkorDB container health checks and timeouts
- Enhanced FalkorDB readiness check with GRAPH module verification
- Added debugging for FalkorDB startup failures

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Daniel Chalef 2025-08-26 07:35:09 -07:00
parent d20340701f
commit 694ea46f66
6 changed files with 72 additions and 53 deletions

View file

@ -40,11 +40,11 @@ jobs:
- 6379:6379
- 3000:3000
options: >-
--health-cmd "redis-cli -h localhost -p 6379 ping"
--health-interval 15s
--health-timeout 10s
--health-retries 8
--health-start-period 30s
--health-cmd "redis-cli -h localhost -p 6379 ping || exit 1"
--health-interval 20s
--health-timeout 15s
--health-retries 12
--health-start-period 60s
steps:
- name: Checkout repository
@ -200,12 +200,26 @@ jobs:
- name: Wait for FalkorDB to be ready
run: |
echo "🔄 Waiting for FalkorDB to be ready..."
max_attempts=30
# Install redis-tools first if not available
if ! command -v redis-cli &> /dev/null; then
echo "📦 Installing redis-tools..."
sudo apt-get update && sudo apt-get install -y redis-tools
fi
max_attempts=40
attempt=1
while [ $attempt -le $max_attempts ]; do
if redis-cli -h localhost -p 6379 ping >/dev/null 2>&1; then
if redis-cli -h localhost -p 6379 ping 2>/dev/null | grep -q PONG; then
echo "✅ FalkorDB is ready!"
break
# Verify GRAPH module is loaded
if redis-cli -h localhost -p 6379 MODULE LIST 2>/dev/null | grep -q graph; then
echo "✅ FalkorDB GRAPH module is loaded!"
break
else
echo "⏳ Waiting for GRAPH module to load..."
fi
fi
echo "⏳ Attempt $attempt/$max_attempts - FalkorDB not ready yet..."
sleep 3
@ -214,6 +228,9 @@ jobs:
if [ $attempt -gt $max_attempts ]; then
echo "❌ FalkorDB failed to start within timeout"
# Get container logs for debugging
docker ps -a
docker logs $(docker ps -q -f "ancestor=falkordb/falkordb:v4.12.4") 2>&1 | tail -50 || echo "Could not fetch logs"
exit 1
fi

View file

@ -34,8 +34,9 @@ RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev
# Copy application code and configuration
COPY *.py ./
COPY config.yaml ./
COPY main.py ./
COPY src/ ./src/
COPY config/ ./config/
# Change ownership to app user
RUN chown -Rv app:app /app
@ -47,4 +48,4 @@ USER app
EXPOSE 8000
# Command to run the application
CMD ["uv", "run", "graphiti_mcp_server.py"]
CMD ["uv", "run", "main.py"]

View file

@ -206,7 +206,7 @@ class GraphitiConfig(BaseSettings):
embedder: EmbedderConfig = Field(default_factory=EmbedderConfig)
database: DatabaseConfig = Field(default_factory=DatabaseConfig)
graphiti: GraphitiAppConfig = Field(default_factory=GraphitiAppConfig)
# Additional server options
use_custom_entities: bool = Field(default=False, description='Enable custom entity types')
destroy_graph: bool = Field(default=False, description='Clear graph on startup')

View file

@ -272,7 +272,7 @@ class DatabaseDriverFactory:
)
if not config.providers.falkordb:
raise ValueError('FalkorDB provider configuration not found')
falkor_config = config.providers.falkordb
return {
'driver': 'falkordb',

View file

@ -9,7 +9,7 @@ import json
import time
from typing import Any
from mcp import ClientSession, StdioServerParameters
from mcp import StdioServerParameters
from mcp.client.stdio import stdio_client
@ -37,14 +37,14 @@ class GraphitiFalkorDBIntegrationTest:
# Start the stdio client
self.session = await stdio_client(server_params).__aenter__()
print(f' 📡 Started MCP client session with FalkorDB backend')
print(' 📡 Started MCP client session with FalkorDB backend')
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Clean up the MCP client session."""
if self.session:
await self.session.close()
print(f' 🔌 Closed MCP client session')
print(' 🔌 Closed MCP client session')
async def call_mcp_tool(self, tool_name: str, arguments: dict[str, Any]) -> dict[str, Any]:
"""Call an MCP tool via the stdio client."""
@ -68,15 +68,15 @@ class GraphitiFalkorDBIntegrationTest:
"""Test the get_status tool to verify FalkorDB connectivity."""
print(' 🏥 Testing server status with FalkorDB...')
result = await self.call_mcp_tool('get_status', {})
if 'error' in result:
print(f' ❌ Status check failed: {result["error"]}')
return False
# Check if status indicates FalkorDB is working
status_text = result.get('raw_response', result.get('content', ''))
if 'running' in str(status_text).lower() or 'ready' in str(status_text).lower():
print(f' ✅ Server status OK with FalkorDB')
print(' ✅ Server status OK with FalkorDB')
return True
else:
print(f' ⚠️ Status unclear: {status_text}')
@ -85,54 +85,53 @@ class GraphitiFalkorDBIntegrationTest:
async def test_add_episode(self) -> bool:
"""Test adding an episode to FalkorDB."""
print(' 📝 Testing episode addition to FalkorDB...')
episode_data = {
'name': 'FalkorDB Test Episode',
'episode_body': 'This is a test episode to verify FalkorDB integration works correctly.',
'source': 'text',
'source_description': 'Integration test for FalkorDB backend'
'source_description': 'Integration test for FalkorDB backend',
}
result = await self.call_mcp_tool('add_episode', episode_data)
if 'error' in result:
print(f' ❌ Add episode failed: {result["error"]}')
return False
print(f' ✅ Episode added successfully to FalkorDB')
print(' ✅ Episode added successfully to FalkorDB')
return True
async def test_search_functionality(self) -> bool:
"""Test search functionality with FalkorDB."""
print(' 🔍 Testing search functionality with FalkorDB...')
# Give some time for episode processing
await asyncio.sleep(2)
# Test node search
search_result = await self.call_mcp_tool('search_nodes', {
'query': 'FalkorDB test episode',
'limit': 5
})
search_result = await self.call_mcp_tool(
'search_nodes', {'query': 'FalkorDB test episode', 'limit': 5}
)
if 'error' in search_result:
print(f' ⚠️ Search returned error (may be expected): {search_result["error"]}')
return True # Don't fail on search errors in integration test
print(f' ✅ Search functionality working with FalkorDB')
print(' ✅ Search functionality working with FalkorDB')
return True
async def test_clear_graph(self) -> bool:
"""Test clearing the graph in FalkorDB."""
print(' 🧹 Testing graph clearing in FalkorDB...')
result = await self.call_mcp_tool('clear_graph', {})
if 'error' in result:
print(f' ❌ Clear graph failed: {result["error"]}')
return False
print(f' ✅ Graph cleared successfully in FalkorDB')
print(' ✅ Graph cleared successfully in FalkorDB')
return True
@ -140,13 +139,13 @@ async def run_falkordb_integration_test() -> bool:
"""Run the complete FalkorDB integration test suite."""
print('🧪 Starting FalkorDB Integration Test Suite')
print('=' * 55)
test_results = []
try:
async with GraphitiFalkorDBIntegrationTest() as test_client:
print(f' 🎯 Using test group: {test_client.test_group_id}')
# Run test suite
tests = [
('Server Status', test_client.test_server_status),
@ -154,7 +153,7 @@ async def run_falkordb_integration_test() -> bool:
('Search Functionality', test_client.test_search_functionality),
('Clear Graph', test_client.test_clear_graph),
]
for test_name, test_func in tests:
print(f'\n🔬 Running {test_name} Test...')
try:
@ -167,25 +166,25 @@ async def run_falkordb_integration_test() -> bool:
except Exception as e:
print(f' 💥 {test_name}: ERROR - {e}')
test_results.append((test_name, False))
except Exception as e:
print(f'💥 Test setup failed: {e}')
return False
# Summary
print('\n' + '=' * 55)
print('📊 FalkorDB Integration Test Results:')
print('-' * 30)
passed = sum(1 for _, result in test_results if result)
total = len(test_results)
for test_name, result in test_results:
status = '✅ PASS' if result else '❌ FAIL'
print(f' {test_name}: {status}')
print(f'\n🎯 Overall: {passed}/{total} tests passed')
if passed == total:
print('🎉 All FalkorDB integration tests PASSED!')
return True
@ -196,4 +195,4 @@ async def run_falkordb_integration_test() -> bool:
if __name__ == '__main__':
success = asyncio.run(run_falkordb_integration_test())
exit(0 if success else 1)
exit(0 if success else 1)

View file

@ -13,10 +13,12 @@ import time
def test_server_startup():
"""Test that the refactored server starts up successfully."""
print('🚀 Testing Graphiti MCP Server Startup...')
# Skip server startup test in CI - we have comprehensive integration tests
if os.environ.get('CI'):
print(' ⚠️ Skipping server startup test in CI (comprehensive integration tests handle this)')
print(
' ⚠️ Skipping server startup test in CI (comprehensive integration tests handle this)'
)
return True
# Check if uv is available
@ -29,7 +31,7 @@ def test_server_startup():
break
except (subprocess.TimeoutExpired, FileNotFoundError):
continue
if not uv_cmd:
print(' ⚠️ uv not found in PATH, skipping server startup test')
return True
@ -72,7 +74,7 @@ def test_server_startup():
except Exception:
continue
if not success:
print(' ⚠️ Timeout waiting for initialization or server startup failed')