This is page 25 of 25. Use http://codebase.md/beehiveinnovations/gemini-mcp-server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .claude
│   ├── commands
│   │   └── fix-github-issue.md
│   └── settings.json
├── .coveragerc
├── .dockerignore
├── .env.example
├── .gitattributes
├── .github
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── documentation.yml
│   │   ├── feature_request.yml
│   │   └── tool_addition.yml
│   ├── pull_request_template.md
│   └── workflows
│       ├── docker-pr.yml
│       ├── docker-release.yml
│       ├── semantic-pr.yml
│       ├── semantic-release.yml
│       └── test.yml
├── .gitignore
├── .pre-commit-config.yaml
├── AGENTS.md
├── CHANGELOG.md
├── claude_config_example.json
├── CLAUDE.md
├── clink
│   ├── __init__.py
│   ├── agents
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── claude.py
│   │   ├── codex.py
│   │   └── gemini.py
│   ├── constants.py
│   ├── models.py
│   ├── parsers
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── claude.py
│   │   ├── codex.py
│   │   └── gemini.py
│   └── registry.py
├── code_quality_checks.ps1
├── code_quality_checks.sh
├── communication_simulator_test.py
├── conf
│   ├── __init__.py
│   ├── azure_models.json
│   ├── cli_clients
│   │   ├── claude.json
│   │   ├── codex.json
│   │   └── gemini.json
│   ├── custom_models.json
│   ├── dial_models.json
│   ├── gemini_models.json
│   ├── openai_models.json
│   ├── openrouter_models.json
│   └── xai_models.json
├── config.py
├── docker
│   ├── README.md
│   └── scripts
│       ├── build.ps1
│       ├── build.sh
│       ├── deploy.ps1
│       ├── deploy.sh
│       └── healthcheck.py
├── docker-compose.yml
├── Dockerfile
├── docs
│   ├── adding_providers.md
│   ├── adding_tools.md
│   ├── advanced-usage.md
│   ├── ai_banter.md
│   ├── ai-collaboration.md
│   ├── azure_openai.md
│   ├── configuration.md
│   ├── context-revival.md
│   ├── contributions.md
│   ├── custom_models.md
│   ├── docker-deployment.md
│   ├── gemini-setup.md
│   ├── getting-started.md
│   ├── index.md
│   ├── locale-configuration.md
│   ├── logging.md
│   ├── model_ranking.md
│   ├── testing.md
│   ├── tools
│   │   ├── analyze.md
│   │   ├── apilookup.md
│   │   ├── challenge.md
│   │   ├── chat.md
│   │   ├── clink.md
│   │   ├── codereview.md
│   │   ├── consensus.md
│   │   ├── debug.md
│   │   ├── docgen.md
│   │   ├── listmodels.md
│   │   ├── planner.md
│   │   ├── precommit.md
│   │   ├── refactor.md
│   │   ├── secaudit.md
│   │   ├── testgen.md
│   │   ├── thinkdeep.md
│   │   ├── tracer.md
│   │   └── version.md
│   ├── troubleshooting.md
│   ├── vcr-testing.md
│   └── wsl-setup.md
├── examples
│   ├── claude_config_macos.json
│   └── claude_config_wsl.json
├── LICENSE
├── providers
│   ├── __init__.py
│   ├── azure_openai.py
│   ├── base.py
│   ├── custom.py
│   ├── dial.py
│   ├── gemini.py
│   ├── openai_compatible.py
│   ├── openai.py
│   ├── openrouter.py
│   ├── registries
│   │   ├── __init__.py
│   │   ├── azure.py
│   │   ├── base.py
│   │   ├── custom.py
│   │   ├── dial.py
│   │   ├── gemini.py
│   │   ├── openai.py
│   │   ├── openrouter.py
│   │   └── xai.py
│   ├── registry_provider_mixin.py
│   ├── registry.py
│   ├── shared
│   │   ├── __init__.py
│   │   ├── model_capabilities.py
│   │   ├── model_response.py
│   │   ├── provider_type.py
│   │   └── temperature.py
│   └── xai.py
├── pyproject.toml
├── pytest.ini
├── README.md
├── requirements-dev.txt
├── requirements.txt
├── run_integration_tests.ps1
├── run_integration_tests.sh
├── run-server.ps1
├── run-server.sh
├── scripts
│   └── sync_version.py
├── server.py
├── simulator_tests
│   ├── __init__.py
│   ├── base_test.py
│   ├── conversation_base_test.py
│   ├── log_utils.py
│   ├── test_analyze_validation.py
│   ├── test_basic_conversation.py
│   ├── test_chat_simple_validation.py
│   ├── test_codereview_validation.py
│   ├── test_consensus_conversation.py
│   ├── test_consensus_three_models.py
│   ├── test_consensus_workflow_accurate.py
│   ├── test_content_validation.py
│   ├── test_conversation_chain_validation.py
│   ├── test_cross_tool_comprehensive.py
│   ├── test_cross_tool_continuation.py
│   ├── test_debug_certain_confidence.py
│   ├── test_debug_validation.py
│   ├── test_line_number_validation.py
│   ├── test_logs_validation.py
│   ├── test_model_thinking_config.py
│   ├── test_o3_model_selection.py
│   ├── test_o3_pro_expensive.py
│   ├── test_ollama_custom_url.py
│   ├── test_openrouter_fallback.py
│   ├── test_openrouter_models.py
│   ├── test_per_tool_deduplication.py
│   ├── test_planner_continuation_history.py
│   ├── test_planner_validation_old.py
│   ├── test_planner_validation.py
│   ├── test_precommitworkflow_validation.py
│   ├── test_prompt_size_limit_bug.py
│   ├── test_refactor_validation.py
│   ├── test_secaudit_validation.py
│   ├── test_testgen_validation.py
│   ├── test_thinkdeep_validation.py
│   ├── test_token_allocation_validation.py
│   ├── test_vision_capability.py
│   └── test_xai_models.py
├── systemprompts
│   ├── __init__.py
│   ├── analyze_prompt.py
│   ├── chat_prompt.py
│   ├── clink
│   │   ├── codex_codereviewer.txt
│   │   ├── default_codereviewer.txt
│   │   ├── default_planner.txt
│   │   └── default.txt
│   ├── codereview_prompt.py
│   ├── consensus_prompt.py
│   ├── debug_prompt.py
│   ├── docgen_prompt.py
│   ├── generate_code_prompt.py
│   ├── planner_prompt.py
│   ├── precommit_prompt.py
│   ├── refactor_prompt.py
│   ├── secaudit_prompt.py
│   ├── testgen_prompt.py
│   ├── thinkdeep_prompt.py
│   └── tracer_prompt.py
├── tests
│   ├── __init__.py
│   ├── CASSETTE_MAINTENANCE.md
│   ├── conftest.py
│   ├── gemini_cassettes
│   │   ├── chat_codegen
│   │   │   └── gemini25_pro_calculator
│   │   │       └── mldev.json
│   │   ├── chat_cross
│   │   │   └── step1_gemini25_flash_number
│   │   │       └── mldev.json
│   │   └── consensus
│   │       └── step2_gemini25_flash_against
│   │           └── mldev.json
│   ├── http_transport_recorder.py
│   ├── mock_helpers.py
│   ├── openai_cassettes
│   │   ├── chat_cross_step2_gpt5_reminder.json
│   │   ├── chat_gpt5_continuation.json
│   │   ├── chat_gpt5_moon_distance.json
│   │   ├── consensus_step1_gpt5_for.json
│   │   └── o3_pro_basic_math.json
│   ├── pii_sanitizer.py
│   ├── sanitize_cassettes.py
│   ├── test_alias_target_restrictions.py
│   ├── test_auto_mode_comprehensive.py
│   ├── test_auto_mode_custom_provider_only.py
│   ├── test_auto_mode_model_listing.py
│   ├── test_auto_mode_provider_selection.py
│   ├── test_auto_mode.py
│   ├── test_auto_model_planner_fix.py
│   ├── test_azure_openai_provider.py
│   ├── test_buggy_behavior_prevention.py
│   ├── test_cassette_semantic_matching.py
│   ├── test_challenge.py
│   ├── test_chat_codegen_integration.py
│   ├── test_chat_cross_model_continuation.py
│   ├── test_chat_openai_integration.py
│   ├── test_chat_simple.py
│   ├── test_clink_claude_agent.py
│   ├── test_clink_claude_parser.py
│   ├── test_clink_codex_agent.py
│   ├── test_clink_gemini_agent.py
│   ├── test_clink_gemini_parser.py
│   ├── test_clink_integration.py
│   ├── test_clink_parsers.py
│   ├── test_clink_tool.py
│   ├── test_collaboration.py
│   ├── test_config.py
│   ├── test_consensus_integration.py
│   ├── test_consensus_schema.py
│   ├── test_consensus.py
│   ├── test_conversation_continuation_integration.py
│   ├── test_conversation_field_mapping.py
│   ├── test_conversation_file_features.py
│   ├── test_conversation_memory.py
│   ├── test_conversation_missing_files.py
│   ├── test_custom_openai_temperature_fix.py
│   ├── test_custom_provider.py
│   ├── test_debug.py
│   ├── test_deploy_scripts.py
│   ├── test_dial_provider.py
│   ├── test_directory_expansion_tracking.py
│   ├── test_disabled_tools.py
│   ├── test_docker_claude_desktop_integration.py
│   ├── test_docker_config_complete.py
│   ├── test_docker_healthcheck.py
│   ├── test_docker_implementation.py
│   ├── test_docker_mcp_validation.py
│   ├── test_docker_security.py
│   ├── test_docker_volume_persistence.py
│   ├── test_file_protection.py
│   ├── test_gemini_token_usage.py
│   ├── test_image_support_integration.py
│   ├── test_image_validation.py
│   ├── test_integration_utf8.py
│   ├── test_intelligent_fallback.py
│   ├── test_issue_245_simple.py
│   ├── test_large_prompt_handling.py
│   ├── test_line_numbers_integration.py
│   ├── test_listmodels_restrictions.py
│   ├── test_listmodels.py
│   ├── test_mcp_error_handling.py
│   ├── test_model_enumeration.py
│   ├── test_model_metadata_continuation.py
│   ├── test_model_resolution_bug.py
│   ├── test_model_restrictions.py
│   ├── test_o3_pro_output_text_fix.py
│   ├── test_o3_temperature_fix_simple.py
│   ├── test_openai_compatible_token_usage.py
│   ├── test_openai_provider.py
│   ├── test_openrouter_provider.py
│   ├── test_openrouter_registry.py
│   ├── test_parse_model_option.py
│   ├── test_per_tool_model_defaults.py
│   ├── test_pii_sanitizer.py
│   ├── test_pip_detection_fix.py
│   ├── test_planner.py
│   ├── test_precommit_workflow.py
│   ├── test_prompt_regression.py
│   ├── test_prompt_size_limit_bug_fix.py
│   ├── test_provider_retry_logic.py
│   ├── test_provider_routing_bugs.py
│   ├── test_provider_utf8.py
│   ├── test_providers.py
│   ├── test_rate_limit_patterns.py
│   ├── test_refactor.py
│   ├── test_secaudit.py
│   ├── test_server.py
│   ├── test_supported_models_aliases.py
│   ├── test_thinking_modes.py
│   ├── test_tools.py
│   ├── test_tracer.py
│   ├── test_utf8_localization.py
│   ├── test_utils.py
│   ├── test_uvx_resource_packaging.py
│   ├── test_uvx_support.py
│   ├── test_workflow_file_embedding.py
│   ├── test_workflow_metadata.py
│   ├── test_workflow_prompt_size_validation_simple.py
│   ├── test_workflow_utf8.py
│   ├── test_xai_provider.py
│   ├── transport_helpers.py
│   └── triangle.png
├── tools
│   ├── __init__.py
│   ├── analyze.py
│   ├── apilookup.py
│   ├── challenge.py
│   ├── chat.py
│   ├── clink.py
│   ├── codereview.py
│   ├── consensus.py
│   ├── debug.py
│   ├── docgen.py
│   ├── listmodels.py
│   ├── models.py
│   ├── planner.py
│   ├── precommit.py
│   ├── refactor.py
│   ├── secaudit.py
│   ├── shared
│   │   ├── __init__.py
│   │   ├── base_models.py
│   │   ├── base_tool.py
│   │   ├── exceptions.py
│   │   └── schema_builders.py
│   ├── simple
│   │   ├── __init__.py
│   │   └── base.py
│   ├── testgen.py
│   ├── thinkdeep.py
│   ├── tracer.py
│   ├── version.py
│   └── workflow
│       ├── __init__.py
│       ├── base.py
│       ├── schema_builders.py
│       └── workflow_mixin.py
├── utils
│   ├── __init__.py
│   ├── client_info.py
│   ├── conversation_memory.py
│   ├── env.py
│   ├── file_types.py
│   ├── file_utils.py
│   ├── image_utils.py
│   ├── model_context.py
│   ├── model_restrictions.py
│   ├── security_config.py
│   ├── storage_backend.py
│   └── token_utils.py
└── zen-mcp-server
```
# Files
--------------------------------------------------------------------------------
/run-server.sh:
--------------------------------------------------------------------------------
```bash
   1 | #!/bin/bash
   2 | set -euo pipefail
   3 | 
   4 | # ============================================================================
   5 | # Zen MCP Server Setup Script
   6 | #
   7 | # A platform-agnostic setup script that works on macOS, Linux, and WSL.
   8 | # Handles environment setup, dependency installation, and configuration.
   9 | # ============================================================================
  10 | 
  11 | # Initialize pyenv if available (do this early)
  12 | if [[ -d "$HOME/.pyenv" ]]; then
  13 |     export PYENV_ROOT="$HOME/.pyenv"
  14 |     export PATH="$PYENV_ROOT/bin:$PATH"
  15 |     if command -v pyenv &> /dev/null; then
  16 |         eval "$(pyenv init --path)" 2>/dev/null || true
  17 |         eval "$(pyenv init -)" 2>/dev/null || true
  18 |     fi
  19 | fi
  20 | 
  21 | # ----------------------------------------------------------------------------
  22 | # Constants and Configuration
  23 | # ----------------------------------------------------------------------------
  24 | 
  25 | # Colors for output (ANSI codes work on all platforms)
  26 | readonly GREEN='\033[0;32m'
  27 | readonly YELLOW='\033[1;33m'
  28 | readonly RED='\033[0;31m'
  29 | readonly NC='\033[0m' # No Color
  30 | 
  31 | # Configuration
  32 | readonly VENV_PATH=".zen_venv"
  33 | readonly DOCKER_CLEANED_FLAG=".docker_cleaned"
  34 | readonly DESKTOP_CONFIG_FLAG=".desktop_configured"
  35 | readonly LOG_DIR="logs"
  36 | readonly LOG_FILE="mcp_server.log"
  37 | 
  38 | # Determine portable arguments for sed -i (GNU vs BSD)
  39 | declare -a SED_INPLACE_ARGS
  40 | if sed --version >/dev/null 2>&1; then
  41 |     SED_INPLACE_ARGS=(-i)
  42 | else
  43 |     SED_INPLACE_ARGS=(-i "")
  44 | fi
  45 | 
  46 | # ----------------------------------------------------------------------------
  47 | # Utility Functions
  48 | # ----------------------------------------------------------------------------
  49 | 
  50 | # Print colored output
  51 | print_success() {
  52 |     echo -e "${GREEN}✓${NC} $1" >&2
  53 | }
  54 | 
  55 | print_error() {
  56 |     echo -e "${RED}✗${NC} $1" >&2
  57 | }
  58 | 
  59 | print_warning() {
  60 |     echo -e "${YELLOW}!${NC} $1" >&2
  61 | }
  62 | 
  63 | print_info() {
  64 |     echo -e "${YELLOW}$1${NC}" >&2
  65 | }
  66 | 
  67 | # Get the script's directory (works on all platforms)
  68 | get_script_dir() {
  69 |     cd "$(dirname "$0")" && pwd
  70 | }
  71 | 
  72 | # Extract version from config.py
  73 | get_version() {
  74 |     grep -E '^__version__ = ' config.py 2>/dev/null | sed 's/__version__ = "\(.*\)"/\1/' || echo "unknown"
  75 | }
  76 | 
  77 | # Clear Python cache files to prevent import issues
  78 | clear_python_cache() {
  79 |     print_info "Clearing Python cache files..."
  80 |     find . -name "*.pyc" -delete 2>/dev/null || true
  81 |     find . -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
  82 |     print_success "Python cache cleared"
  83 | }
  84 | 
  85 | # ----------------------------------------------------------------------------
  86 | # Platform Detection Functions
  87 | # ----------------------------------------------------------------------------
  88 | 
  89 | # Get cross-platform Python executable path from venv
  90 | get_venv_python_path() {
  91 |     local venv_path="$1"
  92 |     
  93 |     # Convert to absolute path for consistent behavior across shell environments
  94 |     local abs_venv_path
  95 |     abs_venv_path=$(cd "$(dirname "$venv_path")" && pwd)/$(basename "$venv_path")
  96 | 
  97 |     # Check for both Unix and Windows Python executable paths
  98 |     if [[ -f "$abs_venv_path/bin/python" ]]; then
  99 |         echo "$abs_venv_path/bin/python"
 100 |     elif [[ -f "$abs_venv_path/Scripts/python.exe" ]]; then
 101 |         echo "$abs_venv_path/Scripts/python.exe"
 102 |     else
 103 |         return 1  # No Python executable found
 104 |     fi
 105 | }
 106 | 
 107 | # Detect the operating system
 108 | detect_os() {
 109 |     case "$OSTYPE" in
 110 |         darwin*)  echo "macos" ;;
 111 |         linux*)
 112 |             if grep -qi microsoft /proc/version 2>/dev/null; then
 113 |                 echo "wsl"
 114 |             else
 115 |                 echo "linux"
 116 |             fi
 117 |             ;;
 118 |         msys*|cygwin*|win32) echo "windows" ;;
 119 |         *)        echo "unknown" ;;
 120 |     esac
 121 | }
 122 | 
 123 | # Get Claude config path based on platform
 124 | get_claude_config_path() {
 125 |     local os_type=$(detect_os)
 126 | 
 127 |     case "$os_type" in
 128 |         macos)
 129 |             echo "$HOME/Library/Application Support/Claude/claude_desktop_config.json"
 130 |             ;;
 131 |         linux)
 132 |             echo "$HOME/.config/Claude/claude_desktop_config.json"
 133 |             ;;
 134 |         wsl)
 135 |             local win_appdata
 136 |             if command -v wslvar &> /dev/null; then
 137 |                 win_appdata=$(wslvar APPDATA 2>/dev/null)
 138 |             fi
 139 | 
 140 |             if [[ -n "${win_appdata:-}" ]]; then
 141 |                 echo "$(wslpath "$win_appdata")/Claude/claude_desktop_config.json"
 142 |             else
 143 |                 print_warning "Could not determine Windows user path automatically. Please ensure APPDATA is set correctly or provide the full path manually."
 144 |                 echo "/mnt/c/Users/$USER/AppData/Roaming/Claude/claude_desktop_config.json"
 145 |             fi
 146 |             ;;
 147 |         windows)
 148 |             echo "$APPDATA/Claude/claude_desktop_config.json"
 149 |             ;;
 150 |         *)
 151 |             echo ""
 152 |             ;;
 153 |     esac
 154 | }
 155 | 
 156 | # ----------------------------------------------------------------------------
 157 | # Docker Cleanup Functions
 158 | # ----------------------------------------------------------------------------
 159 | 
 160 | # Clean up old Docker artifacts
 161 | cleanup_docker() {
 162 |     # Skip if already cleaned or Docker not available
 163 |     [[ -f "$DOCKER_CLEANED_FLAG" ]] && return 0
 164 | 
 165 |     if ! command -v docker &> /dev/null || ! docker info &> /dev/null 2>&1; then
 166 |         return 0
 167 |     fi
 168 | 
 169 |     local found_artifacts=false
 170 | 
 171 |     # Define containers to remove
 172 |     local containers=(
 173 |         "gemini-mcp-server"
 174 |         "gemini-mcp-redis"
 175 |         "zen-mcp-server"
 176 |         "zen-mcp-redis"
 177 |         "zen-mcp-log-monitor"
 178 |     )
 179 | 
 180 |     # Remove containers
 181 |     for container in "${containers[@]}"; do
 182 |         if docker ps -a --format "{{.Names}}" | grep -q "^${container}$" 2>/dev/null; then
 183 |             if [[ "$found_artifacts" == false ]]; then
 184 |                 echo "One-time Docker cleanup..."
 185 |                 found_artifacts=true
 186 |             fi
 187 |             echo "  Removing container: $container"
 188 |             docker stop "$container" >/dev/null 2>&1 || true
 189 |             docker rm "$container" >/dev/null 2>&1 || true
 190 |         fi
 191 |     done
 192 | 
 193 |     # Remove images
 194 |     local images=("gemini-mcp-server:latest" "zen-mcp-server:latest")
 195 |     for image in "${images[@]}"; do
 196 |         if docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^${image}$" 2>/dev/null; then
 197 |             if [[ "$found_artifacts" == false ]]; then
 198 |                 echo "One-time Docker cleanup..."
 199 |                 found_artifacts=true
 200 |             fi
 201 |             echo "  Removing image: $image"
 202 |             docker rmi "$image" >/dev/null 2>&1 || true
 203 |         fi
 204 |     done
 205 | 
 206 |     # Remove volumes
 207 |     local volumes=("redis_data" "mcp_logs")
 208 |     for volume in "${volumes[@]}"; do
 209 |         if docker volume ls --format "{{.Name}}" | grep -q "^${volume}$" 2>/dev/null; then
 210 |             if [[ "$found_artifacts" == false ]]; then
 211 |                 echo "One-time Docker cleanup..."
 212 |                 found_artifacts=true
 213 |             fi
 214 |             echo "  Removing volume: $volume"
 215 |             docker volume rm "$volume" >/dev/null 2>&1 || true
 216 |         fi
 217 |     done
 218 | 
 219 |     if [[ "$found_artifacts" == true ]]; then
 220 |         print_success "Docker cleanup complete"
 221 |     fi
 222 | 
 223 |     touch "$DOCKER_CLEANED_FLAG"
 224 | }
 225 | 
 226 | # ----------------------------------------------------------------------------
 227 | # Python Environment Functions
 228 | # ----------------------------------------------------------------------------
 229 | 
 230 | # Find suitable Python command
 231 | find_python() {
 232 |     # Pyenv should already be initialized at script start, but check if .python-version exists
 233 |     if [[ -f ".python-version" ]] && command -v pyenv &> /dev/null; then
 234 |         # Ensure pyenv respects the local .python-version
 235 |         pyenv local &>/dev/null || true
 236 |     fi
 237 | 
 238 |     # Prefer Python 3.12 for best compatibility
 239 |     local python_cmds=("python3.12" "python3.13" "python3.11" "python3.10" "python3" "python" "py")
 240 | 
 241 |     for cmd in "${python_cmds[@]}"; do
 242 |         if command -v "$cmd" &> /dev/null; then
 243 |             local version=$($cmd --version 2>&1)
 244 |             if [[ $version =~ Python\ 3\.([0-9]+)\.([0-9]+) ]]; then
 245 |                 local major_version=${BASH_REMATCH[1]}
 246 |                 local minor_version=${BASH_REMATCH[2]}
 247 | 
 248 |                 # Check minimum version (3.10) for better library compatibility
 249 |                 if [[ $major_version -ge 10 ]]; then
 250 |                     # Verify the command actually exists (important for pyenv)
 251 |                     if command -v "$cmd" &> /dev/null; then
 252 |                         echo "$cmd"
 253 |                         print_success "Found Python: $version"
 254 | 
 255 |                         # Recommend Python 3.12
 256 |                         if [[ $major_version -ne 12 ]]; then
 257 |                             print_info "Note: Python 3.12 is recommended for best compatibility."
 258 |                         fi
 259 | 
 260 |                         return 0
 261 |                     fi
 262 |                 fi
 263 |             fi
 264 |         fi
 265 |     done
 266 | 
 267 |     # No suitable Python found - check if we can use pyenv
 268 |     local os_type=$(detect_os)
 269 | 
 270 |     # Check for pyenv on Unix-like systems (macOS/Linux)
 271 |     if [[ "$os_type" == "macos" || "$os_type" == "linux" || "$os_type" == "wsl" ]]; then
 272 |         if command -v pyenv &> /dev/null; then
 273 |             # pyenv exists, check if Python 3.12 is installed
 274 |             if ! pyenv versions 2>/dev/null | grep -E "3\.(1[2-9]|[2-9][0-9])" >/dev/null; then
 275 |                 echo ""
 276 |                 echo "Python 3.10+ is required. Pyenv can install Python 3.12 locally for this project."
 277 |                 read -p "Install Python 3.12 using pyenv? (Y/n): " -n 1 -r
 278 |                 echo ""
 279 |                 if [[ ! $REPLY =~ ^[Nn]$ ]]; then
 280 |                     if install_python_with_pyenv; then
 281 |                         # Try finding Python again
 282 |                         if python_cmd=$(find_python); then
 283 |                             echo "$python_cmd"
 284 |                             return 0
 285 |                         fi
 286 |                     fi
 287 |                 fi
 288 |             else
 289 |                 # Python 3.12+ is installed in pyenv but may not be active
 290 |                 # Check if .python-version exists
 291 |                 if [[ ! -f ".python-version" ]] || ! grep -qE "3\.(1[2-9]|[2-9][0-9])" .python-version 2>/dev/null; then
 292 |                     echo ""
 293 |                     print_info "Python 3.12 is installed via pyenv but not set for this project."
 294 |                     read -p "Set Python 3.12.0 for this project? (Y/n): " -n 1 -r
 295 |                     echo ""
 296 |                     if [[ ! $REPLY =~ ^[Nn]$ ]]; then
 297 |                         # Find the first suitable Python version
 298 |                         local py_version=$(pyenv versions --bare | grep -E "^3\.(1[2-9]|[2-9][0-9])" | head -1)
 299 |                         if [[ -n "$py_version" ]]; then
 300 |                             pyenv local "$py_version"
 301 |                             print_success "Set Python $py_version for this project"
 302 |                             # Re-initialize pyenv to pick up the change
 303 |                             eval "$(pyenv init --path)" 2>/dev/null || true
 304 |                             eval "$(pyenv init -)" 2>/dev/null || true
 305 |                             # Try finding Python again
 306 |                             if python_cmd=$(find_python); then
 307 |                                 echo "$python_cmd"
 308 |                                 return 0
 309 |                             fi
 310 |                         fi
 311 |                     fi
 312 |                 fi
 313 |             fi
 314 |         else
 315 |             # No pyenv installed - show instructions
 316 |             echo "" >&2
 317 |             print_error "Python 3.10+ not found. The 'mcp' package requires Python 3.10+."
 318 |             echo "" >&2
 319 | 
 320 |             if [[ "$os_type" == "macos" ]]; then
 321 |                 echo "To install Python locally for this project:" >&2
 322 |                 echo "" >&2
 323 |                 echo "1. Install pyenv (manages Python versions per project):" >&2
 324 |                 echo "   brew install pyenv" >&2
 325 |                 echo "" >&2
 326 |                 echo "2. Add to ~/.zshrc:" >&2
 327 |                 echo '   export PYENV_ROOT="$HOME/.pyenv"' >&2
 328 |                 echo '   export PATH="$PYENV_ROOT/bin:$PATH"' >&2
 329 |                 echo '   eval "$(pyenv init -)"' >&2
 330 |                 echo "" >&2
 331 |                 echo "3. Restart terminal, then run:" >&2
 332 |                 echo "   pyenv install 3.12.0" >&2
 333 |                 echo "   cd $(pwd)" >&2
 334 |                 echo "   pyenv local 3.12.0" >&2
 335 |                 echo "   ./run-server.sh" >&2
 336 |             else
 337 |                 # Linux/WSL
 338 |                 echo "To install Python locally for this project:" >&2
 339 |                 echo "" >&2
 340 |                 echo "1. Install pyenv:" >&2
 341 |                 echo "   curl https://pyenv.run | bash" >&2
 342 |                 echo "" >&2
 343 |                 echo "2. Add to ~/.bashrc:" >&2
 344 |                 echo '   export PYENV_ROOT="$HOME/.pyenv"' >&2
 345 |                 echo '   export PATH="$PYENV_ROOT/bin:$PATH"' >&2
 346 |                 echo '   eval "$(pyenv init -)"' >&2
 347 |                 echo "" >&2
 348 |                 echo "3. Restart terminal, then run:" >&2
 349 |                 echo "   pyenv install 3.12.0" >&2
 350 |                 echo "   cd $(pwd)" >&2
 351 |                 echo "   pyenv local 3.12.0" >&2
 352 |                 echo "   ./run-server.sh" >&2
 353 |             fi
 354 |         fi
 355 |     else
 356 |         # Other systems (shouldn't happen with bash script)
 357 |         print_error "Python 3.10+ not found. Please install Python 3.10 or newer."
 358 |     fi
 359 | 
 360 |     return 1
 361 | }
 362 | 
 363 | # Install Python with pyenv (when pyenv is already installed)
 364 | install_python_with_pyenv() {
 365 |     # Ensure pyenv is initialized
 366 |     export PYENV_ROOT="${PYENV_ROOT:-$HOME/.pyenv}"
 367 |     export PATH="$PYENV_ROOT/bin:$PATH"
 368 |     eval "$(pyenv init -)" 2>/dev/null || true
 369 | 
 370 |     print_info "Installing Python 3.12 (this may take a few minutes)..."
 371 |     if pyenv install -s 3.12.0; then
 372 |         print_success "Python 3.12 installed"
 373 | 
 374 |         # Set local Python version for this project
 375 |         pyenv local 3.12.0
 376 |         print_success "Python 3.12 set for this project"
 377 | 
 378 |         # Show shell configuration instructions
 379 |         echo ""
 380 |         print_info "To make pyenv work in new terminals, add to your shell config:"
 381 |         local shell_config="~/.zshrc"
 382 |         if [[ "$SHELL" == *"bash"* ]]; then
 383 |             shell_config="~/.bashrc"
 384 |         fi
 385 |         echo '  export PYENV_ROOT="$HOME/.pyenv"'
 386 |         echo '  command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"'
 387 |         echo '  eval "$(pyenv init -)"'
 388 |         echo ""
 389 | 
 390 |         # Re-initialize pyenv to use the newly installed Python
 391 |         eval "$(pyenv init --path)" 2>/dev/null || true
 392 |         eval "$(pyenv init -)" 2>/dev/null || true
 393 | 
 394 |         return 0
 395 |     else
 396 |         print_error "Failed to install Python 3.12"
 397 |         return 1
 398 |     fi
 399 | }
 400 | 
 401 | # Detect Linux distribution
 402 | detect_linux_distro() {
 403 |     if [[ -f /etc/os-release ]]; then
 404 |         . /etc/os-release
 405 |         echo "${ID:-unknown}"
 406 |     elif [[ -f /etc/debian_version ]]; then
 407 |         echo "debian"
 408 |     elif [[ -f /etc/redhat-release ]]; then
 409 |         echo "rhel"
 410 |     elif [[ -f /etc/arch-release ]]; then
 411 |         echo "arch"
 412 |     else
 413 |         echo "unknown"
 414 |     fi
 415 | }
 416 | 
 417 | # Get package manager and install command for the distro
 418 | get_install_command() {
 419 |     local distro="$1"
 420 |     local python_version="${2:-}"
 421 | 
 422 |     # Extract major.minor version if provided
 423 |     local version_suffix=""
 424 |     if [[ -n "$python_version" ]] && [[ "$python_version" =~ ([0-9]+\.[0-9]+) ]]; then
 425 |         version_suffix="${BASH_REMATCH[1]}"
 426 |     fi
 427 | 
 428 |     case "$distro" in
 429 |         ubuntu|debian|raspbian|pop|linuxmint|elementary)
 430 |             if [[ -n "$version_suffix" ]]; then
 431 |                 # Try version-specific packages first, then fall back to generic
 432 |                 echo "sudo apt update && (sudo apt install -y python${version_suffix}-venv python${version_suffix}-dev || sudo apt install -y python3-venv python3-pip)"
 433 |             else
 434 |                 echo "sudo apt update && sudo apt install -y python3-venv python3-pip"
 435 |             fi
 436 |             ;;
 437 |         fedora)
 438 |             echo "sudo dnf install -y python3-venv python3-pip"
 439 |             ;;
 440 |         rhel|centos|rocky|almalinux|oracle)
 441 |             echo "sudo dnf install -y python3-venv python3-pip || sudo yum install -y python3-venv python3-pip"
 442 |             ;;
 443 |         arch|manjaro|endeavouros)
 444 |             echo "sudo pacman -Syu --noconfirm python-pip python-virtualenv"
 445 |             ;;
 446 |         opensuse|suse)
 447 |             echo "sudo zypper install -y python3-venv python3-pip"
 448 |             ;;
 449 |         alpine)
 450 |             echo "sudo apk add --no-cache python3-dev py3-pip py3-virtualenv"
 451 |             ;;
 452 |         *)
 453 |             echo ""
 454 |             ;;
 455 |     esac
 456 | }
 457 | 
 458 | # Check if we can use sudo
 459 | can_use_sudo() {
 460 |     # Check if sudo exists and user can use it
 461 |     if command -v sudo &> /dev/null; then
 462 |         # Test sudo with a harmless command
 463 |         if sudo -n true 2>/dev/null; then
 464 |             return 0
 465 |         elif [[ -t 0 ]]; then
 466 |             # Terminal is interactive, test if sudo works with password
 467 |             if sudo true 2>/dev/null; then
 468 |                 return 0
 469 |             fi
 470 |         fi
 471 |     fi
 472 |     return 1
 473 | }
 474 | 
 475 | # Try to install system packages automatically
 476 | try_install_system_packages() {
 477 |     local python_cmd="${1:-python3}"
 478 |     local os_type=$(detect_os)
 479 | 
 480 |     # Skip on macOS as it works fine
 481 |     if [[ "$os_type" == "macos" ]]; then
 482 |         return 1
 483 |     fi
 484 | 
 485 |     # Only try on Linux systems
 486 |     if [[ "$os_type" != "linux" && "$os_type" != "wsl" ]]; then
 487 |         return 1
 488 |     fi
 489 | 
 490 |     # Get Python version
 491 |     local python_version=""
 492 |     if command -v "$python_cmd" &> /dev/null; then
 493 |         python_version=$($python_cmd --version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "")
 494 |     fi
 495 | 
 496 |     local distro=$(detect_linux_distro)
 497 |     local install_cmd=$(get_install_command "$distro" "$python_version")
 498 | 
 499 |     if [[ -z "$install_cmd" ]]; then
 500 |         return 1
 501 |     fi
 502 | 
 503 |     print_info "Attempting to install required Python packages..."
 504 | 
 505 |     # Check if we can use sudo
 506 |     if can_use_sudo; then
 507 |         print_info "Installing system packages (this may ask for your password)..."
 508 |         if bash -c "$install_cmd" >/dev/null 2>&1; then  # Replaced eval to prevent command injection
 509 |             print_success "System packages installed successfully"
 510 |             return 0
 511 |         else
 512 |             print_warning "Failed to install system packages automatically"
 513 |         fi
 514 |     fi
 515 | 
 516 |     return 1
 517 | }
 518 | 
 519 | # Bootstrap pip in virtual environment
 520 | bootstrap_pip() {
 521 |     local venv_python="$1"
 522 |     local python_cmd="$2"
 523 | 
 524 |     print_info "Bootstrapping pip in virtual environment..."
 525 | 
 526 |     # Try ensurepip first
 527 |     if $venv_python -m ensurepip --default-pip >/dev/null 2>&1; then
 528 |         print_success "Successfully bootstrapped pip using ensurepip"
 529 |         return 0
 530 |     fi
 531 | 
 532 |     # Try to download get-pip.py
 533 |     print_info "Downloading pip installer..."
 534 |     local get_pip_url="https://bootstrap.pypa.io/get-pip.py"
 535 |     local temp_pip=$(mktemp)
 536 |     local download_success=false
 537 | 
 538 |     # Try curl first
 539 |     if command -v curl &> /dev/null; then
 540 |         if curl -sSL "$get_pip_url" -o "$temp_pip" 2>/dev/null; then
 541 |             download_success=true
 542 |         fi
 543 |     fi
 544 | 
 545 |     # Try wget if curl failed
 546 |     if [[ "$download_success" == false ]] && command -v wget &> /dev/null; then
 547 |         if wget -qO "$temp_pip" "$get_pip_url" 2>/dev/null; then
 548 |             download_success=true
 549 |         fi
 550 |     fi
 551 | 
 552 |     # Try python urllib as last resort
 553 |     if [[ "$download_success" == false ]]; then
 554 |         print_info "Using Python to download pip installer..."
 555 |         if $python_cmd -c "import urllib.request; urllib.request.urlretrieve('$get_pip_url', '$temp_pip')" 2>/dev/null; then
 556 |             download_success=true
 557 |         fi
 558 |     fi
 559 | 
 560 |     if [[ "$download_success" == true ]] && [[ -f "$temp_pip" ]] && [[ -s "$temp_pip" ]]; then
 561 |         print_info "Installing pip..."
 562 |         if $venv_python "$temp_pip" --no-warn-script-location >/dev/null 2>&1; then
 563 |             rm -f "$temp_pip"
 564 |             print_success "Successfully installed pip"
 565 |             return 0
 566 |         fi
 567 |     fi
 568 | 
 569 |     rm -f "$temp_pip" 2>/dev/null
 570 |     return 1
 571 | }
 572 | 
 573 | # Setup environment using uv-first approach
 574 | setup_environment() {
 575 |     local venv_python=""
 576 | 
 577 |     # Try uv-first approach
 578 |     if command -v uv &> /dev/null; then
 579 |         print_info "Setting up environment with uv..."
 580 | 
 581 |         # Only remove existing venv if it wasn't created by uv (to ensure clean uv setup)
 582 |         if [[ -d "$VENV_PATH" ]] && [[ ! -f "$VENV_PATH/uv_created" ]]; then
 583 |             print_info "Removing existing environment for clean uv setup..."
 584 |             rm -rf "$VENV_PATH"
 585 |         fi
 586 | 
 587 |         # Try Python 3.12 first (preferred)
 588 |         local uv_output
 589 |         if uv_output=$(uv venv --python 3.12 "$VENV_PATH" 2>&1); then
 590 |             # Use helper function for cross-platform path detection
 591 |             if venv_python=$(get_venv_python_path "$VENV_PATH"); then
 592 |                 touch "$VENV_PATH/uv_created"  # Mark as uv-created
 593 |                 print_success "Created environment with uv using Python 3.12"
 594 | 
 595 |                 # Ensure pip is installed in uv environment
 596 |                 if ! $venv_python -m pip --version &>/dev/null 2>&1; then
 597 |                     print_info "Installing pip in uv environment..."
 598 |                     # uv doesn't install pip by default, use bootstrap method
 599 |                     if bootstrap_pip "$venv_python" "python3"; then
 600 |                         print_success "pip installed in uv environment"
 601 |                     else
 602 |                         print_warning "Failed to install pip in uv environment"
 603 |                     fi
 604 |                 fi
 605 |             else
 606 |                 print_warning "uv succeeded but Python executable not found in venv"
 607 |             fi
 608 |         # Fall back to any available Python through uv
 609 |         elif uv_output=$(uv venv "$VENV_PATH" 2>&1); then
 610 |             # Use helper function for cross-platform path detection
 611 |             if venv_python=$(get_venv_python_path "$VENV_PATH"); then
 612 |                 touch "$VENV_PATH/uv_created"  # Mark as uv-created
 613 |                 local python_version=$($venv_python --version 2>&1)
 614 |                 print_success "Created environment with uv using $python_version"
 615 | 
 616 |                 # Ensure pip is installed in uv environment
 617 |                 if ! $venv_python -m pip --version &>/dev/null 2>&1; then
 618 |                     print_info "Installing pip in uv environment..."
 619 |                     # uv doesn't install pip by default, use bootstrap method
 620 |                     if bootstrap_pip "$venv_python" "python3"; then
 621 |                         print_success "pip installed in uv environment"
 622 |                     else
 623 |                         print_warning "Failed to install pip in uv environment"
 624 |                     fi
 625 |                 fi
 626 |             else
 627 |                 print_warning "uv succeeded but Python executable not found in venv"
 628 |             fi
 629 |         else
 630 |             print_warning "uv environment creation failed, falling back to system Python detection"
 631 |             print_warning "uv output: $uv_output"
 632 |         fi
 633 |     else
 634 |         print_info "uv not found, using system Python detection"
 635 |     fi
 636 | 
 637 |     # If uv failed or not available, fallback to system Python detection
 638 |     if [[ -z "$venv_python" ]]; then
 639 |         print_info "Setting up environment with system Python..."
 640 |         local python_cmd
 641 |         python_cmd=$(find_python) || return 1
 642 | 
 643 |         # Use existing venv creation logic
 644 |         venv_python=$(setup_venv "$python_cmd")
 645 |         if [[ $? -ne 0 ]]; then
 646 |             return 1
 647 |         fi
 648 |     else
 649 |         # venv_python was already set by uv creation above, just convert to absolute path
 650 |         if [[ -n "$venv_python" ]]; then
 651 |             # Convert to absolute path for MCP registration
 652 |             local abs_venv_python
 653 |             if cd "$(dirname "$venv_python")" 2>/dev/null; then
 654 |                 abs_venv_python=$(pwd)/$(basename "$venv_python")
 655 |                 venv_python="$abs_venv_python"
 656 |             else
 657 |                 print_error "Failed to resolve absolute path for venv_python"
 658 |                 return 1
 659 |             fi
 660 |         fi
 661 |     fi
 662 | 
 663 |     echo "$venv_python"
 664 |     return 0
 665 | }
 666 | 
 667 | # Setup virtual environment
 668 | setup_venv() {
 669 |     local python_cmd="$1"
 670 |     local venv_python=""
 671 |     local venv_pip=""
 672 | 
 673 |     # Create venv if it doesn't exist
 674 |     if [[ ! -d "$VENV_PATH" ]]; then
 675 |         print_info "Creating isolated environment..."
 676 | 
 677 |         # Capture error output for better diagnostics
 678 |         local venv_error
 679 |         if venv_error=$($python_cmd -m venv "$VENV_PATH" 2>&1); then
 680 |             print_success "Created isolated environment"
 681 |         else
 682 |             # Check for common Linux issues and try fallbacks
 683 |             local os_type=$(detect_os)
 684 |             if [[ "$os_type" == "linux" || "$os_type" == "wsl" ]]; then
 685 |                 if echo "$venv_error" | grep -E -q "No module named venv|venv.*not found|ensurepip is not|python3.*-venv"; then
 686 |                     # Try to install system packages automatically first
 687 |                     if try_install_system_packages "$python_cmd"; then
 688 |                         print_info "Retrying virtual environment creation..."
 689 |                         if venv_error=$($python_cmd -m venv "$VENV_PATH" 2>&1); then
 690 |                             print_success "Created isolated environment"
 691 |                         else
 692 |                             # Continue to fallback methods below
 693 |                             print_warning "Still unable to create venv, trying fallback methods..."
 694 |                         fi
 695 |                     fi
 696 | 
 697 |                     # If venv still doesn't exist, try fallback methods
 698 |                     if [[ ! -d "$VENV_PATH" ]]; then
 699 |                         # Try virtualenv as fallback
 700 |                         if command -v virtualenv &> /dev/null; then
 701 |                             print_info "Attempting to create environment with virtualenv..."
 702 |                             if virtualenv -p "$python_cmd" "$VENV_PATH" &>/dev/null 2>&1; then
 703 |                                 print_success "Created environment using virtualenv fallback"
 704 |                             fi
 705 |                         fi
 706 | 
 707 |                         # Try python -m virtualenv if directory wasn't created
 708 |                         if [[ ! -d "$VENV_PATH" ]]; then
 709 |                             if $python_cmd -m virtualenv "$VENV_PATH" &>/dev/null 2>&1; then
 710 |                                 print_success "Created environment using python -m virtualenv fallback"
 711 |                             fi
 712 |                         fi
 713 | 
 714 |                         # Last resort: try to install virtualenv via pip and use it
 715 |                         if [[ ! -d "$VENV_PATH" ]] && command -v pip3 &> /dev/null; then
 716 |                             print_info "Installing virtualenv via pip..."
 717 |                             if pip3 install --user virtualenv &>/dev/null 2>&1; then
 718 |                                 local user_bin="$HOME/.local/bin"
 719 |                                 if [[ -f "$user_bin/virtualenv" ]]; then
 720 |                                     if "$user_bin/virtualenv" -p "$python_cmd" "$VENV_PATH" &>/dev/null 2>&1; then
 721 |                                         print_success "Created environment using pip-installed virtualenv"
 722 |                                     fi
 723 |                                 fi
 724 |                             fi
 725 |                         fi
 726 |                     fi
 727 | 
 728 |                     # Check if any method succeeded
 729 |                     if [[ ! -d "$VENV_PATH" ]]; then
 730 |                         print_error "Unable to create virtual environment"
 731 |                         echo ""
 732 |                         echo "Your system is missing Python development packages."
 733 |                         echo ""
 734 | 
 735 |                         local distro=$(detect_linux_distro)
 736 |                         local python_version=$($python_cmd --version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "")
 737 |                         local install_cmd=$(get_install_command "$distro" "$python_version")
 738 | 
 739 |                         if [[ -n "$install_cmd" ]]; then
 740 |                             echo "Please run this command to install them:"
 741 |                             echo "  $install_cmd"
 742 |                         else
 743 |                             echo "Please install Python venv support for your system:"
 744 |                             echo "  Ubuntu/Debian: sudo apt install python3-venv python3-pip"
 745 |                             echo "  RHEL/CentOS:   sudo dnf install python3-venv python3-pip"
 746 |                             echo "  Arch:          sudo pacman -S python-pip python-virtualenv"
 747 |                         fi
 748 |                         echo ""
 749 |                         echo "Then run this script again."
 750 |                         exit 1
 751 |                     fi
 752 |                 elif echo "$venv_error" | grep -q "Permission denied"; then
 753 |                     print_error "Permission denied creating virtual environment"
 754 |                     echo ""
 755 |                     echo "Try running in a different directory:"
 756 |                     echo "  cd ~ && git clone <repository-url> && cd zen-mcp-server && ./run-server.sh"
 757 |                     echo ""
 758 |                     exit 1
 759 |                 else
 760 |                     print_error "Failed to create virtual environment"
 761 |                     echo "Error: $venv_error"
 762 |                     exit 1
 763 |                 fi
 764 |             else
 765 |                 # For non-Linux systems, show the error and exit
 766 |                 print_error "Failed to create virtual environment"
 767 |                 echo "Error: $venv_error"
 768 |                 exit 1
 769 |             fi
 770 |         fi
 771 |     fi
 772 | 
 773 |     # Get venv Python path based on platform
 774 |     local os_type=$(detect_os)
 775 |     case "$os_type" in
 776 |         windows)
 777 |             venv_python="$VENV_PATH/Scripts/python.exe"
 778 |             venv_pip="$VENV_PATH/Scripts/pip.exe"
 779 |             ;;
 780 |         *)
 781 |             venv_python="$VENV_PATH/bin/python"
 782 |             venv_pip="$VENV_PATH/bin/pip"
 783 |             ;;
 784 |     esac
 785 | 
 786 |     # Check if venv Python exists
 787 |     if [[ ! -f "$venv_python" ]]; then
 788 |         print_error "Virtual environment Python not found"
 789 |         exit 1
 790 |     fi
 791 | 
 792 |     # Always check if pip exists in the virtual environment (regardless of how it was created)
 793 |     if [[ ! -f "$venv_pip" ]] && ! $venv_python -m pip --version &>/dev/null 2>&1; then
 794 |         print_warning "pip not found in virtual environment, installing..."
 795 | 
 796 |         # On Linux, try to install system packages if pip is missing
 797 |         local os_type=$(detect_os)
 798 |         if [[ "$os_type" == "linux" || "$os_type" == "wsl" ]]; then
 799 |             if try_install_system_packages "$python_cmd"; then
 800 |                 # Check if pip is now available after system package install
 801 |                 if $venv_python -m pip --version &>/dev/null 2>&1; then
 802 |                     print_success "pip is now available"
 803 |                 else
 804 |                     # Still need to bootstrap pip
 805 |                     bootstrap_pip "$venv_python" "$python_cmd" || true
 806 |                 fi
 807 |             else
 808 |                 # Try to bootstrap pip without system packages
 809 |                 bootstrap_pip "$venv_python" "$python_cmd" || true
 810 |             fi
 811 |         else
 812 |             # For non-Linux systems, just try to bootstrap pip
 813 |             bootstrap_pip "$venv_python" "$python_cmd" || true
 814 |         fi
 815 | 
 816 |         # Final check after all attempts
 817 |         if ! $venv_python -m pip --version &>/dev/null 2>&1; then
 818 |             print_error "Failed to install pip in virtual environment"
 819 |             echo ""
 820 |             echo "Your Python installation appears to be incomplete."
 821 |             echo ""
 822 | 
 823 |             local distro=$(detect_linux_distro)
 824 |             local python_version=$($python_cmd --version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "")
 825 |             local install_cmd=$(get_install_command "$distro" "$python_version")
 826 | 
 827 |             if [[ -n "$install_cmd" ]]; then
 828 |                 echo "Please run this command to install Python packages:"
 829 |                 echo "  $install_cmd"
 830 |             else
 831 |                 echo "Please install Python pip support for your system."
 832 |             fi
 833 |             echo ""
 834 |             echo "Then delete the virtual environment and run this script again:"
 835 |             echo "  rm -rf $VENV_PATH"
 836 |             echo "  ./run-server.sh"
 837 |             echo ""
 838 |             exit 1
 839 |         fi
 840 |     fi
 841 | 
 842 |     # Verify pip is working
 843 |     if ! $venv_python -m pip --version &>/dev/null 2>&1; then
 844 |         print_error "pip is not working correctly in the virtual environment"
 845 |         echo ""
 846 |         echo "Try deleting the virtual environment and running again:"
 847 |         echo "  rm -rf $VENV_PATH"
 848 |         echo "  ./run-server.sh"
 849 |         echo ""
 850 |         exit 1
 851 |     fi
 852 | 
 853 |     if [[ -n "${VIRTUAL_ENV:-}" ]]; then
 854 |         print_success "Using activated virtual environment with pip"
 855 |     else
 856 |         print_success "Virtual environment ready with pip"
 857 |     fi
 858 | 
 859 |     # Convert to absolute path for MCP registration
 860 |     local abs_venv_python=$(cd "$(dirname "$venv_python")" && pwd)/$(basename "$venv_python")
 861 |     echo "$abs_venv_python"
 862 |     return 0
 863 | }
 864 | 
 865 | # Check if package is installed
 866 | check_package() {
 867 |     local python_cmd="$1"
 868 |     local module_name="$2"
 869 |     "$python_cmd" -c "import importlib, sys; importlib.import_module(sys.argv[1])" "$module_name" &>/dev/null
 870 | }
 871 | 
 872 | # Install dependencies
 873 | install_dependencies() {
 874 |     local python_cmd="$1"
 875 |     local deps_needed=false
 876 | 
 877 |     # First verify pip is available with retry logic and bootstrap fallback
 878 |     local pip_available=false
 879 |     local max_attempts=3
 880 | 
 881 |     for ((attempt=1; attempt<=max_attempts; attempt++)); do
 882 |         if "$python_cmd" -m pip --version &>/dev/null; then
 883 |             pip_available=true
 884 |             break
 885 |         else
 886 |             if (( attempt < max_attempts )); then
 887 |                 print_warning "Attempt $attempt/$max_attempts: pip not available, retrying in 1 second..."
 888 |                 sleep 1
 889 |             fi
 890 |         fi
 891 |     done
 892 | 
 893 |     # If pip is still not available after retries, try to bootstrap it
 894 |     if [[ "$pip_available" == false ]]; then
 895 |         print_warning "pip is not available in the Python environment after $max_attempts attempts"
 896 |         
 897 |         # Enhanced diagnostic information for debugging
 898 |         print_info "Diagnostic information:"
 899 |         print_info "  Python executable: $python_cmd"
 900 |         print_info "  Python executable exists: $(if [[ -f "$python_cmd" ]]; then echo "Yes"; else echo "No"; fi)"
 901 |         print_info "  Python executable permissions: $(ls -la "$python_cmd" 2>/dev/null || echo "Cannot check")"
 902 |         print_info "  Virtual environment path: $VENV_PATH"
 903 |         print_info "  Virtual environment exists: $(if [[ -d "$VENV_PATH" ]]; then echo "Yes"; else echo "No"; fi)"
 904 |         
 905 |         print_info "Attempting to bootstrap pip..."
 906 | 
 907 |         # Extract the base python command for bootstrap (fallback to python3)
 908 |         local base_python_cmd="python3"
 909 |         if command -v python &> /dev/null; then
 910 |             base_python_cmd="python"
 911 |         fi
 912 | 
 913 |         # Try to bootstrap pip
 914 |         if bootstrap_pip "$python_cmd" "$base_python_cmd"; then
 915 |             print_success "Successfully bootstrapped pip"
 916 | 
 917 |             # Verify pip is now available
 918 |             if $python_cmd -m pip --version &>/dev/null 2>&1; then
 919 |                 pip_available=true
 920 |             else
 921 |                 print_error "pip still not available after bootstrap attempt"
 922 |             fi
 923 |         else
 924 |             print_error "Failed to bootstrap pip"
 925 |         fi
 926 |     fi
 927 | 
 928 |     # Final check - if pip is still not available, exit with error
 929 |     if [[ "$pip_available" == false ]]; then
 930 |         print_error "pip is not available in the Python environment"
 931 |         echo ""
 932 |         echo "This indicates an incomplete Python installation or a problem with the virtual environment."
 933 |         echo ""
 934 |         echo "Final diagnostic information:"
 935 |         echo "  Python executable: $python_cmd"
 936 |         echo "  Python version: $($python_cmd --version 2>&1 || echo "Cannot determine")"
 937 |         echo "  pip module check: $($python_cmd -c "import pip; print('Available')" 2>&1 || echo "Not available")"
 938 |         echo ""
 939 |         echo "Troubleshooting steps:"
 940 |         echo "1. Delete the virtual environment: rm -rf $VENV_PATH"
 941 |         echo "2. Run this script again: ./run-server.sh"
 942 |         echo "3. If the problem persists, check your Python installation"
 943 |         echo "4. For Git Bash on Windows, try running from a regular Command Prompt or PowerShell"
 944 |         echo ""
 945 |         return 1
 946 |     fi
 947 | 
 948 |     # Check required packages
 949 |     local packages=("mcp" "google.genai" "openai" "pydantic" "dotenv")
 950 |     for package in "${packages[@]}"; do
 951 |         if ! check_package "$python_cmd" "$package"; then
 952 |             deps_needed=true
 953 |             break
 954 |         fi
 955 |     done
 956 | 
 957 |     if [[ "$deps_needed" == false ]]; then
 958 |         print_success "Dependencies already installed"
 959 |         return 0
 960 |     fi
 961 | 
 962 |     echo ""
 963 |     print_info "Setting up Zen MCP Server..."
 964 |     echo "Installing required components:"
 965 |     echo "  • MCP protocol library"
 966 |     echo "  • AI model connectors"
 967 |     echo "  • Data validation tools"
 968 |     echo "  • Environment configuration"
 969 |     echo ""
 970 | 
 971 |     # Determine installation method and execute directly to handle paths with spaces
 972 |     local install_output
 973 |     local exit_code=0
 974 | 
 975 |     echo -n "Downloading packages..."
 976 | 
 977 |     if command -v uv &> /dev/null && [[ -f "$VENV_PATH/uv_created" ]]; then
 978 |         print_info "Using uv for faster package installation..."
 979 |         install_output=$(uv pip install -q -r requirements.txt --python "$python_cmd" 2>&1) || exit_code=$?
 980 |     elif [[ -n "${VIRTUAL_ENV:-}" ]] || [[ "$python_cmd" == *"$VENV_PATH"* ]]; then
 981 |         install_output=$("$python_cmd" -m pip install -q -r requirements.txt 2>&1) || exit_code=$?
 982 |     else
 983 |         install_output=$("$python_cmd" -m pip install -q --user -r requirements.txt 2>&1) || exit_code=$?
 984 |     fi
 985 | 
 986 |     if [[ $exit_code -ne 0 ]]; then
 987 |         echo -e "\r${RED}✗ Setup failed${NC}                      "
 988 |         echo ""
 989 |         echo "Installation error:"
 990 |         echo "$install_output" | head -20
 991 |         echo ""
 992 | 
 993 |         # Check for common issues
 994 |         if echo "$install_output" | grep -q "No module named pip"; then
 995 |             print_error "pip module not found"
 996 |             echo ""
 997 |             echo "Your Python installation is incomplete. Please install pip:"
 998 | 
 999 |             local distro=$(detect_linux_distro)
1000 |             local python_version=$($python_cmd --version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "")
1001 |             local install_cmd=$(get_install_command "$distro" "$python_version")
1002 | 
1003 |             if [[ -n "$install_cmd" ]]; then
1004 |                 echo ""
1005 |                 echo "For your system ($distro), run:"
1006 |                 echo "  $install_cmd"
1007 |             else
1008 |                 echo ""
1009 |                 echo "  Ubuntu/Debian: sudo apt install python3-pip"
1010 |                 echo "  RHEL/CentOS:   sudo dnf install python3-pip"
1011 |                 echo "  Arch:          sudo pacman -S python-pip"
1012 |             fi
1013 |         elif echo "$install_output" | grep -q "Permission denied"; then
1014 |             print_error "Permission denied during installation"
1015 |             echo ""
1016 |             echo "Try using a virtual environment or install with --user flag:"
1017 |             echo "  $python_cmd -m pip install --user -r requirements.txt"
1018 |         else
1019 |             echo "Try running manually:"
1020 |             if [[ "$use_uv" == true ]]; then
1021 |                 echo "  uv pip install -r requirements.txt --python $python_cmd"
1022 |                 echo "Or fallback to pip:"
1023 |             fi
1024 |             echo "  $python_cmd -m pip install -r requirements.txt"
1025 |             echo ""
1026 |             echo "Or install individual packages:"
1027 |             echo "  $python_cmd -m pip install mcp google-genai openai pydantic python-dotenv"
1028 |         fi
1029 |         return 1
1030 |     else
1031 |         echo -e "\r${GREEN}✓ Setup complete!${NC}                    "
1032 | 
1033 |         # Verify critical imports work
1034 |         if ! check_package "$python_cmd" "dotenv"; then
1035 |             print_warning "python-dotenv not imported correctly, installing explicitly..."
1036 |             if $python_cmd -m pip install python-dotenv &>/dev/null 2>&1; then
1037 |                 print_success "python-dotenv installed successfully"
1038 |             else
1039 |                 print_error "Failed to install python-dotenv"
1040 |                 return 1
1041 |             fi
1042 |         fi
1043 | 
1044 |         return 0
1045 |     fi
1046 | }
1047 | 
1048 | # ----------------------------------------------------------------------------
1049 | # Environment Configuration Functions
1050 | # ----------------------------------------------------------------------------
1051 | 
1052 | # Setup .env file
1053 | setup_env_file() {
1054 |     if [[ -f .env ]]; then
1055 |         print_success ".env file already exists"
1056 |         migrate_env_file
1057 |         return 0
1058 |     fi
1059 | 
1060 |     if [[ ! -f .env.example ]]; then
1061 |         print_error ".env.example not found!"
1062 |         return 1
1063 |     fi
1064 | 
1065 |     cp .env.example .env
1066 |     print_success "Created .env from .env.example"
1067 | 
1068 |     # Update API keys from environment if present
1069 |     local api_keys=(
1070 |         "GEMINI_API_KEY:your_gemini_api_key_here"
1071 |         "OPENAI_API_KEY:your_openai_api_key_here"
1072 |         "XAI_API_KEY:your_xai_api_key_here"
1073 |         "DIAL_API_KEY:your_dial_api_key_here"
1074 |         "OPENROUTER_API_KEY:your_openrouter_api_key_here"
1075 |     )
1076 | 
1077 |     for key_pair in "${api_keys[@]}"; do
1078 |         local key_name="${key_pair%%:*}"
1079 |         local placeholder="${key_pair##*:}"
1080 |         local key_value="${!key_name:-}"
1081 | 
1082 |         if [[ -n "$key_value" ]]; then
1083 |             sed "${SED_INPLACE_ARGS[@]}" "s/$placeholder/$key_value/" .env
1084 |             print_success "Updated .env with $key_name from environment"
1085 |         fi
1086 |     done
1087 | 
1088 |     return 0
1089 | }
1090 | 
1091 | # Migrate .env file from Docker to standalone format
1092 | migrate_env_file() {
1093 |     # Check if migration is needed
1094 |     if ! grep -q "host\.docker\.internal" .env 2>/dev/null; then
1095 |         return 0
1096 |     fi
1097 | 
1098 |     print_warning "Migrating .env from Docker to standalone format..."
1099 | 
1100 |     # Create backup
1101 |     cp .env .env.backup_$(date +%Y%m%d_%H%M%S)
1102 | 
1103 |     # Replace host.docker.internal with localhost
1104 |     sed "${SED_INPLACE_ARGS[@]}" 's/host\.docker\.internal/localhost/g' .env
1105 | 
1106 |     print_success "Migrated Docker URLs to localhost in .env"
1107 |     echo "  (Backup saved as .env.backup_*)"
1108 | }
1109 | 
1110 | # Check API keys and warn if missing (non-blocking)
1111 | check_api_keys() {
1112 |     local has_key=false
1113 |     local api_keys=(
1114 |         "GEMINI_API_KEY:your_gemini_api_key_here"
1115 |         "OPENAI_API_KEY:your_openai_api_key_here"
1116 |         "XAI_API_KEY:your_xai_api_key_here"
1117 |         "DIAL_API_KEY:your_dial_api_key_here"
1118 |         "OPENROUTER_API_KEY:your_openrouter_api_key_here"
1119 |     )
1120 | 
1121 |     for key_pair in "${api_keys[@]}"; do
1122 |         local key_name="${key_pair%%:*}"
1123 |         local placeholder="${key_pair##*:}"
1124 |         local key_value="${!key_name:-}"
1125 | 
1126 |         if [[ -n "$key_value" ]] && [[ "$key_value" != "$placeholder" ]]; then
1127 |             print_success "$key_name configured"
1128 |             has_key=true
1129 |         fi
1130 |     done
1131 | 
1132 |     # Check custom API URL
1133 |     if [[ -n "${CUSTOM_API_URL:-}" ]]; then
1134 |         print_success "CUSTOM_API_URL configured: $CUSTOM_API_URL"
1135 |         has_key=true
1136 |     fi
1137 | 
1138 |     if [[ "$has_key" == false ]]; then
1139 |         print_warning "No API keys found in .env!"
1140 |         echo ""
1141 |         echo "The Python development environment will be set up, but you won't be able to use the MCP server until you add API keys."
1142 |         echo ""
1143 |         echo "To add API keys, edit .env and add at least one:"
1144 |         echo "  GEMINI_API_KEY=your-actual-key"
1145 |         echo "  OPENAI_API_KEY=your-actual-key"
1146 |         echo "  XAI_API_KEY=your-actual-key"
1147 |         echo "  DIAL_API_KEY=your-actual-key"
1148 |         echo "  OPENROUTER_API_KEY=your-actual-key"
1149 |         echo ""
1150 |         print_info "You can continue with development setup and add API keys later."
1151 |         echo ""
1152 |     fi
1153 | 
1154 |     return 0  # Always return success to continue setup
1155 | }
1156 | 
1157 | 
1158 | # ----------------------------------------------------------------------------
1159 | # Environment Variable Parsing Function
1160 | # ----------------------------------------------------------------------------
1161 | 
1162 | # Parse .env file and extract all valid environment variables
1163 | parse_env_variables() {
1164 |     local env_vars=""
1165 |     
1166 |     if [[ -f .env ]]; then
1167 |         # Read .env file and extract non-empty, non-comment variables
1168 |         while IFS= read -r line; do
1169 |             # Skip comments, empty lines, and lines starting with #
1170 |             if [[ -n "$line" && ! "$line" =~ ^[[:space:]]*# && "$line" =~ ^[[:space:]]*([^=]+)=(.*)$ ]]; then
1171 |                 local key="${BASH_REMATCH[1]}"
1172 |                 local value="${BASH_REMATCH[2]}"
1173 |                 
1174 |                 # Clean up key (remove leading/trailing whitespace)
1175 |                 key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
1176 |                 
1177 |                 # Skip if value is empty or just whitespace
1178 |                 if [[ -n "$value" && ! "$value" =~ ^[[:space:]]*$ ]]; then
1179 |                     # Clean up value (remove leading/trailing whitespace and quotes)
1180 |                     value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/^"//;s/"$//')
1181 |                     
1182 |                     # Remove inline comments (everything after # that's not in quotes)
1183 |                     value=$(echo "$value" | sed 's/[[:space:]]*#.*$//')
1184 |                     
1185 |                     # Skip if value is a placeholder or empty after comment removal
1186 |                     if [[ ! "$value" =~ ^your_.*_here$ && "$value" != "your_" && -n "$value" && ! "$value" =~ ^[[:space:]]*$ ]]; then
1187 |                         env_vars+="$key=$value"$'\n'
1188 |                     fi
1189 |                 fi
1190 |             fi
1191 |         done < .env
1192 |     fi
1193 | 
1194 |     # If no .env file or no valid vars, fall back to environment variables
1195 |     if [[ -z "$env_vars" ]]; then
1196 |         local api_keys=(
1197 |             "GEMINI_API_KEY"
1198 |             "OPENAI_API_KEY" 
1199 |             "XAI_API_KEY"
1200 |             "DIAL_API_KEY"
1201 |             "OPENROUTER_API_KEY"
1202 |             "CUSTOM_API_URL"
1203 |             "CUSTOM_API_KEY"
1204 |             "CUSTOM_MODEL_NAME"
1205 |             "DISABLED_TOOLS"
1206 |             "DEFAULT_MODEL"
1207 |             "LOG_LEVEL"
1208 |             "DEFAULT_THINKING_MODE_THINKDEEP"
1209 |             "CONVERSATION_TIMEOUT_HOURS"
1210 |             "MAX_CONVERSATION_TURNS"
1211 |         )
1212 | 
1213 |         for key_name in "${api_keys[@]}"; do
1214 |             local key_value="${!key_name:-}"
1215 |             if [[ -n "$key_value" && ! "$key_value" =~ ^your_.*_here$ ]]; then
1216 |                 env_vars+="$key_name=$key_value"$'\n'
1217 |             fi
1218 |         done
1219 |     fi
1220 |     
1221 |     echo "$env_vars"
1222 | }
1223 | 
1224 | # ----------------------------------------------------------------------------
1225 | # Claude Integration Functions
1226 | # ----------------------------------------------------------------------------
1227 | 
1228 | # Check if MCP is added to Claude CLI and verify it's correct
1229 | check_claude_cli_integration() {
1230 |     local python_cmd="$1"
1231 |     local server_path="$2"
1232 | 
1233 |     if ! command -v claude &> /dev/null; then
1234 |         echo ""
1235 |         print_warning "Claude CLI not found"
1236 |         echo ""
1237 |         read -p "Would you like to add Zen to Claude Code? (Y/n): " -n 1 -r
1238 |         echo ""
1239 |         if [[ $REPLY =~ ^[Nn]$ ]]; then
1240 |             print_info "Skipping Claude Code integration"
1241 |             return 0
1242 |         fi
1243 | 
1244 |         echo ""
1245 |         echo "Please install Claude Code first:"
1246 |         echo "  Visit: https://docs.anthropic.com/en/docs/claude-code/cli-usage"
1247 |         echo ""
1248 |         echo "Then run this script again to register MCP."
1249 |         return 1
1250 |     fi
1251 | 
1252 |     # Check if zen is registered
1253 |     local mcp_list=$(claude mcp list 2>/dev/null)
1254 |     if echo "$mcp_list" | grep -q "zen"; then
1255 |         # Check if it's using the old Docker command
1256 |         if echo "$mcp_list" | grep -E "zen.*docker|zen.*compose" &>/dev/null; then
1257 |             print_warning "Found old Docker-based Zen registration, updating..."
1258 |             claude mcp remove zen -s user 2>/dev/null || true
1259 | 
1260 |             # Re-add with correct Python command and environment variables
1261 |             local env_vars=$(parse_env_variables)
1262 |             local env_args=""
1263 |             
1264 |             # Convert environment variables to -e arguments
1265 |             if [[ -n "$env_vars" ]]; then
1266 |                 while IFS= read -r line; do
1267 |                     if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
1268 |                         env_args+=" -e ${BASH_REMATCH[1]}=\"${BASH_REMATCH[2]}\""
1269 |                     fi
1270 |                 done <<< "$env_vars"
1271 |             fi
1272 |             
1273 |             local claude_cmd="claude mcp add zen -s user$env_args -- \"$python_cmd\" \"$server_path\""
1274 |             if eval "$claude_cmd" 2>/dev/null; then
1275 |                 print_success "Updated Zen to become a standalone script with environment variables"
1276 |                 return 0
1277 |             else
1278 |                 echo ""
1279 |                 echo "Failed to update MCP registration. Please run manually:"
1280 |                 echo "  claude mcp remove zen -s user"
1281 |                 echo "  $claude_cmd"
1282 |                 return 1
1283 |             fi
1284 |         else
1285 |             # Verify the registered path matches current setup
1286 |             local expected_cmd="$python_cmd $server_path"
1287 |             if echo "$mcp_list" | grep -F "$server_path" &>/dev/null; then
1288 |                 return 0
1289 |             else
1290 |                 print_warning "Zen registered with different path, updating..."
1291 |                 claude mcp remove zen -s user 2>/dev/null || true
1292 | 
1293 |                 # Re-add with current path and environment variables
1294 |                 local env_vars=$(parse_env_variables)
1295 |                 local env_args=""
1296 |                 
1297 |                 # Convert environment variables to -e arguments
1298 |                 if [[ -n "$env_vars" ]]; then
1299 |                     while IFS= read -r line; do
1300 |                         if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
1301 |                             env_args+=" -e ${BASH_REMATCH[1]}=\"${BASH_REMATCH[2]}\""
1302 |                         fi
1303 |                     done <<< "$env_vars"
1304 |                 fi
1305 |                 
1306 |                 local claude_cmd="claude mcp add zen -s user$env_args -- \"$python_cmd\" \"$server_path\""
1307 |                 if eval "$claude_cmd" 2>/dev/null; then
1308 |                     print_success "Updated Zen with current path and environment variables"
1309 |                     return 0
1310 |                 else
1311 |                     echo ""
1312 |                     echo "Failed to update MCP registration. Please run manually:"
1313 |                     echo "  claude mcp remove zen -s user"
1314 |                     echo "  $claude_cmd"
1315 |                     return 1
1316 |                 fi
1317 |             fi
1318 |         fi
1319 |     else
1320 |         # Not registered at all, ask user if they want to add it
1321 |         echo ""
1322 |         read -p "Add Zen to Claude Code? (Y/n): " -n 1 -r
1323 |         echo ""
1324 |         if [[ $REPLY =~ ^[Nn]$ ]]; then
1325 |             local env_vars=$(parse_env_variables)
1326 |             local env_args=""
1327 |             
1328 |             # Convert environment variables to -e arguments for manual command
1329 |             if [[ -n "$env_vars" ]]; then
1330 |                 while IFS= read -r line; do
1331 |                     if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
1332 |                         env_args+=" -e ${BASH_REMATCH[1]}=\"${BASH_REMATCH[2]}\""
1333 |                     fi
1334 |                 done <<< "$env_vars"
1335 |             fi
1336 |             
1337 |             print_info "To add manually later, run:"
1338 |             echo "  claude mcp add zen -s user$env_args -- $python_cmd $server_path"
1339 |             return 0
1340 |         fi
1341 | 
1342 |         print_info "Registering Zen with Claude Code..."
1343 |         
1344 |         # Add with environment variables
1345 |         local env_vars=$(parse_env_variables)
1346 |         local env_args=""
1347 |         
1348 |         # Convert environment variables to -e arguments
1349 |         if [[ -n "$env_vars" ]]; then
1350 |             while IFS= read -r line; do
1351 |                 if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
1352 |                     env_args+=" -e ${BASH_REMATCH[1]}=\"${BASH_REMATCH[2]}\""
1353 |                 fi
1354 |             done <<< "$env_vars"
1355 |         fi
1356 |         
1357 |         local claude_cmd="claude mcp add zen -s user$env_args -- \"$python_cmd\" \"$server_path\""
1358 |         if eval "$claude_cmd" 2>/dev/null; then
1359 |             print_success "Successfully added Zen to Claude Code with environment variables"
1360 |             return 0
1361 |         else
1362 |             echo ""
1363 |             echo "Failed to add automatically. To add manually, run:"
1364 |             echo "  $claude_cmd"
1365 |             return 1
1366 |         fi
1367 |     fi
1368 | }
1369 | 
1370 | # Check and update Claude Desktop configuration
1371 | check_claude_desktop_integration() {
1372 |     local python_cmd="$1"
1373 |     local server_path="$2"
1374 | 
1375 |     # Skip if already configured (check flag)
1376 |     if [[ -f "$DESKTOP_CONFIG_FLAG" ]]; then
1377 |         return 0
1378 |     fi
1379 | 
1380 |     local config_path=$(get_claude_config_path)
1381 |     if [[ -z "$config_path" ]]; then
1382 |         print_warning "Unable to determine Claude Desktop config path for this platform"
1383 |         return 0
1384 |     fi
1385 | 
1386 |     echo ""
1387 |     read -p "Configure Zen for Claude Desktop? (Y/n): " -n 1 -r
1388 |     echo ""
1389 |     if [[ $REPLY =~ ^[Nn]$ ]]; then
1390 |         print_info "Skipping Claude Desktop integration"
1391 |         touch "$DESKTOP_CONFIG_FLAG"  # Don't ask again
1392 |         return 0
1393 |     fi
1394 | 
1395 |     # Create config directory if it doesn't exist
1396 |     local config_dir=$(dirname "$config_path")
1397 |     mkdir -p "$config_dir" 2>/dev/null || true
1398 | 
1399 |     # Handle existing config
1400 |     if [[ -f "$config_path" ]]; then
1401 |         print_info "Updating existing Claude Desktop config..."
1402 | 
1403 |         # Check for old Docker config and remove it
1404 |         if grep -q "docker.*compose.*zen\|zen.*docker" "$config_path" 2>/dev/null; then
1405 |             print_warning "Removing old Docker-based MCP configuration..."
1406 |             # Create backup
1407 |             cp "$config_path" "${config_path}.backup_$(date +%Y%m%d_%H%M%S)"
1408 | 
1409 |             # Remove old zen config using a more robust approach
1410 |             local temp_file=$(mktemp)
1411 |             python3 -c "
1412 | import json
1413 | import sys
1414 | 
1415 | try:
1416 |     with open('$config_path', 'r') as f:
1417 |         config = json.load(f)
1418 | 
1419 |     # Remove zen from mcpServers if it exists
1420 |     if 'mcpServers' in config and 'zen' in config['mcpServers']:
1421 |         del config['mcpServers']['zen']
1422 |         print('Removed old zen MCP configuration')
1423 | 
1424 |     with open('$temp_file', 'w') as f:
1425 |         json.dump(config, f, indent=2)
1426 | 
1427 | except Exception as e:
1428 |     print(f'Error processing config: {e}', file=sys.stderr)
1429 |     sys.exit(1)
1430 | " && mv "$temp_file" "$config_path"
1431 |         fi
1432 | 
1433 |         # Add new config with environment variables
1434 |         local env_vars=$(parse_env_variables)
1435 |         local temp_file=$(mktemp)
1436 |         local env_file=$(mktemp)
1437 |         
1438 |         # Write environment variables to a temporary file for Python to read
1439 |         if [[ -n "$env_vars" ]]; then
1440 |             echo "$env_vars" > "$env_file"
1441 |         fi
1442 |         
1443 |         python3 -c "
1444 | import json
1445 | import sys
1446 | 
1447 | try:
1448 |     with open('$config_path', 'r') as f:
1449 |         config = json.load(f)
1450 | except:
1451 |     config = {}
1452 | 
1453 | # Ensure mcpServers exists
1454 | if 'mcpServers' not in config:
1455 |     config['mcpServers'] = {}
1456 | 
1457 | # Add zen server
1458 | zen_config = {
1459 |     'command': '$python_cmd',
1460 |     'args': ['$server_path']
1461 | }
1462 | 
1463 | # Add environment variables if they exist
1464 | env_dict = {}
1465 | try:
1466 |     with open('$env_file', 'r') as f:
1467 |         for line in f:
1468 |             line = line.strip()
1469 |             if '=' in line and line:
1470 |                 key, value = line.split('=', 1)
1471 |                 env_dict[key] = value
1472 | except:
1473 |     pass
1474 | 
1475 | if env_dict:
1476 |     zen_config['env'] = env_dict
1477 | 
1478 | config['mcpServers']['zen'] = zen_config
1479 | 
1480 | with open('$temp_file', 'w') as f:
1481 |     json.dump(config, f, indent=2)
1482 | " && mv "$temp_file" "$config_path"
1483 |         
1484 |         # Clean up temporary env file
1485 |         rm -f "$env_file" 2>/dev/null || true
1486 | 
1487 |     else
1488 |         print_info "Creating new Claude Desktop config..."
1489 |         
1490 |         # Create new config with environment variables
1491 |         local env_vars=$(parse_env_variables)
1492 |         local temp_file=$(mktemp)
1493 |         local env_file=$(mktemp)
1494 |         
1495 |         # Write environment variables to a temporary file for Python to read
1496 |         if [[ -n "$env_vars" ]]; then
1497 |             echo "$env_vars" > "$env_file"
1498 |         fi
1499 |         
1500 |         python3 -c "
1501 | import json
1502 | import sys
1503 | 
1504 | config = {'mcpServers': {}}
1505 | 
1506 | # Add zen server
1507 | zen_config = {
1508 |     'command': '$python_cmd',
1509 |     'args': ['$server_path']
1510 | }
1511 | 
1512 | # Add environment variables if they exist
1513 | env_dict = {}
1514 | try:
1515 |     with open('$env_file', 'r') as f:
1516 |         for line in f:
1517 |             line = line.strip()
1518 |             if '=' in line and line:
1519 |                 key, value = line.split('=', 1)
1520 |                 env_dict[key] = value
1521 | except:
1522 |     pass
1523 | 
1524 | if env_dict:
1525 |     zen_config['env'] = env_dict
1526 | 
1527 | config['mcpServers']['zen'] = zen_config
1528 | 
1529 | with open('$temp_file', 'w') as f:
1530 |     json.dump(config, f, indent=2)
1531 | " && mv "$temp_file" "$config_path"
1532 |         
1533 |         # Clean up temporary env file
1534 |         rm -f "$env_file" 2>/dev/null || true
1535 |     fi
1536 | 
1537 |     if [[ $? -eq 0 ]]; then
1538 |         print_success "Successfully configured Claude Desktop"
1539 |         echo "  Config: $config_path"
1540 |         echo "  Restart Claude Desktop to use the new MCP server"
1541 |         touch "$DESKTOP_CONFIG_FLAG"
1542 |     else
1543 |         print_error "Failed to update Claude Desktop config"
1544 |         echo "Manual config location: $config_path"
1545 |         echo "Add this configuration:"
1546 |         
1547 |         # Generate example with actual environment variables for error case
1548 |         example_env=""
1549 |         env_vars=$(parse_env_variables)
1550 |         if [[ -n "$env_vars" ]]; then
1551 |             local first_entry=true
1552 |             while IFS= read -r line; do
1553 |                 if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
1554 |                     local key="${BASH_REMATCH[1]}"
1555 |                     local value="your_$(echo "${key}" | tr '[:upper:]' '[:lower:]')"
1556 |                     
1557 |                     if [[ "$first_entry" == true ]]; then
1558 |                         first_entry=false
1559 |                         example_env="      \"$key\": \"$value\""
1560 |                     else
1561 |                         example_env+=",\n      \"$key\": \"$value\""
1562 |                     fi
1563 |                 fi
1564 |             done <<< "$env_vars"
1565 |         fi
1566 |         
1567 |         cat << EOF
1568 | {
1569 |   "mcpServers": {
1570 |     "zen": {
1571 |       "command": "$python_cmd",
1572 |       "args": ["$server_path"]$(if [[ -n "$example_env" ]]; then echo ","; fi)$(if [[ -n "$example_env" ]]; then echo "
1573 |       \"env\": {
1574 | $(echo -e "$example_env")
1575 |       }"; fi)
1576 |     }
1577 |   }
1578 | }
1579 | EOF
1580 |     fi
1581 | }
1582 | 
1583 | # Check and update Gemini CLI configuration
1584 | check_gemini_cli_integration() {
1585 |     local script_dir="$1"
1586 |     local zen_wrapper="$script_dir/zen-mcp-server"
1587 | 
1588 |     # Check if Gemini settings file exists
1589 |     local gemini_config="$HOME/.gemini/settings.json"
1590 |     if [[ ! -f "$gemini_config" ]]; then
1591 |         # Gemini CLI not installed or not configured
1592 |         return 0
1593 |     fi
1594 | 
1595 |     # Check if zen is already configured
1596 |     if grep -q '"zen"' "$gemini_config" 2>/dev/null; then
1597 |         # Already configured
1598 |         return 0
1599 |     fi
1600 | 
1601 |     # Ask user if they want to add Zen to Gemini CLI
1602 |     echo ""
1603 |     read -p "Configure Zen for Gemini CLI? (Y/n): " -n 1 -r
1604 |     echo ""
1605 |     if [[ $REPLY =~ ^[Nn]$ ]]; then
1606 |         print_info "Skipping Gemini CLI integration"
1607 |         return 0
1608 |     fi
1609 | 
1610 |     # Ensure wrapper script exists
1611 |     if [[ ! -f "$zen_wrapper" ]]; then
1612 |         print_info "Creating wrapper script for Gemini CLI..."
1613 |         cat > "$zen_wrapper" << 'EOF'
1614 | #!/bin/bash
1615 | # Wrapper script for Gemini CLI compatibility
1616 | DIR="$(cd "$(dirname "$0")" && pwd)"
1617 | cd "$DIR"
1618 | exec .zen_venv/bin/python server.py "$@"
1619 | EOF
1620 |         chmod +x "$zen_wrapper"
1621 |         print_success "Created zen-mcp-server wrapper script"
1622 |     fi
1623 | 
1624 |     # Update Gemini settings
1625 |     print_info "Updating Gemini CLI configuration..."
1626 | 
1627 |     # Create backup
1628 |     cp "$gemini_config" "${gemini_config}.backup_$(date +%Y%m%d_%H%M%S)"
1629 | 
1630 |     # Add zen configuration using Python for proper JSON handling
1631 |     local temp_file=$(mktemp)
1632 |     python3 -c "
1633 | import json
1634 | import sys
1635 | 
1636 | try:
1637 |     with open('$gemini_config', 'r') as f:
1638 |         config = json.load(f)
1639 | 
1640 |     # Ensure mcpServers exists
1641 |     if 'mcpServers' not in config:
1642 |         config['mcpServers'] = {}
1643 | 
1644 |     # Add zen server
1645 |     config['mcpServers']['zen'] = {
1646 |         'command': '$zen_wrapper'
1647 |     }
1648 | 
1649 |     with open('$temp_file', 'w') as f:
1650 |         json.dump(config, f, indent=2)
1651 | 
1652 | except Exception as e:
1653 |     print(f'Error processing config: {e}', file=sys.stderr)
1654 |     sys.exit(1)
1655 | " && mv "$temp_file" "$gemini_config"
1656 | 
1657 |     if [[ $? -eq 0 ]]; then
1658 |         print_success "Successfully configured Gemini CLI"
1659 |         echo "  Config: $gemini_config"
1660 |         echo "  Restart Gemini CLI to use Zen MCP Server"
1661 |     else
1662 |         print_error "Failed to update Gemini CLI config"
1663 |         echo "Manual config location: $gemini_config"
1664 |         echo "Add this configuration:"
1665 |         cat << EOF
1666 | {
1667 |   "mcpServers": {
1668 |     "zen": {
1669 |       "command": "$zen_wrapper"
1670 |     }
1671 |   }
1672 | }
1673 | EOF
1674 |     fi
1675 | }
1676 | 
1677 | # Check and update Codex CLI configuration
1678 | check_codex_cli_integration() {
1679 |     # Check if Codex is installed
1680 |     if ! command -v codex &> /dev/null; then
1681 |         # Codex CLI not installed
1682 |         return 0
1683 |     fi
1684 | 
1685 |     local codex_config="$HOME/.codex/config.toml"
1686 |     
1687 |     # Check if zen is already configured
1688 |     if [[ -f "$codex_config" ]] && grep -q '\[mcp_servers\.zen\]' "$codex_config" 2>/dev/null; then
1689 |         # Already configured
1690 |         return 0
1691 |     fi
1692 | 
1693 |     # Ask user if they want to add Zen to Codex CLI
1694 |     echo ""
1695 |     read -p "Configure Zen for Codex CLI? (Y/n): " -n 1 -r
1696 |     echo ""
1697 |     if [[ $REPLY =~ ^[Nn]$ ]]; then
1698 |         print_info "Skipping Codex CLI integration"
1699 |         return 0
1700 |     fi
1701 | 
1702 |     print_info "Updating Codex CLI configuration..."
1703 | 
1704 |     # Create config directory if it doesn't exist
1705 |     mkdir -p "$(dirname "$codex_config")" 2>/dev/null || true
1706 | 
1707 |     # Create backup if config exists
1708 |     if [[ -f "$codex_config" ]]; then
1709 |         cp "$codex_config" "${codex_config}.backup_$(date +%Y%m%d_%H%M%S)"
1710 |     fi
1711 | 
1712 |     # Get environment variables using shared function
1713 |     local env_vars=$(parse_env_variables)
1714 | 
1715 |     # Write zen configuration to config.toml
1716 |     {
1717 |         echo ""
1718 |         echo "[mcp_servers.zen]"
1719 |         echo "command = \"bash\""
1720 |         echo "args = [\"-c\", \"for p in \$(which uvx 2>/dev/null) \$HOME/.local/bin/uvx /opt/homebrew/bin/uvx /usr/local/bin/uvx uvx; do [ -x \\\"\$p\\\" ] && exec \\\"\$p\\\" --from git+https://github.com/BeehiveInnovations/zen-mcp-server.git zen-mcp-server; done; echo 'uvx not found' >&2; exit 1\"]"
1721 |         echo "tool_timeout_sec = 1200"
1722 |         echo ""
1723 |         echo "[mcp_servers.zen.env]"
1724 |         echo "PATH = \"/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:\$HOME/.local/bin:\$HOME/.cargo/bin:\$HOME/bin\""
1725 |         if [[ -n "$env_vars" ]]; then
1726 |             # Convert KEY=VALUE format to TOML KEY = "VALUE" format
1727 |             while IFS= read -r line; do
1728 |                 if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
1729 |                     local key="${BASH_REMATCH[1]}"
1730 |                     local value="${BASH_REMATCH[2]}"
1731 |                     # Escape backslashes first, then double quotes for TOML compatibility
1732 |                     local escaped_value
1733 |                     escaped_value=$(echo "$value" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
1734 |                     echo "$key = \"$escaped_value\""
1735 |                 fi
1736 |             done <<< "$env_vars"
1737 |         fi
1738 |     } >> "$codex_config"
1739 | 
1740 |     if [[ $? -eq 0 ]]; then
1741 |         print_success "Successfully configured Codex CLI"
1742 |         echo "  Config: $codex_config"
1743 |         echo "  Restart Codex CLI to use Zen MCP Server"
1744 | 
1745 |         if ! grep -Eq '^\s*web_search\s*=' "$codex_config" 2>/dev/null; then
1746 |             echo ""
1747 |             print_info "Web search lets Codex pull fresh documentation for Zen's API lookup tooling."
1748 |             read -p "Enable Codex CLI web search tool? (Y/n): " -n 1 -r
1749 |             echo ""
1750 |             if [[ ! $REPLY =~ ^[Nn]$ ]]; then
1751 |                 if grep -Eq '^\s*\[tools\]' "$codex_config" 2>/dev/null; then
1752 |                     if ! python3 - "$codex_config" <<'PY'
1753 | import sys
1754 | from pathlib import Path
1755 | 
1756 | cfg_path = Path(sys.argv[1])
1757 | content = cfg_path.read_text().splitlines()
1758 | output = []
1759 | in_tools = False
1760 | added = False
1761 | 
1762 | for line in content:
1763 |     stripped = line.strip()
1764 |     if stripped.startswith("[") and stripped.endswith("]"):
1765 |         if in_tools and not added:
1766 |             output.append("web_search = true")
1767 |             added = True
1768 |         in_tools = stripped == "[tools]"
1769 |         output.append(line)
1770 |         continue
1771 |     if in_tools and stripped.startswith("web_search"):
1772 |         added = True
1773 |     output.append(line)
1774 | 
1775 | if in_tools and not added:
1776 |     output.append("web_search = true")
1777 | 
1778 | cfg_path.write_text("\n".join(output) + "\n")
1779 | PY
1780 |                     then
1781 |                         print_error "Failed to enable Codex web search tool. Add 'web_search = true' under [tools] in $codex_config manually."
1782 |                     else
1783 |                         print_success "Enabled Codex web search tool"
1784 |                     fi
1785 |                 else
1786 |                     {
1787 |                         echo ""
1788 |                         echo "[tools]"
1789 |                         echo "web_search = true"
1790 |                     } >> "$codex_config" && print_success "Enabled Codex web search tool" || \
1791 |                         print_error "Failed to enable Codex web search tool. Add 'web_search = true' under [tools] in $codex_config manually."
1792 |                 fi
1793 |             else
1794 |                 print_info "Skipping Codex web search tool enablement"
1795 |             fi
1796 |         fi
1797 |     else
1798 |         print_error "Failed to update Codex CLI config"
1799 |         echo "Manual config location: $codex_config"
1800 |         echo "Add this configuration:"
1801 |         
1802 |         # Generate example with actual environment variables for error case
1803 |         env_vars=$(parse_env_variables)
1804 | cat << EOF
1805 | [mcp_servers.zen]
1806 | command = "sh"
1807 | args = ["-c", "exec \$(which uvx 2>/dev/null || echo uvx) --from git+https://github.com/BeehiveInnovations/zen-mcp-server.git zen-mcp-server"]
1808 | tool_timeout_sec = 1200
1809 | 
1810 | [mcp_servers.zen.env]
1811 | PATH = "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:\$HOME/.local/bin:\$HOME/.cargo/bin:\$HOME/bin"
1812 | EOF
1813 |         
1814 |         # Add environment variable examples only if they exist
1815 |         if [[ -n "$env_vars" ]]; then
1816 |             while IFS= read -r line; do
1817 |                 if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
1818 |                     local key="${BASH_REMATCH[1]}"
1819 |                     echo "${key} = \"your_$(echo "${key}" | tr '[:upper:]' '[:lower:]')\""
1820 |                 fi
1821 |             done <<< "$env_vars"
1822 |         else
1823 |             # Show GEMINI_API_KEY example if no environment variables exist
1824 |             echo "GEMINI_API_KEY = \"your_gemini_api_key_here\""
1825 |         fi
1826 |     fi
1827 | }
1828 | 
1829 | # Print manual Qwen CLI configuration guidance
1830 | print_qwen_manual_instructions() {
1831 |     local python_cmd="$1"
1832 |     local server_path="$2"
1833 |     local script_dir="$3"
1834 |     local config_path="$4"
1835 |     local env_lines="$5"
1836 | 
1837 |     local env_array=()
1838 |     if [[ -n "$env_lines" ]]; then
1839 |         while IFS= read -r line; do
1840 |             [[ -z "$line" ]] && continue
1841 |             env_array+=("$line")
1842 |         done <<< "$env_lines"
1843 |     fi
1844 | 
1845 |     echo "Manual config location: $config_path"
1846 |     echo "Add or update this entry:"
1847 | 
1848 |     local env_block=""
1849 |     if [[ ${#env_array[@]} -gt 0 ]]; then
1850 |         env_block=$'      "env": {\n'
1851 |         local first=true
1852 |         for env_entry in "${env_array[@]}"; do
1853 |             local key="${env_entry%%=*}"
1854 |             local value="${env_entry#*=}"
1855 |             value=${value//\\/\\\\}
1856 |             value=${value//"/\\"}
1857 |             if [[ "$first" == true ]]; then
1858 |                 first=false
1859 |                 env_block+="        \"$key\": \"$value\""
1860 |             else
1861 |                 env_block+=$',\n        '
1862 |                 env_block+="\"$key\": \"$value\""
1863 |             fi
1864 |         done
1865 |         env_block+=$'\n      }'
1866 |     fi
1867 | 
1868 |     if [[ -n "$env_block" ]]; then
1869 |         cat << EOF
1870 | {
1871 |   "mcpServers": {
1872 |     "zen": {
1873 |       "command": "$python_cmd",
1874 |       "args": ["$server_path"],
1875 |       "cwd": "$script_dir",
1876 | $env_block
1877 |     }
1878 |   }
1879 | }
1880 | EOF
1881 |     else
1882 |         cat << EOF
1883 | {
1884 |   "mcpServers": {
1885 |     "zen": {
1886 |       "command": "$python_cmd",
1887 |       "args": ["$server_path"],
1888 |       "cwd": "$script_dir"
1889 |     }
1890 |   }
1891 | }
1892 | EOF
1893 |     fi
1894 | }
1895 | 
1896 | # Check and update Qwen Code CLI configuration
1897 | check_qwen_cli_integration() {
1898 |     local python_cmd="$1"
1899 |     local server_path="$2"
1900 | 
1901 |     if ! command -v qwen &> /dev/null; then
1902 |         return 0
1903 |     fi
1904 | 
1905 |     local qwen_config="$HOME/.qwen/settings.json"
1906 |     local script_dir
1907 |     script_dir=$(dirname "$server_path")
1908 | 
1909 |     local env_vars
1910 |     env_vars=$(parse_env_variables)
1911 |     local env_array=()
1912 |     if [[ -n "$env_vars" ]]; then
1913 |         while IFS= read -r line; do
1914 |             if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
1915 |                 env_array+=("${BASH_REMATCH[1]}=${BASH_REMATCH[2]}")
1916 |             fi
1917 |         done <<< "$env_vars"
1918 |     fi
1919 | 
1920 |     local env_lines=""
1921 |     if [[ ${#env_array[@]} -gt 0 ]]; then
1922 |         env_lines=$(printf '%s\n' "${env_array[@]}")
1923 |     fi
1924 | 
1925 |     local config_status=3
1926 |     if [[ -f "$qwen_config" ]]; then
1927 |         if python3 - "$qwen_config" "$python_cmd" "$server_path" "$script_dir" <<'PYCONF'
1928 | import json
1929 | import sys
1930 | 
1931 | config_path, expected_cmd, expected_arg, expected_cwd = sys.argv[1:5]
1932 | try:
1933 |     with open(config_path, 'r', encoding='utf-8') as f:
1934 |         data = json.load(f)
1935 | except FileNotFoundError:
1936 |     sys.exit(1)
1937 | except Exception:
1938 |     sys.exit(5)
1939 | 
1940 | servers = data.get('mcpServers')
1941 | if not isinstance(servers, dict):
1942 |     sys.exit(3)
1943 | 
1944 | config = servers.get('zen')
1945 | if not isinstance(config, dict):
1946 |     sys.exit(3)
1947 | 
1948 | cmd = config.get('command')
1949 | args = config.get('args') or []
1950 | cwd = config.get('cwd')
1951 | 
1952 | cwd_matches = cwd in (None, "", expected_cwd)
1953 | if cmd == expected_cmd and len(args) == 1 and args[0] == expected_arg and cwd_matches:
1954 |     sys.exit(0)
1955 | 
1956 | sys.exit(4)
1957 | PYCONF
1958 |         then
1959 |             config_status=0
1960 |         else
1961 |             config_status=$?
1962 |             if [[ $config_status -eq 1 ]]; then
1963 |                 config_status=3
1964 |             fi
1965 |         fi
1966 |     fi
1967 | 
1968 |     if [[ $config_status -eq 0 ]]; then
1969 |         return 0
1970 |     fi
1971 | 
1972 |     echo ""
1973 | 
1974 |     if [[ $config_status -eq 4 ]]; then
1975 |         print_warning "Found existing Qwen CLI zen configuration with different settings."
1976 |     elif [[ $config_status -eq 5 ]]; then
1977 |         print_warning "Unable to parse Qwen CLI settings; replacing with a fresh entry may help."
1978 |     fi
1979 | 
1980 |     local prompt="Configure Zen for Qwen CLI? (Y/n): "
1981 |     if [[ $config_status -eq 4 || $config_status -eq 5 ]]; then
1982 |         prompt="Update Qwen CLI zen configuration? (Y/n): "
1983 |     fi
1984 | 
1985 |     read -p "$prompt" -n 1 -r
1986 |     echo ""
1987 |     if [[ $REPLY =~ ^[Nn]$ ]]; then
1988 |         print_info "Skipping Qwen CLI integration"
1989 |         print_qwen_manual_instructions "$python_cmd" "$server_path" "$script_dir" "$qwen_config" "$env_lines"
1990 |         return 0
1991 |     fi
1992 | 
1993 |     mkdir -p "$(dirname "$qwen_config")" 2>/dev/null || true
1994 |     if [[ -f "$qwen_config" && $config_status -ne 3 ]]; then
1995 |         cp "$qwen_config" "${qwen_config}.backup_$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
1996 |     fi
1997 | 
1998 |     local update_output
1999 |     local update_status=0
2000 |     update_output=$(ZEN_QWEN_ENV="$env_lines" ZEN_QWEN_CMD="$python_cmd" ZEN_QWEN_ARG="$server_path" ZEN_QWEN_CWD="$script_dir" python3 - "$qwen_config" <<'PYUPDATE'
2001 | import json
2002 | import os
2003 | import pathlib
2004 | import sys
2005 | 
2006 | config_path = pathlib.Path(sys.argv[1])
2007 | cmd = os.environ['ZEN_QWEN_CMD']
2008 | arg = os.environ['ZEN_QWEN_ARG']
2009 | cwd = os.environ['ZEN_QWEN_CWD']
2010 | env_lines = os.environ.get('ZEN_QWEN_ENV', '').splitlines()
2011 | 
2012 | env_map = {}
2013 | for line in env_lines:
2014 |     if not line.strip():
2015 |         continue
2016 |     if '=' in line:
2017 |         key, value = line.split('=', 1)
2018 |         env_map[key] = value
2019 | 
2020 | if config_path.exists():
2021 |     try:
2022 |         with config_path.open('r', encoding='utf-8') as f:
2023 |             data = json.load(f)
2024 |     except Exception:
2025 |         data = {}
2026 | else:
2027 |     data = {}
2028 | 
2029 | if not isinstance(data, dict):
2030 |     data = {}
2031 | 
2032 | servers = data.get('mcpServers')
2033 | if not isinstance(servers, dict):
2034 |     servers = {}
2035 |     data['mcpServers'] = servers
2036 | 
2037 | zen_config = {
2038 |     'command': cmd,
2039 |     'args': [arg],
2040 |     'cwd': cwd,
2041 | }
2042 | 
2043 | if env_map:
2044 |     zen_config['env'] = env_map
2045 | 
2046 | servers['zen'] = zen_config
2047 | 
2048 | config_path.parent.mkdir(parents=True, exist_ok=True)
2049 | tmp_path = config_path.with_suffix(config_path.suffix + '.tmp')
2050 | with tmp_path.open('w', encoding='utf-8') as f:
2051 |     json.dump(data, f, indent=2)
2052 |     f.write('\n')
2053 | tmp_path.replace(config_path)
2054 | PYUPDATE
2055 |     ) || update_status=$?
2056 | 
2057 |     if [[ $update_status -eq 0 ]]; then
2058 |         print_success "Successfully configured Qwen CLI"
2059 |         echo "  Config: $qwen_config"
2060 |         echo "  Restart Qwen CLI to use Zen MCP Server"
2061 |     else
2062 |         print_error "Failed to update Qwen CLI config"
2063 |         if [[ -n "$update_output" ]]; then
2064 |             echo "$update_output"
2065 |         fi
2066 |         print_qwen_manual_instructions "$python_cmd" "$server_path" "$script_dir" "$qwen_config" "$env_lines"
2067 |     fi
2068 | }
2069 | 
2070 | # Display configuration instructions
2071 | display_config_instructions() {
2072 |     local python_cmd="$1"
2073 |     local server_path="$2"
2074 | 
2075 |     # Get script directory for Gemini CLI config
2076 |     local script_dir=$(dirname "$server_path")
2077 | 
2078 |     echo ""
2079 |     local config_header="ZEN MCP SERVER CONFIGURATION"
2080 |     echo "===== $config_header ====="
2081 |     printf '%*s\n' "$((${#config_header} + 12))" | tr ' ' '='
2082 |     echo ""
2083 |     echo "To use Zen MCP Server with your CLI clients:"
2084 |     echo ""
2085 | 
2086 |     print_info "1. For Claude Code (CLI):"
2087 |     # Show command with environment variables
2088 |     local env_vars=$(parse_env_variables)
2089 |     local env_args=""
2090 |     if [[ -n "$env_vars" ]]; then
2091 |         while IFS= read -r line; do
2092 |             if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
2093 |                 env_args+=" -e ${BASH_REMATCH[1]}=\"${BASH_REMATCH[2]}\""
2094 |             fi
2095 |         done <<< "$env_vars"
2096 |     fi
2097 |     echo -e "   ${GREEN}claude mcp add zen -s user$env_args -- $python_cmd $server_path${NC}"
2098 |     echo ""
2099 | 
2100 |     print_info "2. For Claude Desktop:"
2101 |     echo "   Add this configuration to your Claude Desktop config file:"
2102 |     echo ""
2103 |     
2104 |     # Generate example with actual environment variables that exist
2105 |     example_env=""
2106 |     env_vars=$(parse_env_variables)
2107 |     if [[ -n "$env_vars" ]]; then
2108 |         local first_entry=true
2109 |         while IFS= read -r line; do
2110 |             if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
2111 |                 local key="${BASH_REMATCH[1]}"
2112 |                 local value="your_$(echo "${key}" | tr '[:upper:]' '[:lower:]')"
2113 |                 
2114 |                 if [[ "$first_entry" == true ]]; then
2115 |                     first_entry=false
2116 |                     example_env="           \"$key\": \"$value\""
2117 |                 else
2118 |                     example_env+=",\n           \"$key\": \"$value\""
2119 |                 fi
2120 |             fi
2121 |         done <<< "$env_vars"
2122 |     fi
2123 |     
2124 |     if [[ -n "$example_env" ]]; then
2125 |         cat << EOF
2126 |    {
2127 |      "mcpServers": {
2128 |        "zen": {
2129 |          "command": "$python_cmd",
2130 |          "args": ["$server_path"],
2131 |          "cwd": "$script_dir",
2132 |          "env": {
2133 | $(echo -e "$example_env")
2134 |          }
2135 |        }
2136 |      }
2137 |    }
2138 | EOF
2139 |     else
2140 |         cat << EOF
2141 |    {
2142 |      "mcpServers": {
2143 |        "zen": {
2144 |          "command": "$python_cmd",
2145 |          "args": ["$server_path"],
2146 |          "cwd": "$script_dir"
2147 |        }
2148 |      }
2149 |    }
2150 | EOF
2151 |     fi
2152 | 
2153 |     # Show platform-specific config location
2154 |     local config_path=$(get_claude_config_path)
2155 |     if [[ -n "$config_path" ]]; then
2156 |         echo ""
2157 |         print_info "   Config file location:"
2158 |         echo -e "   ${YELLOW}$config_path${NC}"
2159 |     fi
2160 | 
2161 |     echo ""
2162 |     print_info "3. Restart Claude Desktop after updating the config file"
2163 |     echo ""
2164 | 
2165 |     print_info "For Gemini CLI:"
2166 |     echo "   Add this configuration to ~/.gemini/settings.json:"
2167 |     echo ""
2168 |     cat << EOF
2169 |    {
2170 |      "mcpServers": {
2171 |        "zen": {
2172 |          "command": "$script_dir/zen-mcp-server"
2173 |        }
2174 |      }
2175 |    }
2176 | EOF
2177 |     echo ""
2178 | 
2179 |     print_info "For Qwen Code CLI:"
2180 |     echo "   Add this configuration to ~/.qwen/settings.json:"
2181 |     echo ""
2182 |     if [[ -n "$example_env" ]]; then
2183 |         cat << EOF
2184 |    {
2185 |      "mcpServers": {
2186 |        "zen": {
2187 |          "command": "$python_cmd",
2188 |          "args": ["$server_path"],
2189 |          "cwd": "$script_dir",
2190 |          "env": {
2191 | $(echo -e "$example_env")
2192 |          }
2193 |        }
2194 |      }
2195 |    }
2196 | EOF
2197 |     else
2198 |         cat << EOF
2199 |    {
2200 |      "mcpServers": {
2201 |        "zen": {
2202 |          "command": "$python_cmd",
2203 |          "args": ["$server_path"],
2204 |          "cwd": "$script_dir"
2205 |        }
2206 |      }
2207 |    }
2208 | EOF
2209 |     fi
2210 |     echo ""
2211 | 
2212 |     print_info "For Codex CLI:"
2213 |     echo "   Add this configuration to ~/.codex/config.toml:"
2214 |     echo ""
2215 |     cat << EOF
2216 |    [mcp_servers.zen]
2217 |    command = "bash"
2218 |    args = ["-c", "for p in \$(which uvx 2>/dev/null) \$HOME/.local/bin/uvx /opt/homebrew/bin/uvx /usr/local/bin/uvx uvx; do [ -x \\\"\$p\\\" ] && exec \\\"\$p\\\" --from git+https://github.com/BeehiveInnovations/zen-mcp-server.git zen-mcp-server; done; echo 'uvx not found' >&2; exit 1"]
2219 | 
2220 |    [mcp_servers.zen.env]
2221 |    PATH = "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:\$HOME/.local/bin:\$HOME/.cargo/bin:\$HOME/bin"
2222 |    GEMINI_API_KEY = "your_gemini_api_key_here"
2223 | EOF
2224 |     echo ""
2225 | }
2226 | 
2227 | # Display setup instructions
2228 | display_setup_instructions() {
2229 |     local python_cmd="$1"
2230 |     local server_path="$2"
2231 | 
2232 |     echo ""
2233 |     local setup_header="SETUP COMPLETE"
2234 |     echo "===== $setup_header ====="
2235 |     printf '%*s\n' "$((${#setup_header} + 12))" | tr ' ' '='
2236 |     echo ""
2237 |     print_success "Zen is ready to use!"
2238 |     
2239 |     # Display enabled/disabled tools if DISABLED_TOOLS is configured
2240 |     if [[ -n "${DISABLED_TOOLS:-}" ]]; then
2241 |         echo ""
2242 |         print_info "Tool Configuration:"
2243 |         
2244 |         # Dynamically discover all available tools from the tools directory
2245 |         # Excludes: __pycache__, shared modules, models.py, listmodels.py, version.py
2246 |         local all_tools=()
2247 |         for tool_file in tools/*.py; do
2248 |             if [[ -f "$tool_file" ]]; then
2249 |                 local tool_name=$(basename "$tool_file" .py)
2250 |                 # Skip non-tool files
2251 |                 if [[ "$tool_name" != "models" && "$tool_name" != "listmodels" && "$tool_name" != "version" && "$tool_name" != "__init__" ]]; then
2252 |                     all_tools+=("$tool_name")
2253 |                 fi
2254 |             fi
2255 |         done
2256 |         
2257 |         # Convert DISABLED_TOOLS to array
2258 |         IFS=',' read -ra disabled_array <<< "$DISABLED_TOOLS"
2259 |         
2260 |         # Trim whitespace from disabled tools
2261 |         local disabled_tools=()
2262 |         for tool in "${disabled_array[@]}"; do
2263 |             disabled_tools+=("$(echo "$tool" | xargs)")
2264 |         done
2265 |         
2266 |         # Determine enabled tools
2267 |         local enabled_tools=()
2268 |         for tool in "${all_tools[@]}"; do
2269 |             local is_disabled=false
2270 |             for disabled in "${disabled_tools[@]}"; do
2271 |                 if [[ "$tool" == "$disabled" ]]; then
2272 |                     is_disabled=true
2273 |                     break
2274 |                 fi
2275 |             done
2276 |             if [[ "$is_disabled" == false ]]; then
2277 |                 enabled_tools+=("$tool")
2278 |             fi
2279 |         done
2280 |         
2281 |         # Display enabled tools
2282 |         echo ""
2283 |         echo -e "  ${GREEN}Enabled Tools (${#enabled_tools[@]}):${NC}"
2284 |         local enabled_list=""
2285 |         for tool in "${enabled_tools[@]}"; do
2286 |             if [[ -n "$enabled_list" ]]; then
2287 |                 enabled_list+=", "
2288 |             fi
2289 |             enabled_list+="$tool"
2290 |         done
2291 |         echo "    $enabled_list"
2292 |         
2293 |         # Display disabled tools
2294 |         echo ""
2295 |         echo -e "  ${YELLOW}Disabled Tools (${#disabled_tools[@]}):${NC}"
2296 |         local disabled_list=""
2297 |         for tool in "${disabled_tools[@]}"; do
2298 |             if [[ -n "$disabled_list" ]]; then
2299 |                 disabled_list+=", "
2300 |             fi
2301 |             disabled_list+="$tool"
2302 |         done
2303 |         echo "    $disabled_list"
2304 |         
2305 |         echo ""
2306 |         echo "  To enable more tools, edit the DISABLED_TOOLS variable in .env"
2307 |     fi
2308 | }
2309 | 
2310 | # ----------------------------------------------------------------------------
2311 | # Log Management Functions
2312 | # ----------------------------------------------------------------------------
2313 | 
2314 | # Show help message
2315 | show_help() {
2316 |     local version=$(get_version)
2317 |     local header="🤖 Zen MCP Server v$version"
2318 |     echo "$header"
2319 |     printf '%*s\n' "${#header}" | tr ' ' '='
2320 |     echo ""
2321 |     echo "Usage: $0 [OPTIONS]"
2322 |     echo ""
2323 |     echo "Options:"
2324 |     echo "  -h, --help      Show this help message"
2325 |     echo "  -v, --version   Show version information"
2326 |     echo "  -f, --follow    Follow server logs in real-time"
2327 |     echo "  -c, --config    Show configuration instructions for Claude clients"
2328 |     echo "  --clear-cache   Clear Python cache and exit (helpful for import issues)"
2329 |     echo ""
2330 |     echo "Examples:"
2331 |     echo "  $0              Setup and start the MCP server"
2332 |     echo "  $0 -f           Setup and follow logs"
2333 |     echo "  $0 -c           Show configuration instructions"
2334 |     echo "  $0 --version    Show version only"
2335 |     echo "  $0 --clear-cache Clear Python cache (fixes import issues)"
2336 |     echo ""
2337 |     echo "For more information, visit:"
2338 |     echo "  https://github.com/BeehiveInnovations/zen-mcp-server"
2339 | }
2340 | 
2341 | # Show version only
2342 | show_version() {
2343 |     local version=$(get_version)
2344 |     echo "$version"
2345 | }
2346 | 
2347 | # Follow logs
2348 | follow_logs() {
2349 |     local log_path="$LOG_DIR/$LOG_FILE"
2350 | 
2351 |     echo "Following server logs (Ctrl+C to stop)..."
2352 |     echo ""
2353 | 
2354 |     # Create logs directory and file if they don't exist
2355 |     mkdir -p "$LOG_DIR"
2356 |     touch "$log_path"
2357 | 
2358 |     # Follow the log file
2359 |     tail -f "$log_path"
2360 | }
2361 | 
2362 | # ----------------------------------------------------------------------------
2363 | # Main Function
2364 | # ----------------------------------------------------------------------------
2365 | 
2366 | main() {
2367 |     # Parse command line arguments
2368 |     local arg="${1:-}"
2369 | 
2370 |     case "$arg" in
2371 |         -h|--help)
2372 |             show_help
2373 |             exit 0
2374 |             ;;
2375 |         -v|--version)
2376 |             show_version
2377 |             exit 0
2378 |             ;;
2379 |         -c|--config)
2380 |             # Setup minimal environment to get paths for config display
2381 |             echo "Setting up environment for configuration display..."
2382 |             echo ""
2383 |             local python_cmd
2384 |             python_cmd=$(setup_environment) || exit 1
2385 |             local script_dir=$(get_script_dir)
2386 |             local server_path="$script_dir/server.py"
2387 |             display_config_instructions "$python_cmd" "$server_path"
2388 |             exit 0
2389 |             ;;
2390 |         -f|--follow)
2391 |             # Continue with normal setup then follow logs
2392 |             ;;
2393 |         --clear-cache)
2394 |             # Clear cache and exit
2395 |             clear_python_cache
2396 |             print_success "Cache cleared successfully"
2397 |             echo ""
2398 |             echo "You can now run './run-server.sh' normally"
2399 |             exit 0
2400 |             ;;
2401 |         "")
2402 |             # Normal setup without following logs
2403 |             ;;
2404 |         *)
2405 |             print_error "Unknown option: $arg"
2406 |             echo "" >&2
2407 |             show_help
2408 |             exit 1
2409 |             ;;
2410 |     esac
2411 | 
2412 |     # Display header
2413 |     local main_header="🤖 Zen MCP Server"
2414 |     echo "$main_header"
2415 |     printf '%*s\n' "${#main_header}" | tr ' ' '='
2416 | 
2417 |     # Get and display version
2418 |     local version=$(get_version)
2419 |     echo "Version: $version"
2420 |     echo ""
2421 | 
2422 |     # Check if venv exists
2423 |     if [[ ! -d "$VENV_PATH" ]]; then
2424 |         echo "Setting up Python environment for first time..."
2425 |     fi
2426 | 
2427 |     # Step 1: Docker cleanup
2428 |     cleanup_docker
2429 | 
2430 |     # Step 1.5: Clear Python cache to prevent import issues
2431 |     clear_python_cache
2432 | 
2433 |     # Step 2: Setup environment file
2434 |     setup_env_file || exit 1
2435 | 
2436 |     # Step 3: Source .env file
2437 |     if [[ -f .env ]]; then
2438 |         set -a
2439 |         source .env
2440 |         set +a
2441 |     fi
2442 | 
2443 |     # Step 4: Check API keys (non-blocking - just warn if missing)
2444 |     check_api_keys
2445 | 
2446 |     # Step 5: Setup Python environment (uv-first approach)
2447 |     local python_cmd
2448 |     python_cmd=$(setup_environment) || exit 1
2449 | 
2450 |     # Step 6: Install dependencies
2451 |     install_dependencies "$python_cmd" || exit 1
2452 | 
2453 |     # Step 7: Get absolute server path
2454 |     local script_dir=$(get_script_dir)
2455 |     local server_path="$script_dir/server.py"
2456 | 
2457 |     # Step 8: Display setup instructions
2458 |     display_setup_instructions "$python_cmd" "$server_path"
2459 | 
2460 |     # Step 9: Check Claude integrations
2461 |     check_claude_cli_integration "$python_cmd" "$server_path"
2462 |     check_claude_desktop_integration "$python_cmd" "$server_path"
2463 | 
2464 |     # Step 10: Check Gemini CLI integration
2465 |     check_gemini_cli_integration "$script_dir"
2466 | 
2467 |     # Step 11: Check Codex CLI integration
2468 |     check_codex_cli_integration
2469 | 
2470 |     # Step 12: Check Qwen CLI integration
2471 |     check_qwen_cli_integration "$python_cmd" "$server_path"
2472 | 
2473 |     # Step 13: Display log information
2474 |     echo ""
2475 |     echo "Logs will be written to: $script_dir/$LOG_DIR/$LOG_FILE"
2476 |     echo ""
2477 | 
2478 |     # Step 14: Handle command line arguments
2479 |     if [[ "$arg" == "-f" ]] || [[ "$arg" == "--follow" ]]; then
2480 |         follow_logs
2481 |     else
2482 |         echo "To follow logs: ./run-server.sh -f"
2483 |         echo "To show config: ./run-server.sh -c"
2484 |         echo "To update: git pull, then run ./run-server.sh again"
2485 |         echo ""
2486 |         echo "Happy coding! 🎉"
2487 |     fi
2488 | }
2489 | 
2490 | # ----------------------------------------------------------------------------
2491 | # Script Entry Point
2492 | # ----------------------------------------------------------------------------
2493 | 
2494 | if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
2495 |     main "$@"
2496 | fi
2497 | 
```