feat: enhance chat agent intelligence for name-based queries #81
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: MCP Server CI/CD Pipeline | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| paths: | |
| - 'apps/mcp/**' | |
| - 'packages/auth/**' | |
| - '.github/workflows/mcp-deploy.yml' | |
| pull_request: | |
| branches: [ main, develop ] | |
| paths: | |
| - 'apps/mcp/**' | |
| - 'packages/auth/**' | |
| env: | |
| PYTHON_VERSION: '3.11' | |
| DOCKER_IMAGE: 'wildeditor-mcp' | |
| REGISTRY: 'ghcr.io' | |
| jobs: | |
| # Test MCP server | |
| test-mcp: | |
| name: Test MCP Server | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Cache pip dependencies | |
| uses: actions/cache@v3 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-mcp-${{ hashFiles('apps/mcp/requirements.txt') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-mcp- | |
| - name: Install dependencies | |
| run: | | |
| # Install auth package first | |
| pip install -e packages/auth | |
| cd apps/mcp | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: Lint with flake8 | |
| run: | | |
| pip install flake8 | |
| flake8 apps/mcp/src --count --select=E9,F63,F7,F82 --show-source --statistics | |
| flake8 apps/mcp/src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics | |
| - name: Type check with mypy | |
| run: | | |
| pip install mypy | |
| mypy apps/mcp/src --ignore-missing-imports || true | |
| - name: Run tests | |
| run: | | |
| cd apps/mcp | |
| # Set test environment variables | |
| export WILDEDITOR_MCP_KEY="test-mcp-key-for-github-actions" | |
| export WILDEDITOR_API_KEY="test-backend-key-for-github-actions" | |
| export WILDEDITOR_BACKEND_URL="http://localhost:8000" | |
| export TESTING="1" | |
| # Run tests | |
| PYTHONPATH=. pytest tests/ -v --tb=short | |
| - name: Test server startup | |
| run: | | |
| cd apps/mcp | |
| # Set environment variables for testing | |
| export WILDEDITOR_MCP_KEY="test-startup-key" | |
| export WILDEDITOR_API_KEY="test-startup-backend-key" | |
| export WILDEDITOR_BACKEND_URL="http://localhost:8000" | |
| export TESTING="1" | |
| # Run a basic smoke test to ensure the app can start | |
| python -c " | |
| import sys | |
| import os | |
| sys.path.insert(0, '.') | |
| os.environ['WILDEDITOR_MCP_KEY'] = 'test-startup-key' | |
| os.environ['WILDEDITOR_API_KEY'] = 'test-startup-backend-key' | |
| os.environ['WILDEDITOR_BACKEND_URL'] = 'http://localhost:8000' | |
| os.environ['TESTING'] = '1' | |
| from src.main import app | |
| from fastapi.testclient import TestClient | |
| client = TestClient(app) | |
| response = client.get('/health') | |
| assert response.status_code == 200 | |
| print('✅ MCP server startup test passed') | |
| " | |
| - name: Security check with bandit | |
| run: | | |
| pip install bandit | |
| bandit -r apps/mcp/src -f json -o mcp-bandit-report.json || true | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: mcp-test-results | |
| path: | | |
| mcp-bandit-report.json | |
| retention-days: 7 | |
| # Build Docker image | |
| build-mcp-image: | |
| name: Build MCP Docker Image | |
| runs-on: ubuntu-latest | |
| needs: test-mcp | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/luminarimud/wildeditor-mcp | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=sha,prefix={{branch}}- | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: apps/mcp/Dockerfile | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Output image digest | |
| run: echo ${{ steps.build.outputs.digest }} | |
| # Deploy to production server | |
| deploy-mcp-production: | |
| name: Deploy MCP to Production | |
| runs-on: ubuntu-latest | |
| needs: [test-mcp, build-mcp-image] | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| environment: | |
| name: production | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup SSH | |
| uses: webfactory/ssh-agent@v0.8.0 | |
| with: | |
| ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }} | |
| - name: Add server to known hosts | |
| run: | | |
| mkdir -p ~/.ssh | |
| ssh-keyscan -H ${{ secrets.PRODUCTION_HOST }} >> ~/.ssh/known_hosts | |
| chmod 600 ~/.ssh/known_hosts | |
| - name: Test SSH connection | |
| run: | | |
| echo "Testing SSH connection to ${{ secrets.PRODUCTION_HOST }}..." | |
| ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no ${{ secrets.PRODUCTION_USER }}@${{ secrets.PRODUCTION_HOST }} "echo 'SSH connection successful'" | |
| - name: Setup MCP server environment | |
| run: | | |
| echo "Setting up MCP server environment..." | |
| ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 ${{ secrets.PRODUCTION_USER }}@${{ secrets.PRODUCTION_HOST }} << 'SETUP_EOF' | |
| # Create MCP directories in user's home directory (no sudo needed) | |
| mkdir -p ~/wildeditor-mcp | |
| mkdir -p ~/logs/wildeditor-mcp | |
| # Check if port 8001 is available | |
| echo "🌐 Checking if port 8001 is available..." | |
| if ss -tlnp | grep :8001; then | |
| echo "⚠️ Port 8001 is already in use:" | |
| ss -tlnp | grep :8001 | |
| else | |
| echo "✅ Port 8001 is available" | |
| fi | |
| echo "✅ MCP server environment setup complete" | |
| SETUP_EOF | |
| - name: Deploy MCP server to production | |
| env: | |
| WILDEDITOR_MCP_KEY: ${{ secrets.WILDEDITOR_MCP_KEY }} | |
| WILDEDITOR_API_KEY: ${{ secrets.WILDEDITOR_API_KEY }} | |
| # AI Provider Keys | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} | |
| AI_PROVIDER: ${{ secrets.AI_PROVIDER }} | |
| OPENAI_MODEL: ${{ secrets.OPENAI_MODEL }} | |
| ANTHROPIC_MODEL: ${{ secrets.ANTHROPIC_MODEL }} | |
| DEEPSEEK_MODEL: ${{ secrets.DEEPSEEK_MODEL }} | |
| OLLAMA_BASE_URL: ${{ secrets.OLLAMA_BASE_URL }} | |
| OLLAMA_MODEL: ${{ secrets.OLLAMA_MODEL }} | |
| run: | | |
| # Check if MCP keys are provided | |
| if [ -z "$WILDEDITOR_MCP_KEY" ]; then | |
| echo "❌ WILDEDITOR_MCP_KEY secret is not set in GitHub repository" | |
| echo "Please add it in Settings → Secrets and variables → Actions" | |
| echo "Generate using: PowerShell -Command \"\$bytes = New-Object byte[] 32; (New-Object Security.Cryptography.RNGCryptoServiceProvider).GetBytes(\$bytes); [Convert]::ToBase64String(\$bytes)\"" | |
| exit 1 | |
| fi | |
| if [ -z "$WILDEDITOR_API_KEY" ]; then | |
| echo "❌ WILDEDITOR_API_KEY secret is not set in GitHub repository" | |
| echo "Please add it in Settings → Secrets and variables → Actions" | |
| echo "Generate using: PowerShell -Command \"\$bytes = New-Object byte[] 32; (New-Object Security.Cryptography.RNGCryptoServiceProvider).GetBytes(\$bytes); [Convert]::ToBase64String(\$bytes)\"" | |
| exit 1 | |
| fi | |
| echo "✅ MCP keys are configured" | |
| # Create MCP deployment script | |
| cat > deploy-mcp.sh << 'EOF' | |
| #!/bin/bash | |
| set -e | |
| echo "🚀 Starting MCP server deployment..." | |
| # Configuration | |
| DOCKER_IMAGE="ghcr.io/luminarimud/wildeditor-mcp:latest" | |
| CONTAINER_NAME="wildeditor-mcp" | |
| # Keys will be passed as environment variables | |
| if [ -z "$MCP_KEY" ]; then | |
| echo "❌ MCP key not provided to deployment script" | |
| exit 1 | |
| fi | |
| if [ -z "$MCP_BACKEND_KEY" ]; then | |
| echo "❌ MCP backend key not provided to deployment script" | |
| exit 1 | |
| fi | |
| echo "✅ MCP key received: ${MCP_KEY:0:8}..." | |
| echo "✅ MCP backend key received: ${MCP_BACKEND_KEY:0:8}..." | |
| # Check Docker access | |
| if groups | grep -q docker; then | |
| DOCKER_CMD="docker" | |
| echo "✅ User is in docker group" | |
| elif sudo -n true 2>/dev/null; then | |
| DOCKER_CMD="sudo docker" | |
| echo "✅ Passwordless sudo available" | |
| else | |
| echo "❌ Error: User needs to be in docker group or have passwordless sudo access" | |
| exit 1 | |
| fi | |
| # Debug: Check if directory exists and show current working directory | |
| echo "🔍 Current working directory: $(pwd)" | |
| echo "🔍 Target directory: ~/wildeditor-mcp" | |
| if [ -d "$HOME/wildeditor-mcp" ]; then | |
| echo "✅ Target directory exists" | |
| ls -la ~/wildeditor-mcp | |
| else | |
| echo "❌ Target directory does not exist, creating it..." | |
| mkdir -p ~/wildeditor-mcp | |
| echo "✅ Directory created in home directory" | |
| fi | |
| echo "🔍 Changing to target directory..." | |
| cd ~/wildeditor-mcp | |
| echo "✅ Now in directory: $(pwd)" | |
| # Login to GitHub Container Registry | |
| echo "GITHUB_TOKEN_PLACEHOLDER" | $DOCKER_CMD login ghcr.io -u GITHUB_ACTOR_PLACEHOLDER --password-stdin | |
| # Pull latest MCP image | |
| echo "📦 Pulling latest MCP Docker image..." | |
| $DOCKER_CMD pull $DOCKER_IMAGE | |
| # Stop existing MCP container | |
| echo "🛑 Stopping existing MCP container..." | |
| $DOCKER_CMD stop $CONTAINER_NAME || true | |
| $DOCKER_CMD rm $CONTAINER_NAME || true | |
| # Start new MCP container with host networking (same as backend) | |
| echo "🏃 Starting new MCP container with host networking..." | |
| $DOCKER_CMD run -d \ | |
| --name $CONTAINER_NAME \ | |
| --restart unless-stopped \ | |
| --network host \ | |
| -e WILDEDITOR_MCP_KEY="$MCP_KEY" \ | |
| -e WILDEDITOR_API_KEY="$MCP_BACKEND_KEY" \ | |
| -e WILDEDITOR_BACKEND_URL="http://localhost:8000" \ | |
| -e ENVIRONMENT="production" \ | |
| -e DEBUG="false" \ | |
| -e PORT="8001" \ | |
| -e LOG_LEVEL="INFO" \ | |
| -e AI_PROVIDER="${AI_PROVIDER:-none}" \ | |
| -e OPENAI_API_KEY="$OPENAI_API_KEY" \ | |
| -e OPENAI_MODEL="${OPENAI_MODEL:-gpt-4-turbo-preview}" \ | |
| -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \ | |
| -e ANTHROPIC_MODEL="${ANTHROPIC_MODEL:-claude-3-opus-20240229}" \ | |
| -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ | |
| -e DEEPSEEK_MODEL="${DEEPSEEK_MODEL:-deepseek-chat}" \ | |
| -e OLLAMA_BASE_URL="$OLLAMA_BASE_URL" \ | |
| -e OLLAMA_MODEL="${OLLAMA_MODEL:-llama2}" \ | |
| -v $HOME/logs/wildeditor-mcp:/var/log/wildeditor-mcp \ | |
| $DOCKER_IMAGE | |
| # Wait for container to be healthy | |
| echo "🏥 Waiting for health check..." | |
| for i in {1..30}; do | |
| # First check if container is running | |
| if ! $DOCKER_CMD ps | grep -q $CONTAINER_NAME; then | |
| echo "❌ Container is not running" | |
| $DOCKER_CMD logs --tail 10 $CONTAINER_NAME | |
| exit 1 | |
| fi | |
| # Try health check with curl from host | |
| if curl -s --connect-timeout 5 --max-time 10 http://localhost:8001/health >/dev/null 2>&1; then | |
| echo "✅ MCP container is healthy!" | |
| break | |
| fi | |
| if [ $i -eq 30 ]; then | |
| echo "❌ MCP container failed to become healthy after 60 seconds" | |
| echo "🔍 Container status:" | |
| $DOCKER_CMD ps -a | grep $CONTAINER_NAME | |
| echo "🔍 Container logs:" | |
| $DOCKER_CMD logs --tail 20 $CONTAINER_NAME | |
| echo "🔍 Port status:" | |
| ss -tlnp | grep :8001 || echo "Port 8001 not listening" | |
| echo "🔍 Manual health check:" | |
| curl -v http://localhost:8001/health || echo "Curl failed" | |
| exit 1 | |
| fi | |
| echo "Waiting... ($i/30)" | |
| sleep 2 | |
| done | |
| # Test MCP endpoints | |
| echo "🧪 Testing MCP endpoints..." | |
| if curl -s --connect-timeout 5 --max-time 10 http://localhost:8001/health >/dev/null; then | |
| echo "✅ Health endpoint working" | |
| else | |
| echo "❌ Health endpoint failed" | |
| exit 1 | |
| fi | |
| # Test authenticated endpoint | |
| if curl -s --connect-timeout 5 --max-time 10 -H "X-API-Key: $MCP_KEY" http://localhost:8001/mcp/status >/dev/null; then | |
| echo "✅ Authenticated endpoints working" | |
| else | |
| echo "⚠️ Authenticated endpoint test failed (but health check passed)" | |
| fi | |
| # Cleanup old images | |
| echo "🧹 Cleaning up old images..." | |
| $DOCKER_CMD image prune -f | |
| echo "🎉 MCP server deployment completed successfully!" | |
| EOF | |
| # Replace placeholders in the script | |
| sed -i "s/GITHUB_TOKEN_PLACEHOLDER/${{ secrets.GITHUB_TOKEN }}/g" deploy-mcp.sh | |
| sed -i "s/GITHUB_ACTOR_PLACEHOLDER/${{ github.actor }}/g" deploy-mcp.sh | |
| # Copy and execute MCP deployment script with environment variables | |
| echo "Copying MCP deployment script to server..." | |
| scp -o StrictHostKeyChecking=no -o ConnectTimeout=30 deploy-mcp.sh ${{ secrets.PRODUCTION_USER }}@${{ secrets.PRODUCTION_HOST }}:/tmp/ | |
| echo "Executing MCP deployment script on server..." | |
| ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 ${{ secrets.PRODUCTION_USER }}@${{ secrets.PRODUCTION_HOST }} " | |
| chmod +x /tmp/deploy-mcp.sh | |
| export MCP_KEY='$WILDEDITOR_MCP_KEY' | |
| export MCP_BACKEND_KEY='$WILDEDITOR_API_KEY' | |
| export AI_PROVIDER='${AI_PROVIDER:-none}' | |
| export OPENAI_API_KEY='$OPENAI_API_KEY' | |
| export OPENAI_MODEL='${OPENAI_MODEL:-gpt-4-turbo-preview}' | |
| export ANTHROPIC_API_KEY='$ANTHROPIC_API_KEY' | |
| export ANTHROPIC_MODEL='${ANTHROPIC_MODEL:-claude-3-opus-20240229}' | |
| export DEEPSEEK_API_KEY='$DEEPSEEK_API_KEY' | |
| export DEEPSEEK_MODEL='${DEEPSEEK_MODEL:-deepseek-chat}' | |
| export OLLAMA_BASE_URL='$OLLAMA_BASE_URL' | |
| export OLLAMA_MODEL='${OLLAMA_MODEL:-llama2}' | |
| /tmp/deploy-mcp.sh | |
| " | |
| - name: Verify MCP deployment | |
| run: | | |
| # Wait a moment for the service to be fully ready | |
| sleep 10 | |
| echo "🔍 Verifying MCP deployment..." | |
| # Test from within the server | |
| echo "Testing MCP health endpoint from server..." | |
| SERVER_HEALTH_CHECK=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 ${{ secrets.PRODUCTION_USER }}@${{ secrets.PRODUCTION_HOST }} " | |
| echo '🏥 Testing MCP health endpoint from server:' | |
| if curl -s --connect-timeout 5 --max-time 10 http://localhost:8001/health; then | |
| echo '' | |
| echo '✅ MCP server-side health check PASSED' | |
| echo '' | |
| echo '🔐 Testing MCP authentication endpoint:' | |
| if curl -s --connect-timeout 5 --max-time 10 -H 'X-API-Key: $WILDEDITOR_MCP_KEY' http://localhost:8001/mcp/status | grep -q 'mcp_server'; then | |
| echo '✅ MCP authentication endpoint PASSED' | |
| else | |
| echo '⚠️ MCP authentication endpoint test failed (but health check passed)' | |
| fi | |
| exit 0 | |
| else | |
| echo '' | |
| echo '❌ MCP server-side health check FAILED' | |
| echo 'Container logs:' | |
| docker logs --tail 10 wildeditor-mcp || true | |
| exit 1 | |
| fi | |
| ") | |
| if [ $? -eq 0 ]; then | |
| echo "✅ MCP server-side health check passed!" | |
| else | |
| echo "❌ MCP server-side health check failed!" | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "✅ MCP deployment verification completed successfully!" | |
| echo "The MCP server is running and responding to health checks on port 8001" | |
| - name: Create MCP deployment notification | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const output = `#### 🤖 MCP Server Deployment Successful!\n | |
| - **Environment**: Production | |
| - **Version**: \`${{ github.sha }}\` | |
| - **MCP API URL**: http://${{ secrets.PRODUCTION_HOST }}:8001 | |
| - **Health Check**: ✅ Passing | |
| - **Authentication**: 🔐 MCP Key Required | |
| - **AI Provider**: ${process.env.AI_PROVIDER || 'Template Fallback'} | |
| - **Supported Providers**: OpenAI, Anthropic, DeepSeek, Ollama | |
| - **Tools**: 14 Wilderness Management Tools (5 with AI) | |
| - **Resources**: 7 Knowledge Resources | |
| - **Prompts**: 5 AI Content Generation Templates | |
| - **Deployed by**: @${{ github.actor }} | |
| The MCP server is now live with AI-powered description generation! | |
| **For AI Agents:** | |
| - Connect to: \`http://${{ secrets.PRODUCTION_HOST }}:8001\` | |
| - Use the configured MCP key for authentication | |
| - Test connection: \`curl -H "X-API-Key: YOUR_MCP_KEY" http://${{ secrets.PRODUCTION_HOST }}:8001/mcp/status\` | |
| **Available Tools:** | |
| - \`analyze_region\` - Deep wilderness region analysis | |
| - \`find_path\` - Pathfinding between regions | |
| - \`search_regions\` - Advanced region search | |
| - \`create_region\` - New region creation | |
| - \`validate_connections\` - Connection consistency checking`; | |
| github.rest.repos.createCommitComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| commit_sha: '${{ github.sha }}', | |
| body: output | |
| }) | |
| # Notify deployment status | |
| notify-mcp: | |
| name: Notify MCP Deployment Status | |
| runs-on: ubuntu-latest | |
| needs: [deploy-mcp-production] | |
| if: always() && github.ref == 'refs/heads/main' | |
| steps: | |
| - name: Log MCP deployment result | |
| run: | | |
| if [ "${{ needs.deploy-mcp-production.result }}" == "success" ]; then | |
| echo "🚀 Wildeditor MCP Server deployed successfully!" | |
| else | |
| echo "❌ Wildeditor MCP Server deployment failed!" | |
| exit 1 | |
| fi |