diff --git a/Dockerfile b/Dockerfile index 6ea8fdc..adbf239 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y \ WORKDIR /app # Copy requirements files -COPY requirements.txt requirements-dev.txt ./ +COPY requirements.txt ./ # Create virtual environment and install dependencies RUN python -m venv /opt/venv diff --git a/docker/scripts/deploy.sh b/docker/scripts/deploy.sh index 312eb9e..572c83a 100644 --- a/docker/scripts/deploy.sh +++ b/docker/scripts/deploy.sh @@ -11,7 +11,22 @@ echo -e "${GREEN}=== Deploying Zen MCP Server ===${NC}" # Function to check if required environment variables are set check_env_vars() { - local required_vars=("GOOGLE_API_KEY" "OPENAI_API_KEY") + # At least one of these API keys must be set + local required_vars=("GEMINI_API_KEY" "GOOGLE_API_KEY" "OPENAI_API_KEY" "XAI_API_KEY" "DIAL_API_KEY" "OPENROUTER_API_KEY") + + local has_api_key=false + for var in "${required_vars[@]}"; do + if [[ -n "${!var:-}" ]]; then + has_api_key=true + break + fi + done + + if [[ "$has_api_key" == false ]]; then + echo -e "${RED}Error: At least one API key must be set in your .env file${NC}" + printf ' %s\n' "${required_vars[@]}" + exit 1 + fi local missing_vars=() for var in "${required_vars[@]}"; do @@ -43,6 +58,29 @@ fi # Check required environment variables check_env_vars +# Exponential backoff health check function +wait_for_health() { + local max_attempts=6 + local attempt=1 + local delay=2 + + while (( attempt <= max_attempts )); do + status=$(docker-compose ps -q zen-mcp | xargs docker inspect -f "{{.State.Health.Status}}" 2>/dev/null || echo "unavailable") + if [[ "$status" == "healthy" ]]; then + return 0 + fi + echo -e "${YELLOW}Waiting for service to be healthy... (attempt $attempt/${max_attempts}, retrying in ${delay}s)${NC}" + sleep $delay + delay=$(( delay * 2 )) + attempt=$(( attempt + 1 )) + done + + echo -e "${RED}Service failed to become healthy after $max_attempts attempts${NC}" + echo -e "${YELLOW}Checking logs:${NC}" + docker-compose logs zen-mcp + exit 1 +} + # Create logs directory if it doesn't exist mkdir -p logs diff --git a/docker/scripts/healthcheck.py b/docker/scripts/healthcheck.py index 339c6f0..118c835 100644 --- a/docker/scripts/healthcheck.py +++ b/docker/scripts/healthcheck.py @@ -7,15 +7,16 @@ import os import subprocess import sys +import openai + def check_process(): """Check if the main server process is running""" - try: - result = subprocess.run(["pgrep", "-f", "server.py"], capture_output=True, text=True) - return result.returncode == 0 - except Exception as e: - print(f"Process check failed: {e}", file=sys.stderr) - return False + result = subprocess.run(["pgrep", "-f", "server.py"], capture_output=True, text=True, timeout=10) + if result.returncode == 0: + return True + print(f"Process check failed: {result.stderr}", file=sys.stderr) + return False def check_python_imports(): @@ -69,6 +70,41 @@ def check_environment(): print("No API keys found in environment", file=sys.stderr) return False + # Validate API key formats (basic checks) + for key in api_keys: + value = os.getenv(key) + if value: + if len(value.strip()) < 10: + print(f"API key {key} appears too short or invalid", file=sys.stderr) + return False + + # Optionally, try a minimal connectivity check for OpenAI and Google Gemini + # (only if their API keys are present) + try: + if os.getenv("OPENAI_API_KEY"): + openai.api_key = os.getenv("OPENAI_API_KEY") + try: + openai.Model.list() + except Exception as e: + print(f"OpenAI connectivity check failed: {e}", file=sys.stderr) + return False + except ImportError: + pass # Already checked in check_python_imports + + try: + if os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY"): + import google.genai as genai + + key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY") + genai.configure(api_key=key) + try: + genai.list_models() + except Exception as e: + print(f"Google Gemini connectivity check failed: {e}", file=sys.stderr) + return False + except ImportError: + pass # Already checked in check_python_imports + return True