This is page 24 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.ps1:
--------------------------------------------------------------------------------
```
   1 | <#
   2 | .SYNOPSIS
   3 |     Installation, configuration, and launch script for Zen MCP server on Windows.
   4 | 
   5 | .DESCRIPTION
   6 |     This PowerShell script prepares the environment for the Zen MCP server:
   7 |     - Installs and checks Python 3.10+ (with venv or uv if available)
   8 |     - Installs required Python dependencies
   9 |     - Configures environment files (.env)
  10 |     - Validates presence of required API keys
  11 |     - Cleans Python caches and obsolete Docker artifacts
  12 |     - Offers automatic integration with Claude Desktop, Gemini CLI, VSCode, Cursor, Windsurf, and Trae
  13 |     - Manages configuration file backups (max 3 retained)
  14 |     - Allows real-time log following or server launch
  15 | 
  16 | .PARAMETER Help
  17 |     Shows script help.
  18 | 
  19 | .PARAMETER Version
  20 |     Shows Zen MCP server version.
  21 | 
  22 | .PARAMETER Follow
  23 |     Follows server logs in real time.
  24 | 
  25 | .PARAMETER Config
  26 |     Shows configuration instructions for Claude and other compatible clients.
  27 | 
  28 | .PARAMETER ClearCache
  29 |     Removes Python cache files (__pycache__, .pyc).
  30 | 
  31 | .PARAMETER SkipVenv
  32 |     Skips Python virtual environment creation.
  33 | 
  34 | .PARAMETER SkipDocker
  35 |     Skips Docker checks and cleanup.
  36 | 
  37 | .PARAMETER Force
  38 |     Forces recreation of the Python virtual environment.
  39 |     
  40 | .PARAMETER VerboseOutput
  41 |     Enables more detailed output (currently unused).
  42 | 
  43 | .PARAMETER Dev
  44 |     Installs development dependencies from requirements-dev.txt if available.
  45 | 
  46 | .PARAMETER Docker
  47 |     Uses Docker to build and run the MCP server instead of Python virtual environment.
  48 | 
  49 | .EXAMPLE
  50 |     .\run-server.ps1
  51 |     Prepares the environment and starts the Zen MCP server.
  52 | 
  53 |     .\run-server.ps1 -Follow
  54 |     Follows server logs in real time.
  55 | 
  56 |     .\run-server.ps1 -Config
  57 |     Shows configuration instructions for clients.
  58 | 
  59 |     .\run-server.ps1 -Dev
  60 |     Prepares the environment with development dependencies and starts the server.
  61 | 
  62 |     .\run-server.ps1 -Docker
  63 |     Builds and runs the server using Docker containers.
  64 | 
  65 |     .\run-server.ps1 -Docker -Follow
  66 |     Builds and runs the server using Docker containers and follows the logs.
  67 | 
  68 |     .\run-server.ps1 -Docker -Force
  69 |     Forces rebuilding of the Docker image and runs the server.
  70 | 
  71 | .NOTES
  72 |     Project Author     : BeehiveInnovations
  73 |     Script Author      : GiGiDKR (https://github.com/GiGiDKR)
  74 |     Date               : 07-05-2025
  75 |     Version            : See config.py (__version__)
  76 |     References         : https://github.com/BeehiveInnovations/zen-mcp-server
  77 | 
  78 | #>
  79 | #Requires -Version 5.1
  80 | [CmdletBinding()]
  81 | param(
  82 |     [switch]$Help,
  83 |     [switch]$Version,
  84 |     [switch]$Follow,
  85 |     [switch]$Config,
  86 |     [switch]$ClearCache,
  87 |     [switch]$SkipVenv,
  88 |     [switch]$SkipDocker,
  89 |     [switch]$Force,
  90 |     [switch]$VerboseOutput,
  91 |     [switch]$Dev,
  92 |     [switch]$Docker
  93 | )
  94 | 
  95 | # ============================================================================
  96 | # Zen MCP Server Setup Script for Windows
  97 | # 
  98 | # A Windows-compatible setup script that handles environment setup, 
  99 | # dependency installation, and configuration.
 100 | # ============================================================================
 101 | 
 102 | # Set error action preference
 103 | $ErrorActionPreference = "Stop"
 104 | 
 105 | # ----------------------------------------------------------------------------
 106 | # Constants and Configuration  
 107 | # ----------------------------------------------------------------------------
 108 | 
 109 | $script:VENV_PATH = ".zen_venv"
 110 | $script:DOCKER_CLEANED_FLAG = ".docker_cleaned"
 111 | $script:DESKTOP_CONFIG_FLAG = ".desktop_configured"
 112 | $script:LOG_DIR = "logs"
 113 | $script:LOG_FILE = "mcp_server.log"
 114 | 
 115 | # ----------------------------------------------------------------------------
 116 | # Utility Functions
 117 | # ----------------------------------------------------------------------------
 118 | 
 119 | function Write-Success {
 120 |     param([string]$Message)
 121 |     Write-Host "✓ " -ForegroundColor Green -NoNewline
 122 |     Write-Host $Message
 123 | }
 124 | 
 125 | function Write-Error {
 126 |     param([string]$Message)
 127 |     Write-Host "✗ " -ForegroundColor Red -NoNewline
 128 |     Write-Host $Message
 129 | }
 130 | 
 131 | function Write-Warning {
 132 |     param([string]$Message)
 133 |     Write-Host "⚠ " -ForegroundColor Yellow -NoNewline
 134 |     Write-Host $Message
 135 | }
 136 | 
 137 | function Write-Info {
 138 |     param([string]$Message)
 139 |     Write-Host "ℹ " -ForegroundColor Cyan -NoNewline
 140 |     Write-Host $Message
 141 | }
 142 | 
 143 | function Write-Step {
 144 |     param([string]$Message)
 145 |     Write-Host ""
 146 |     Write-Host "=== $Message ===" -ForegroundColor Cyan
 147 | }
 148 | 
 149 | # Check if command exists
 150 | function Test-Command {
 151 |     param([string]$Command)
 152 |     try {
 153 |         $null = Get-Command $Command -ErrorAction Stop
 154 |         return $true
 155 |     }
 156 |     catch {
 157 |         return $false
 158 |     }
 159 | }
 160 | 
 161 | # Alternative method to force remove locked directories
 162 | function Remove-LockedDirectory {
 163 |     param([string]$Path)
 164 |     
 165 |     if (!(Test-Path $Path)) {
 166 |         return $true
 167 |     }
 168 |     
 169 |     try {
 170 |         # Try standard removal first
 171 |         Remove-Item -Recurse -Force $Path -ErrorAction Stop
 172 |         return $true
 173 |     }
 174 |     catch {
 175 |         Write-Warning "Standard removal failed, trying alternative methods..."
 176 |         
 177 |         # Method 1: Use takeown and icacls to force ownership
 178 |         try {
 179 |             Write-Info "Attempting to take ownership of locked files..."
 180 |             takeown /F "$Path" /R /D Y 2>$null | Out-Null
 181 |             icacls "$Path" /grant administrators:F /T 2>$null | Out-Null
 182 |             Remove-Item -Recurse -Force $Path -ErrorAction Stop
 183 |             return $true
 184 |         }
 185 |         catch {
 186 |             Write-Warning "Ownership method failed"
 187 |         }
 188 |         
 189 |         # Method 2: Rename and schedule for deletion on reboot
 190 |         try {
 191 |             $tempName = "$Path.delete_$(Get-Random)"
 192 |             Write-Info "Renaming to: $tempName (will be deleted on next reboot)"
 193 |             Rename-Item $Path $tempName -ErrorAction Stop
 194 |             
 195 |             # Schedule for deletion on reboot using movefile
 196 |             if (Get-Command "schtasks" -ErrorAction SilentlyContinue) {
 197 |                 Write-Info "Scheduling for deletion on next reboot..."
 198 |             }
 199 |             
 200 |             Write-Warning "Environment renamed to $tempName and will be deleted on next reboot"
 201 |             return $true
 202 |         }
 203 |         catch {
 204 |             Write-Warning "Rename method failed"
 205 |         }
 206 |         
 207 |         # If all methods fail, return false
 208 |         return $false
 209 |     }
 210 | }
 211 | 
 212 | # Manage configuration file backups with maximum 3 files retention
 213 | function Manage-ConfigBackups {
 214 |     param(
 215 |         [string]$ConfigFilePath,
 216 |         [int]$MaxBackups = 3
 217 |     )
 218 |     
 219 |     if (!(Test-Path $ConfigFilePath)) {
 220 |         Write-Warning "Configuration file not found: $ConfigFilePath"
 221 |         return $null
 222 |     }
 223 |     
 224 |     try {
 225 |         # Create new backup with timestamp
 226 |         $timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
 227 |         $backupPath = "$ConfigFilePath.backup_$timestamp"
 228 |         Copy-Item $ConfigFilePath $backupPath -ErrorAction Stop
 229 |         
 230 |         # Find all existing backups for this config file
 231 |         $configDir = Split-Path $ConfigFilePath -Parent
 232 |         $configFileName = Split-Path $ConfigFilePath -Leaf
 233 |         $backupPattern = "$configFileName.backup_*"
 234 |         
 235 |         $existingBackups = Get-ChildItem -Path $configDir -Filter $backupPattern -ErrorAction SilentlyContinue |
 236 |         Sort-Object LastWriteTime -Descending
 237 |         
 238 |         # Keep only the most recent MaxBackups files
 239 |         if ($existingBackups.Count -gt $MaxBackups) {
 240 |             $backupsToRemove = $existingBackups | Select-Object -Skip $MaxBackups
 241 |             foreach ($backup in $backupsToRemove) {
 242 |                 try {
 243 |                     Remove-Item $backup.FullName -Force -ErrorAction Stop
 244 |                     Write-Info "Removed old backup: $($backup.Name)"
 245 |                 }
 246 |                 catch {
 247 |                     Write-Warning "Could not remove old backup: $($backup.Name)"
 248 |                 }
 249 |             }
 250 |             Write-Success "Backup retention: kept $MaxBackups most recent backups"
 251 |         }
 252 |         
 253 |         Write-Success "Backup created: $(Split-Path $backupPath -Leaf)"
 254 |         return $backupPath
 255 |         
 256 |     }
 257 |     catch {
 258 |         Write-Warning "Failed to create backup: $_"
 259 |         return $null
 260 |     }
 261 | }
 262 | 
 263 | # Get version from config.py
 264 | function Get-Version {
 265 |     try {
 266 |         if (Test-Path "config.py") {
 267 |             $content = Get-Content "config.py" -ErrorAction Stop
 268 |             $versionLine = $content | Where-Object { $_ -match '^__version__ = ' }
 269 |             if ($versionLine) {
 270 |                 return ($versionLine -replace '__version__ = "([^"]*)"', '$1')
 271 |             }
 272 |         }
 273 |         return "unknown"
 274 |     }
 275 |     catch {
 276 |         return "unknown"
 277 |     }
 278 | }
 279 | 
 280 | # Clear Python cache files
 281 | function Clear-PythonCache {
 282 |     Write-Info "Clearing Python cache files..."
 283 |     
 284 |     try {
 285 |         # Remove .pyc files
 286 |         Get-ChildItem -Path . -Recurse -Filter "*.pyc" -ErrorAction SilentlyContinue | Remove-Item -Force
 287 |         
 288 |         # Remove __pycache__ directories
 289 |         Get-ChildItem -Path . -Recurse -Name "__pycache__" -Directory -ErrorAction SilentlyContinue | 
 290 |         ForEach-Object { Remove-Item -Path $_ -Recurse -Force }
 291 |         
 292 |         Write-Success "Python cache cleared"
 293 |     }
 294 |     catch {
 295 |         Write-Warning "Could not clear all cache files: $_"
 296 |     }
 297 | }
 298 | 
 299 | # Get absolute path
 300 | function Get-AbsolutePath {
 301 |     param([string]$Path)
 302 |     
 303 |     if (Test-Path $Path) {
 304 |         # Use Resolve-Path for full resolution
 305 |         return Resolve-Path $Path
 306 |     }
 307 |     else {
 308 |         # Use unresolved method
 309 |         return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
 310 |     }
 311 | }
 312 | 
 313 | # Check Python version
 314 | function Test-PythonVersion {
 315 |     param([string]$PythonCmd)
 316 |     try {
 317 |         $version = & $PythonCmd --version 2>&1
 318 |         if ($version -match "Python (\d+)\.(\d+)") {
 319 |             $major = [int]$matches[1]
 320 |             $minor = [int]$matches[2]
 321 |             return ($major -gt 3) -or ($major -eq 3 -and $minor -ge 10)
 322 |         }
 323 |         return $false
 324 |     }
 325 |     catch {
 326 |         return $false
 327 |     }
 328 | }
 329 | 
 330 | # Find Python installation
 331 | function Find-Python {
 332 |     $pythonCandidates = @("python", "python3", "py")
 333 |     
 334 |     foreach ($cmd in $pythonCandidates) {
 335 |         if (Test-Command $cmd) {
 336 |             if (Test-PythonVersion $cmd) {
 337 |                 $version = & $cmd --version 2>&1
 338 |                 Write-Success "Found Python: $version"
 339 |                 return $cmd
 340 |             }
 341 |         }
 342 |     }
 343 |     
 344 |     # Try Windows Python Launcher with specific versions
 345 |     $pythonVersions = @("3.12", "3.11", "3.10", "3.9")
 346 |     foreach ($version in $pythonVersions) {
 347 |         $cmd = "py -$version"
 348 |         try {
 349 |             $null = Invoke-Expression "$cmd --version" 2>$null
 350 |             Write-Success "Found Python via py launcher: $cmd"
 351 |             return $cmd
 352 |         }
 353 |         catch {
 354 |             continue
 355 |         }
 356 |     }
 357 |     
 358 |     Write-Error "Python 3.10+ not found. Please install Python from https://python.org"
 359 |     return $null
 360 | }
 361 | 
 362 | # Clean up old Docker artifacts
 363 | function Cleanup-Docker {
 364 |     if (Test-Path $DOCKER_CLEANED_FLAG) {
 365 |         return
 366 |     }
 367 |     
 368 |     if (!(Test-Command "docker")) {
 369 |         return
 370 |     }
 371 |     
 372 |     try {
 373 |         $null = docker info 2>$null
 374 |     }
 375 |     catch {
 376 |         return
 377 |     }
 378 |     
 379 |     $foundArtifacts = $false
 380 |     
 381 |     # Define containers to remove
 382 |     $containers = @(
 383 |         "gemini-mcp-server",
 384 |         "gemini-mcp-redis", 
 385 |         "zen-mcp-server",
 386 |         "zen-mcp-redis",
 387 |         "zen-mcp-log-monitor"
 388 |     )
 389 |     
 390 |     # Remove containers
 391 |     foreach ($container in $containers) {
 392 |         try {
 393 |             $exists = docker ps -a --format "{{.Names}}" | Where-Object { $_ -eq $container }
 394 |             if ($exists) {
 395 |                 if (!$foundArtifacts) {
 396 |                     Write-Info "One-time Docker cleanup..."
 397 |                     $foundArtifacts = $true
 398 |                 }
 399 |                 Write-Info "  Removing container: $container"
 400 |                 docker stop $container 2>$null | Out-Null
 401 |                 docker rm $container 2>$null | Out-Null
 402 |             }
 403 |         }
 404 |         catch {
 405 |             # Ignore errors
 406 |         }
 407 |     }
 408 |     
 409 |     # Remove images
 410 |     $images = @("gemini-mcp-server:latest", "zen-mcp-server:latest")
 411 |     foreach ($image in $images) {
 412 |         try {
 413 |             $exists = docker images --format "{{.Repository}}:{{.Tag}}" | Where-Object { $_ -eq $image }
 414 |             if ($exists) {
 415 |                 if (!$foundArtifacts) {
 416 |                     Write-Info "One-time Docker cleanup..."
 417 |                     $foundArtifacts = $true
 418 |                 }
 419 |                 Write-Info "  Removing image: $image"
 420 |                 docker rmi $image 2>$null | Out-Null
 421 |             }
 422 |         }
 423 |         catch {
 424 |             # Ignore errors
 425 |         }
 426 |     }
 427 |     
 428 |     # Remove volumes
 429 |     $volumes = @("redis_data", "mcp_logs")
 430 |     foreach ($volume in $volumes) {
 431 |         try {
 432 |             $exists = docker volume ls --format "{{.Name}}" | Where-Object { $_ -eq $volume }
 433 |             if ($exists) {
 434 |                 if (!$foundArtifacts) {
 435 |                     Write-Info "One-time Docker cleanup..."
 436 |                     $foundArtifacts = $true
 437 |                 }
 438 |                 Write-Info "  Removing volume: $volume"
 439 |                 docker volume rm $volume 2>$null | Out-Null
 440 |             }
 441 |         }
 442 |         catch {
 443 |             # Ignore errors
 444 |         }
 445 |     }
 446 |     
 447 |     if ($foundArtifacts) {
 448 |         Write-Success "Docker cleanup complete"
 449 |     }
 450 |     
 451 |     New-Item -Path $DOCKER_CLEANED_FLAG -ItemType File -Force | Out-Null
 452 | }
 453 | 
 454 | # Validate API keys
 455 | function Test-ApiKeys {
 456 |     Write-Step "Validating API Keys"
 457 |     
 458 |     if (!(Test-Path ".env")) {
 459 |         Write-Warning "No .env file found. API keys should be configured."
 460 |         return $false
 461 |     }
 462 |     
 463 |     $envContent = Get-Content ".env"
 464 |     $hasValidKey = $false
 465 |     
 466 |     $keyPatterns = @{
 467 |         "GEMINI_API_KEY"     = "AIza[0-9A-Za-z-_]{35}"
 468 |         "OPENAI_API_KEY"     = "sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20}"
 469 |         "XAI_API_KEY"        = "xai-[a-zA-Z0-9-_]+"
 470 |         "OPENROUTER_API_KEY" = "sk-or-[a-zA-Z0-9-_]+"
 471 |     }
 472 |     
 473 |     foreach ($line in $envContent) {
 474 |         if ($line -match '^([^#][^=]*?)=(.*)$') {
 475 |             $key = $matches[1].Trim()
 476 |             $value = $matches[2].Trim() -replace '^["'']|["'']$', ''
 477 |             
 478 |             if ($keyPatterns.ContainsKey($key) -and $value -ne "your_${key.ToLower()}_here" -and $value.Length -gt 10) {
 479 |                 Write-Success "Found valid $key"
 480 |                 $hasValidKey = $true
 481 |             }
 482 |         }
 483 |     }
 484 |     
 485 |     if (!$hasValidKey) {
 486 |         Write-Warning "No valid API keys found in .env file"
 487 |         Write-Info "Please edit .env file with your actual API keys"
 488 |         return $false
 489 |     }
 490 |     
 491 |     return $true
 492 | }
 493 | 
 494 | # Check if uv is available
 495 | function Test-Uv {
 496 |     return Test-Command "uv"
 497 | }
 498 | 
 499 | # Setup environment using uv-first approach
 500 | function Initialize-Environment {
 501 |     Write-Step "Setting up Python Environment"
 502 |     
 503 |     # Try uv first for faster package management
 504 |     if (Test-Uv) {
 505 |         Write-Info "Using uv for faster package management..."
 506 |         
 507 |         if (Test-Path $VENV_PATH) {
 508 |             if ($Force) {
 509 |                 Write-Warning "Removing existing environment..."
 510 |                 Remove-Item -Recurse -Force $VENV_PATH
 511 |             }
 512 |             else {
 513 |                 Write-Success "Virtual environment already exists"
 514 |                 $pythonPath = "$VENV_PATH\Scripts\python.exe"
 515 |                 if (Test-Path $pythonPath) {
 516 |                     return Get-AbsolutePath $pythonPath
 517 |                 }
 518 |             }
 519 |         }
 520 |         
 521 |         try {
 522 |             Write-Info "Creating virtual environment with uv..."
 523 |             uv venv $VENV_PATH --python 3.12
 524 |             if ($LASTEXITCODE -eq 0) {
 525 |                 Write-Success "Environment created with uv"
 526 |                 return Get-AbsolutePath "$VENV_PATH\Scripts\python.exe"
 527 |             }
 528 |         }
 529 |         catch {
 530 |             Write-Warning "uv failed, falling back to venv"
 531 |         }
 532 |     }
 533 |     
 534 |     # Fallback to standard venv
 535 |     $pythonCmd = Find-Python
 536 |     if (!$pythonCmd) {
 537 |         throw "Python 3.10+ not found"
 538 |     }
 539 |     
 540 |     if (Test-Path $VENV_PATH) {
 541 |         if ($Force) {
 542 |             Write-Warning "Removing existing environment..."
 543 |             try {
 544 |                 # Stop any Python processes that might be using the venv
 545 |                 Get-Process python* -ErrorAction SilentlyContinue | Where-Object { $_.Path -like "*$VENV_PATH*" } | Stop-Process -Force -ErrorAction SilentlyContinue
 546 |                 
 547 |                 # Wait a moment for processes to terminate
 548 |                 Start-Sleep -Seconds 2
 549 |                 
 550 |                 # Use the robust removal function
 551 |                 if (Remove-LockedDirectory $VENV_PATH) {
 552 |                     Write-Success "Existing environment removed"
 553 |                 }
 554 |                 else {
 555 |                     throw "Unable to remove existing environment. Please restart your computer and try again."
 556 |                 }
 557 |                 
 558 |             }
 559 |             catch {
 560 |                 Write-Error "Failed to remove existing environment: $_"
 561 |                 Write-Host ""
 562 |                 Write-Host "Try these solutions:" -ForegroundColor Yellow
 563 |                 Write-Host "1. Close all terminals and VS Code instances" -ForegroundColor White
 564 |                 Write-Host "2. Run: Get-Process python* | Stop-Process -Force" -ForegroundColor White
 565 |                 Write-Host "3. Manually delete: $VENV_PATH" -ForegroundColor White
 566 |                 Write-Host "4. Then run the script again" -ForegroundColor White
 567 |                 exit 1
 568 |             }
 569 |         }
 570 |         else {
 571 |             Write-Success "Virtual environment already exists"
 572 |             return Get-AbsolutePath "$VENV_PATH\Scripts\python.exe"
 573 |         }
 574 |     }
 575 |     
 576 |     Write-Info "Creating virtual environment with $pythonCmd..."
 577 |     if ($pythonCmd.StartsWith("py ")) {
 578 |         Invoke-Expression "$pythonCmd -m venv $VENV_PATH"
 579 |     }
 580 |     else {
 581 |         & $pythonCmd -m venv $VENV_PATH
 582 |     }
 583 |     
 584 |     if ($LASTEXITCODE -ne 0) {
 585 |         throw "Failed to create virtual environment"
 586 |     }
 587 |     
 588 |     Write-Success "Virtual environment created"
 589 |     return Get-AbsolutePath "$VENV_PATH\Scripts\python.exe"
 590 | }
 591 | 
 592 | # Setup virtual environment (legacy function for compatibility)
 593 | function Initialize-VirtualEnvironment {
 594 |     Write-Step "Setting up Python Virtual Environment"
 595 |     
 596 |     if (!$SkipVenv -and (Test-Path $VENV_PATH)) {
 597 |         if ($Force) {
 598 |             Write-Warning "Removing existing virtual environment..."
 599 |             try {
 600 |                 # Stop any Python processes that might be using the venv
 601 |                 Get-Process python* -ErrorAction SilentlyContinue | Where-Object { $_.Path -like "*$VENV_PATH*" } | Stop-Process -Force -ErrorAction SilentlyContinue
 602 |                 
 603 |                 # Wait a moment for processes to terminate
 604 |                 Start-Sleep -Seconds 2
 605 |                 
 606 |                 # Use the robust removal function
 607 |                 if (Remove-LockedDirectory $VENV_PATH) {
 608 |                     Write-Success "Existing environment removed"
 609 |                 }
 610 |                 else {
 611 |                     throw "Unable to remove existing environment. Please restart your computer and try again."
 612 |                 }
 613 |                 
 614 |             }
 615 |             catch {
 616 |                 Write-Error "Failed to remove existing environment: $_"
 617 |                 Write-Host ""
 618 |                 Write-Host "Try these solutions:" -ForegroundColor Yellow
 619 |                 Write-Host "1. Close all terminals and VS Code instances" -ForegroundColor White
 620 |                 Write-Host "2. Run: Get-Process python* | Stop-Process -Force" -ForegroundColor White
 621 |                 Write-Host "3. Manually delete: $VENV_PATH" -ForegroundColor White
 622 |                 Write-Host "4. Then run the script again" -ForegroundColor White
 623 |                 exit 1
 624 |             }
 625 |         }
 626 |         else {
 627 |             Write-Success "Virtual environment already exists"
 628 |             return
 629 |         }
 630 |     }
 631 |     
 632 |     if ($SkipVenv) {
 633 |         Write-Warning "Skipping virtual environment setup"
 634 |         return
 635 |     }
 636 |     
 637 |     $pythonCmd = Find-Python
 638 |     if (!$pythonCmd) {
 639 |         Write-Error "Python 3.10+ not found. Please install Python from https://python.org"
 640 |         exit 1
 641 |     }
 642 |     
 643 |     Write-Info "Using Python: $pythonCmd"
 644 |     Write-Info "Creating virtual environment..."
 645 |     
 646 |     try {
 647 |         if ($pythonCmd.StartsWith("py ")) {
 648 |             Invoke-Expression "$pythonCmd -m venv $VENV_PATH"
 649 |         }
 650 |         else {
 651 |             & $pythonCmd -m venv $VENV_PATH
 652 |         }
 653 |         
 654 |         if ($LASTEXITCODE -ne 0) {
 655 |             throw "Failed to create virtual environment"
 656 |         }
 657 |         
 658 |         Write-Success "Virtual environment created"
 659 |     }
 660 |     catch {
 661 |         Write-Error "Failed to create virtual environment: $_"
 662 |         exit 1
 663 |     }
 664 | }
 665 | 
 666 | # Install dependencies function - Simplified uv-first approach
 667 | function Install-Dependencies {
 668 |     param(
 669 |         [Parameter(Mandatory = $true)]
 670 |         [string]$PythonPath,
 671 |         [switch]$InstallDevDependencies = $false
 672 |     )
 673 |     
 674 |     Write-Step "Installing Dependencies"
 675 | 
 676 |     # Build requirements files list
 677 |     $requirementsFiles = @("requirements.txt")
 678 |     if ($InstallDevDependencies) {
 679 |         if (Test-Path "requirements-dev.txt") {
 680 |             $requirementsFiles += "requirements-dev.txt"
 681 |             Write-Info "Including development dependencies from requirements-dev.txt"
 682 |         }
 683 |         else {
 684 |             Write-Warning "Development dependencies requested but requirements-dev.txt not found"
 685 |         }
 686 |     }
 687 | 
 688 |     # Try uv first for faster package management
 689 |     $useUv = Test-Uv
 690 |     if ($useUv) {
 691 |         Write-Info "Installing dependencies with uv (fast)..."
 692 |         try {
 693 |             foreach ($file in $requirementsFiles) {
 694 |                 Write-Info "Installing from $file with uv..."
 695 |                 $uv = (Get-Command uv -ErrorAction Stop).Source
 696 |                 $arguments = @('pip', 'install', '-r', $file, '--python', $PythonPath)
 697 |                 $proc = Start-Process -FilePath $uv -ArgumentList $arguments -NoNewWindow -Wait -PassThru
 698 | 
 699 |                 if ($proc.ExitCode -ne 0) { 
 700 |                     throw "uv failed to install $file with exit code $($proc.ExitCode)" 
 701 |                 }
 702 | 
 703 |             }
 704 |             Write-Success "Dependencies installed successfully with uv"
 705 |             return
 706 |         }
 707 |         catch {
 708 |             Write-Warning "uv installation failed: $_. Falling back to pip"
 709 |             $useUv = $false
 710 |         }
 711 |     }
 712 | 
 713 |     # Fallback to pip
 714 |     Write-Info "Installing dependencies with pip..."
 715 |     $pipCmd = Join-Path (Split-Path $PythonPath -Parent) "pip.exe"
 716 |     
 717 |     try {
 718 |         # Upgrade pip first
 719 |         & $pipCmd install --upgrade pip | Out-Null
 720 |     }
 721 |     catch {
 722 |         Write-Warning "Could not upgrade pip, continuing..."
 723 |     }
 724 | 
 725 |     try {
 726 |         foreach ($file in $requirementsFiles) {
 727 |             Write-Info "Installing from $file with pip..."
 728 |             & $pipCmd install -r $file
 729 |             if ($LASTEXITCODE -ne 0) {
 730 |                 throw "pip failed to install $file"
 731 |             }
 732 |         }
 733 |         Write-Success "Dependencies installed successfully with pip"
 734 |     }
 735 |     catch {
 736 |         Write-Error "Failed to install dependencies with pip: $_"
 737 |         exit 1
 738 |     }
 739 | }
 740 | 
 741 | # ----------------------------------------------------------------------------
 742 | # Docker Functions
 743 | # ============================================================================
 744 | 
 745 | # Test Docker availability and requirements
 746 | function Test-DockerRequirements {
 747 |     Write-Step "Checking Docker Requirements"
 748 |     
 749 |     if (!(Test-Command "docker")) {
 750 |         Write-Error "Docker not found. Please install Docker Desktop from https://docker.com"
 751 |         return $false
 752 |     }
 753 |     
 754 |     try {
 755 |         $null = docker version 2>$null
 756 |         Write-Success "Docker is installed and running"
 757 |     }
 758 |     catch {
 759 |         Write-Error "Docker is installed but not running. Please start Docker Desktop."
 760 |         return $false
 761 |     }
 762 |     
 763 |     if (!(Test-Command "docker-compose")) {
 764 |         Write-Warning "docker-compose not found. Trying docker compose..."
 765 |         try {
 766 |             $null = docker compose version 2>$null
 767 |             Write-Success "Docker Compose (v2) is available"
 768 |             return $true
 769 |         }
 770 |         catch {
 771 |             Write-Error "Docker Compose not found. Please install Docker Compose."
 772 |             return $false
 773 |         }
 774 |     }
 775 |     else {
 776 |         Write-Success "Docker Compose is available"
 777 |         return $true
 778 |     }
 779 | }
 780 | 
 781 | # Build Docker image
 782 | function Build-DockerImage {
 783 |     param([switch]$Force = $false)
 784 |     
 785 |     Write-Step "Building Docker Image"
 786 |     
 787 |     # Check if image exists
 788 |     try {
 789 |         $imageExists = docker images --format "{{.Repository}}:{{.Tag}}" | Where-Object { $_ -eq "zen-mcp-server:latest" }
 790 |         if ($imageExists -and !$Force) {
 791 |             Write-Success "Docker image already exists. Use -Force to rebuild."
 792 |             return $true
 793 |         }
 794 |     }
 795 |     catch {
 796 |         # Continue if command fails
 797 |     }
 798 |     
 799 |     if ($Force -and $imageExists) {
 800 |         Write-Info "Forcing rebuild of Docker image..."
 801 |         try {
 802 |             docker rmi zen-mcp-server:latest 2>$null
 803 |         }
 804 |         catch {
 805 |             Write-Warning "Could not remove existing image, continuing..."
 806 |         }
 807 |     }
 808 |     
 809 |     Write-Info "Building Docker image from Dockerfile..."
 810 |     try {
 811 |         $buildArgs = @()
 812 |         if ($Dev) {
 813 |             # For development builds, we could add specific build args
 814 |             Write-Info "Building with development support..."
 815 |         }
 816 |         
 817 |         docker build -t zen-mcp-server:latest .
 818 |         if ($LASTEXITCODE -ne 0) {
 819 |             throw "Docker build failed"
 820 |         }
 821 |         
 822 |         Write-Success "Docker image built successfully"
 823 |         return $true
 824 |     }
 825 |     catch {
 826 |         Write-Error "Failed to build Docker image: $_"
 827 |         return $false
 828 |     }
 829 | }
 830 | 
 831 | # Prepare Docker environment file
 832 | function Initialize-DockerEnvironment {
 833 |     Write-Step "Preparing Docker Environment"
 834 |     
 835 |     # Ensure .env file exists
 836 |     if (!(Test-Path ".env")) {
 837 |         Write-Warning "No .env file found. Creating default .env file..."
 838 |         
 839 |         $defaultEnv = @"
 840 | # API Keys - Replace with your actual keys
 841 | GEMINI_API_KEY=your_gemini_api_key_here
 842 | GOOGLE_API_KEY=your_google_api_key_here
 843 | OPENAI_API_KEY=your_openai_api_key_here
 844 | ANTHROPIC_API_KEY=your_anthropic_api_key_here
 845 | XAI_API_KEY=your_xai_api_key_here
 846 | DIAL_API_KEY=your_dial_api_key_here
 847 | DIAL_API_HOST=your_dial_api_host_here
 848 | DIAL_API_VERSION=your_dial_api_version_here
 849 | OPENROUTER_API_KEY=your_openrouter_api_key_here
 850 | CUSTOM_API_URL=your_custom_api_url_here
 851 | CUSTOM_API_KEY=your_custom_api_key_here
 852 | CUSTOM_MODEL_NAME=your_custom_model_name_here
 853 | 
 854 | # Server Configuration
 855 | DEFAULT_MODEL=auto
 856 | LOG_LEVEL=INFO
 857 | LOG_MAX_SIZE=10MB
 858 | LOG_BACKUP_COUNT=5
 859 | DEFAULT_THINKING_MODE_THINKDEEP=high
 860 | 
 861 | # Optional Advanced Settings
 862 | #DISABLED_TOOLS=
 863 | #MAX_MCP_OUTPUT_TOKENS=
 864 | #TZ=UTC
 865 | "@
 866 |         
 867 |         $defaultEnv | Out-File -FilePath ".env" -Encoding UTF8
 868 |         Write-Success "Default .env file created"
 869 |         Write-Warning "Please edit .env file with your actual API keys"
 870 |     }
 871 |     else {
 872 |         Write-Success ".env file exists"
 873 |     }
 874 |     
 875 |     # Create logs directory for volume mount
 876 |     Initialize-Logging
 877 |     
 878 |     return $true
 879 | }
 880 | 
 881 | # Start Docker services
 882 | function Start-DockerServices {
 883 |     param([switch]$Follow = $false)
 884 |     
 885 |     Write-Step "Starting Docker Services"
 886 |     
 887 |     # Check if docker-compose.yml exists
 888 |     if (!(Test-Path "docker-compose.yml")) {
 889 |         Write-Error "docker-compose.yml not found in current directory"
 890 |         return $false
 891 |     }
 892 |     
 893 |     try {
 894 |         # Stop any existing services
 895 |         Write-Info "Stopping any existing services..."
 896 |         if (Test-Command "docker-compose") {
 897 |             docker-compose down 2>$null
 898 |         }
 899 |         else {
 900 |             docker compose down 2>$null
 901 |         }
 902 |         
 903 |         # Start services
 904 |         Write-Info "Starting Zen MCP Server with Docker Compose..."
 905 |         if (Test-Command "docker-compose") {
 906 |             if ($Follow) {
 907 |                 docker-compose up --build
 908 |             }
 909 |             else {
 910 |                 docker-compose up -d --build
 911 |             }
 912 |         }
 913 |         else {
 914 |             if ($Follow) {
 915 |                 docker compose up --build
 916 |             }
 917 |             else {
 918 |                 docker compose up -d --build
 919 |             }
 920 |         }
 921 |         
 922 |         if ($LASTEXITCODE -ne 0) {
 923 |             throw "Failed to start Docker services"
 924 |         }
 925 |         
 926 |         if (!$Follow) {
 927 |             Write-Success "Docker services started successfully"
 928 |             Write-Info "Container name: zen-mcp-server"
 929 |             Write-Host ""
 930 |             Write-Host "To view logs: " -NoNewline
 931 |             Write-Host "docker logs -f zen-mcp-server" -ForegroundColor Yellow
 932 |             Write-Host "To stop: " -NoNewline
 933 |             Write-Host "docker-compose down" -ForegroundColor Yellow
 934 |         }
 935 |         
 936 |         return $true
 937 |     }
 938 |     catch {
 939 |         Write-Error "Failed to start Docker services: $_"
 940 |         return $false
 941 |     }
 942 | }
 943 | 
 944 | # Get Docker container status
 945 | function Get-DockerStatus {
 946 |     try {
 947 |         $containerStatus = docker ps --filter "name=zen-mcp-server" --format "{{.Status}}"
 948 |         if ($containerStatus) {
 949 |             Write-Success "Container status: $containerStatus"
 950 |             return $true
 951 |         }
 952 |         else {
 953 |             Write-Warning "Container not running"
 954 |             return $false
 955 |         }
 956 |     }
 957 |     catch {
 958 |         Write-Warning "Could not get container status: $_"
 959 |         return $false
 960 |     }
 961 | }
 962 | 
 963 | # ============================================================================
 964 | # End Docker Functions
 965 | # ============================================================================
 966 | 
 967 | # Setup logging directory
 968 | function Initialize-Logging {
 969 |     Write-Step "Setting up Logging"
 970 |     
 971 |     if (!(Test-Path $LOG_DIR)) {
 972 |         New-Item -ItemType Directory -Path $LOG_DIR -Force | Out-Null
 973 |         Write-Success "Logs directory created"
 974 |     }
 975 |     else {
 976 |         Write-Success "Logs directory already exists"
 977 |     }
 978 | }
 979 | 
 980 | # Check Docker
 981 | function Test-Docker {
 982 |     Write-Step "Checking Docker Setup"
 983 |     
 984 |     if ($SkipDocker) {
 985 |         Write-Warning "Skipping Docker checks"
 986 |         return
 987 |     }
 988 |     
 989 |     if (Test-Command "docker") {
 990 |         try {
 991 |             $null = docker version 2>$null
 992 |             Write-Success "Docker is installed and running"
 993 |             
 994 |             if (Test-Command "docker-compose") {
 995 |                 Write-Success "Docker Compose is available"
 996 |             }
 997 |             else {
 998 |                 Write-Warning "Docker Compose not found. Install Docker Desktop for Windows."
 999 |             }
1000 |         }
1001 |         catch {
1002 |             Write-Warning "Docker is installed but not running. Please start Docker Desktop."
1003 |         }
1004 |     }
1005 |     else {
1006 |         Write-Warning "Docker not found. Install Docker Desktop from https://docker.com"
1007 |     }
1008 | }
1009 | 
1010 | # ----------------------------------------------------------------------------
1011 | # MCP Client Configuration System
1012 | # ----------------------------------------------------------------------------
1013 | 
1014 | # Centralized MCP client definitions
1015 | $script:McpClientDefinitions = @(
1016 |     @{
1017 |         Name           = "Claude Desktop"
1018 |         DetectionPath  = "$env:APPDATA\Claude\claude_desktop_config.json"
1019 |         DetectionType  = "Path"
1020 |         ConfigPath     = "$env:APPDATA\Claude\claude_desktop_config.json"
1021 |         ConfigJsonPath = "mcpServers.zen"
1022 |         NeedsConfigDir = $true
1023 |     },
1024 |     @{
1025 |         Name             = "VSCode"
1026 |         DetectionCommand = "code"
1027 |         DetectionType    = "Command"
1028 |         ConfigPath       = "$env:APPDATA\Code\User\settings.json"
1029 |         ConfigJsonPath   = "mcp.servers.zen"
1030 |         IsVSCode         = $true
1031 |     },
1032 |     @{
1033 |         Name             = "VSCode Insiders"
1034 |         DetectionCommand = "code-insiders"
1035 |         DetectionType    = "Command"
1036 |         ConfigPath       = "$env:APPDATA\Code - Insiders\User\mcp.json"
1037 |         ConfigJsonPath   = "servers.zen"
1038 |         IsVSCodeInsiders = $true
1039 |     },
1040 |     @{
1041 |         Name             = "Cursor"
1042 |         DetectionCommand = "cursor"
1043 |         DetectionType    = "Command"
1044 |         ConfigPath       = "$env:USERPROFILE\.cursor\mcp.json"
1045 |         ConfigJsonPath   = "mcpServers.zen"
1046 |     },
1047 |     @{
1048 |         Name           = "Windsurf"
1049 |         DetectionPath  = "$env:USERPROFILE\.codeium\windsurf"
1050 |         DetectionType  = "Path"
1051 |         ConfigPath     = "$env:USERPROFILE\.codeium\windsurf\mcp_config.json"
1052 |         ConfigJsonPath = "mcpServers.zen"
1053 |     },
1054 |     @{
1055 |         Name           = "Trae"
1056 |         DetectionPath  = "$env:APPDATA\Trae"
1057 |         DetectionType  = "Path"
1058 |         ConfigPath     = "$env:APPDATA\Trae\User\mcp.json"
1059 |         ConfigJsonPath = "mcpServers.zen"
1060 |     }
1061 | )
1062 | 
1063 | # Docker MCP configuration template (legacy, kept for backward compatibility)
1064 | $script:DockerMcpConfig = @{
1065 |     command = "docker"
1066 |     args    = @("exec", "-i", "zen-mcp-server", "python", "server.py")
1067 |     type    = "stdio"
1068 | }
1069 | 
1070 | # Generate Docker MCP configuration using docker run (recommended for all clients)
1071 | function Get-DockerMcpConfigRun {
1072 |     param([string]$ServerPath)
1073 |     
1074 |     $scriptDir = Split-Path $ServerPath -Parent
1075 |     $envFile = Join-Path $scriptDir ".env"
1076 |     
1077 |     return @{
1078 |         command = "docker"
1079 |         args    = @("run", "--rm", "-i", "--env-file", $envFile, "zen-mcp-server:latest", "python", "server.py")
1080 |         type    = "stdio"
1081 |     }
1082 | }
1083 | 
1084 | # Generate Python MCP configuration
1085 | function Get-PythonMcpConfig {
1086 |     param([string]$PythonPath, [string]$ServerPath)
1087 |     return @{
1088 |         command = $PythonPath
1089 |         args    = @($ServerPath)
1090 |         type    = "stdio"
1091 |     }
1092 | }
1093 | 
1094 | # Check if client uses mcp.json format with servers structure
1095 | function Test-McpJsonFormat {
1096 |     param([hashtable]$Client)
1097 |     
1098 |     $configFileName = Split-Path $Client.ConfigPath -Leaf
1099 |     return $configFileName -eq "mcp.json"
1100 | }
1101 | 
1102 | # Check if client uses the new VS Code Insiders format (servers instead of mcpServers)
1103 | function Test-VSCodeInsidersFormat {
1104 |     param([hashtable]$Client)
1105 |     
1106 |     return $Client.IsVSCodeInsiders -eq $true -and $Client.ConfigJsonPath -eq "servers.zen"
1107 | }
1108 | 
1109 | # Analyze existing MCP configuration to determine type (Python or Docker)
1110 | function Get-ExistingMcpConfigType {
1111 |     param(
1112 |         [Parameter(Mandatory = $true)]
1113 |         [hashtable]$Client,
1114 |         [Parameter(Mandatory = $true)]
1115 |         [string]$ConfigPath
1116 |     )
1117 |     
1118 |     if (!(Test-Path $ConfigPath)) {
1119 |         return @{
1120 |             Exists  = $false
1121 |             Type    = "None"
1122 |             Details = "No configuration found"
1123 |         }
1124 |     }
1125 |     
1126 |     try {
1127 |         $content = Get-Content $ConfigPath -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
1128 |         if (!$content) {
1129 |             return @{
1130 |                 Exists  = $false
1131 |                 Type    = "None"
1132 |                 Details = "Invalid JSON configuration"
1133 |             }
1134 |         }
1135 |         
1136 |         # Navigate to zen configuration
1137 |         $pathParts = $Client.ConfigJsonPath.Split('.')
1138 |         $zenKey = $pathParts[-1]
1139 |         $parentPath = $pathParts[0..($pathParts.Length - 2)]
1140 |         
1141 |         $targetObject = $content
1142 |         foreach ($key in $parentPath) {
1143 |             if (!$targetObject.PSObject.Properties[$key]) {
1144 |                 return @{
1145 |                     Exists  = $false
1146 |                     Type    = "None"
1147 |                     Details = "Configuration structure not found"
1148 |                 }
1149 |             }
1150 |             $targetObject = $targetObject.$key
1151 |         }
1152 |         
1153 |         if (!$targetObject.PSObject.Properties[$zenKey]) {
1154 |             return @{
1155 |                 Exists  = $false
1156 |                 Type    = "None"
1157 |                 Details = "Zen configuration not found"
1158 |             }
1159 |         }
1160 |         
1161 |         $zenConfig = $targetObject.$zenKey
1162 |         
1163 |         # Analyze configuration type
1164 |         if ($zenConfig.command -eq "docker") {
1165 |             $dockerType = "Unknown"
1166 |             $details = "Docker configuration"
1167 |             
1168 |             if ($zenConfig.args -and $zenConfig.args.Count -gt 0) {
1169 |                 if ($zenConfig.args[0] -eq "run") {
1170 |                     $dockerType = "Docker Run"
1171 |                     $details = "Docker run (dedicated container)"
1172 |                 }
1173 |                 elseif ($zenConfig.args[0] -eq "exec") {
1174 |                     $dockerType = "Docker Exec"
1175 |                     $details = "Docker exec (existing container)"
1176 |                 }
1177 |                 else {
1178 |                     $details = "Docker ($($zenConfig.args[0]))"
1179 |                 }
1180 |             }
1181 |             
1182 |             return @{
1183 |                 Exists  = $true
1184 |                 Type    = "Docker"
1185 |                 SubType = $dockerType
1186 |                 Details = $details
1187 |                 Command = $zenConfig.command
1188 |                 Args    = $zenConfig.args
1189 |             }
1190 |         }
1191 |         elseif ($zenConfig.command -and $zenConfig.command.EndsWith("python.exe")) {
1192 |             $pythonType = "Python"
1193 |             $details = "Python virtual environment"
1194 |             
1195 |             if ($zenConfig.command.Contains(".zen_venv")) {
1196 |                 $details = "Python (zen virtual environment)"
1197 |             }
1198 |             elseif ($zenConfig.command.Contains("venv")) {
1199 |                 $details = "Python (virtual environment)"
1200 |             }
1201 |             else {
1202 |                 $details = "Python (system installation)"
1203 |             }
1204 |             
1205 |             return @{
1206 |                 Exists  = $true
1207 |                 Type    = "Python"
1208 |                 SubType = $pythonType
1209 |                 Details = $details
1210 |                 Command = $zenConfig.command
1211 |                 Args    = $zenConfig.args
1212 |             }
1213 |         }
1214 |         else {
1215 |             return @{
1216 |                 Exists  = $true
1217 |                 Type    = "Unknown"
1218 |                 Details = "Unknown configuration type: $($zenConfig.command)"
1219 |                 Command = $zenConfig.command
1220 |                 Args    = $zenConfig.args
1221 |             }
1222 |         }
1223 |         
1224 |     }
1225 |     catch {
1226 |         return @{
1227 |             Exists  = $false
1228 |             Type    = "Error"
1229 |             Details = "Error reading configuration: $_"
1230 |         }
1231 |     }
1232 | }
1233 | 
1234 | # Generic MCP client configuration function
1235 | function Configure-McpClient {
1236 |     param(
1237 |         [Parameter(Mandatory = $true)]
1238 |         [hashtable]$Client,
1239 |         [Parameter(Mandatory = $true)]
1240 |         [bool]$UseDocker,
1241 |         [string]$PythonPath = "",
1242 |         [string]$ServerPath = ""
1243 |     )
1244 | 
1245 |     Write-Step "Checking $($Client.Name) Integration"
1246 | 
1247 |     # Client detection
1248 |     $detected = $false
1249 |     if ($Client.DetectionType -eq "Command" -and (Test-Command $Client.DetectionCommand)) {
1250 |         $detected = $true
1251 |     }
1252 |     elseif ($Client.DetectionType -eq "Path" -and (Test-Path ($Client.DetectionPath -as [string]))) {
1253 |         $detected = $true
1254 |     }
1255 | 
1256 |     if (!$detected) {
1257 |         Write-Info "$($Client.Name) not detected - skipping integration"
1258 |         return
1259 |     }
1260 |     Write-Info "Found $($Client.Name)"
1261 | 
1262 |     # Handle VSCode special logic for profiles
1263 |     $configPath = $Client.ConfigPath
1264 |     if ($Client.IsVSCode) {
1265 |         $userPath = Split-Path $configPath -Parent
1266 |         if (!(Test-Path $userPath)) {
1267 |             Write-Warning "$($Client.Name) user directory not found. Skipping."
1268 |             return
1269 |         }
1270 |         
1271 |         # Find most recent settings.json (default or profile)
1272 |         $settingsFiles = @()
1273 |         $defaultSettings = $configPath
1274 |         if (Test-Path $defaultSettings) {
1275 |             $settingsFiles += @{
1276 |                 Path         = $defaultSettings
1277 |                 LastModified = (Get-Item $defaultSettings).LastWriteTime
1278 |             }
1279 |         }
1280 |         
1281 |         $profilesPath = Join-Path $userPath "profiles"
1282 |         if (Test-Path $profilesPath) {
1283 |             Get-ChildItem $profilesPath -Directory | ForEach-Object {
1284 |                 $profileSettings = Join-Path $_.FullName "settings.json"
1285 |                 if (Test-Path $profileSettings) {
1286 |                     $settingsFiles += @{
1287 |                         Path         = $profileSettings
1288 |                         LastModified = (Get-Item $profileSettings).LastWriteTime
1289 |                     }
1290 |                 }
1291 |             }
1292 |         }
1293 |         
1294 |         if ($settingsFiles.Count -gt 0) {
1295 |             $configPath = ($settingsFiles | Sort-Object LastModified -Descending | Select-Object -First 1).Path
1296 |         }
1297 |     }
1298 | 
1299 |     # Handle VSCode Insiders special logic for profiles (uses mcp.json)
1300 |     if ($Client.IsVSCodeInsiders) {
1301 |         $userPath = Split-Path $configPath -Parent
1302 |         if (!(Test-Path $userPath)) {
1303 |             Write-Warning "$($Client.Name) user directory not found. Skipping."
1304 |             return
1305 |         }
1306 |         
1307 |         # Find most recent mcp.json (default or profile)
1308 |         $mcpFiles = @()
1309 |         $defaultMcp = $configPath
1310 |         if (Test-Path $defaultMcp) {
1311 |             $mcpFiles += @{
1312 |                 Path         = $defaultMcp
1313 |                 LastModified = (Get-Item $defaultMcp).LastWriteTime
1314 |             }
1315 |         }
1316 |         
1317 |         $profilesPath = Join-Path $userPath "profiles"
1318 |         if (Test-Path $profilesPath) {
1319 |             Get-ChildItem $profilesPath -Directory | ForEach-Object {
1320 |                 $profileMcp = Join-Path $_.FullName "mcp.json"
1321 |                 if (Test-Path $profileMcp) {
1322 |                     $mcpFiles += @{
1323 |                         Path         = $profileMcp
1324 |                         LastModified = (Get-Item $profileMcp).LastWriteTime
1325 |                     }
1326 |                 }
1327 |             }
1328 |         }
1329 |         
1330 |         if ($mcpFiles.Count -gt 0) {
1331 |             $configPath = ($mcpFiles | Sort-Object LastModified -Descending | Select-Object -First 1).Path
1332 |         }
1333 |     }
1334 | 
1335 |     # Check if already configured and analyze existing configuration
1336 |     $existingConfig = Get-ExistingMcpConfigType -Client $Client -ConfigPath $configPath
1337 |     $newConfigType = if ($UseDocker) { "Docker" } else { "Python" }
1338 |     
1339 |     if ($existingConfig.Exists) {
1340 |         Write-Info "Found existing Zen MCP configuration in $($Client.Name)"
1341 |         Write-Info "  Current: $($existingConfig.Details)"
1342 |         Write-Info "  New: $newConfigType configuration"
1343 |         
1344 |         if ($existingConfig.Type -eq $newConfigType) {
1345 |             Write-Warning "Same configuration type ($($existingConfig.Type)) already exists"
1346 |             $response = Read-Host "`nOverwrite existing $($existingConfig.Type) configuration? (y/N)"
1347 |         }
1348 |         else {
1349 |             Write-Warning "Different configuration type detected"
1350 |             Write-Info "  Replacing: $($existingConfig.Type) → $newConfigType"
1351 |             $response = Read-Host "`nReplace $($existingConfig.Type) with $newConfigType configuration? (y/N)"
1352 |         }
1353 |         
1354 |         if ($response -ne 'y' -and $response -ne 'Y') {
1355 |             Write-Info "Keeping existing configuration in $($Client.Name)"
1356 |             return
1357 |         }
1358 |         
1359 |         Write-Info "Proceeding with configuration update..."
1360 |     }
1361 |     else {
1362 |         # User confirmation for new installation
1363 |         $response = Read-Host "`nConfigure Zen MCP for $($Client.Name) (mode: $newConfigType)? (y/N)"
1364 |         if ($response -ne 'y' -and $response -ne 'Y') {
1365 |             Write-Info "Skipping $($Client.Name) integration"
1366 |             return
1367 |         }
1368 |     }
1369 | 
1370 |     try {
1371 |         # Create config directory if needed
1372 |         $configDir = Split-Path $configPath -Parent
1373 |         if (!(Test-Path $configDir)) {
1374 |             New-Item -ItemType Directory -Path $configDir -Force | Out-Null
1375 |         }
1376 | 
1377 |         # Backup existing config
1378 |         if (Test-Path $configPath) {
1379 |             Manage-ConfigBackups -ConfigFilePath $configPath
1380 |         }
1381 | 
1382 |         # Read or create config
1383 |         $config = New-Object PSObject
1384 |         $usesMcpJsonFormat = Test-McpJsonFormat -Client $Client
1385 |         $usesVSCodeInsidersFormat = Test-VSCodeInsidersFormat -Client $Client
1386 |         
1387 |         if (Test-Path $configPath) {
1388 |             $fileContent = Get-Content $configPath -Raw
1389 |             if ($fileContent.Trim()) {
1390 |                 $config = $fileContent | ConvertFrom-Json -ErrorAction SilentlyContinue
1391 |             }
1392 |             if ($null -eq $config) { $config = New-Object PSObject }
1393 |         }
1394 |         
1395 |         # Initialize structure for mcp.json format files if they don't exist or are empty
1396 |         if ($usesMcpJsonFormat) {
1397 |             if ($usesVSCodeInsidersFormat) {
1398 |                 # For VS Code Insiders format: {"servers": {...}}
1399 |                 if (!$config.PSObject.Properties["servers"]) {
1400 |                     $config | Add-Member -MemberType NoteProperty -Name "servers" -Value (New-Object PSObject)
1401 |                 }
1402 |             }
1403 |             else {
1404 |                 # For other clients format: {"mcpServers": {...}}
1405 |                 if (!$config.PSObject.Properties["mcpServers"]) {
1406 |                     $config | Add-Member -MemberType NoteProperty -Name "mcpServers" -Value (New-Object PSObject)
1407 |                 }
1408 |             }
1409 |         }
1410 |         
1411 |         # Initialize MCP structure for VS Code settings.json if it doesn't exist
1412 |         if ($Client.IsVSCode -and $Client.ConfigJsonPath.StartsWith("mcp.")) {
1413 |             if (!$config.PSObject.Properties["mcp"]) {
1414 |                 $config | Add-Member -MemberType NoteProperty -Name "mcp" -Value (New-Object PSObject)
1415 |             }
1416 |             if (!$config.mcp.PSObject.Properties["servers"]) {
1417 |                 $config.mcp | Add-Member -MemberType NoteProperty -Name "servers" -Value (New-Object PSObject)
1418 |             }
1419 |         }
1420 | 
1421 |         # Generate server config
1422 |         $serverConfig = if ($UseDocker) { 
1423 |             # Use docker run for all clients (more reliable than docker exec)
1424 |             Get-DockerMcpConfigRun $ServerPath
1425 |         }
1426 |         else { 
1427 |             Get-PythonMcpConfig $PythonPath $ServerPath 
1428 |         }
1429 | 
1430 |         # Navigate and set configuration
1431 |         $pathParts = $Client.ConfigJsonPath.Split('.')
1432 |         $zenKey = $pathParts[-1]
1433 |         $parentPath = $pathParts[0..($pathParts.Length - 2)]
1434 |         
1435 |         $targetObject = $config
1436 |         foreach ($key in $parentPath) {
1437 |             if (!$targetObject.PSObject.Properties[$key]) {
1438 |                 $targetObject | Add-Member -MemberType NoteProperty -Name $key -Value (New-Object PSObject)
1439 |             }
1440 |             $targetObject = $targetObject.$key
1441 |         }
1442 | 
1443 |         $targetObject | Add-Member -MemberType NoteProperty -Name $zenKey -Value $serverConfig -Force
1444 | 
1445 |         # Write config
1446 |         $config | ConvertTo-Json -Depth 10 | Out-File $configPath -Encoding UTF8
1447 |         Write-Success "Successfully configured $($Client.Name)"
1448 |         Write-Host "  Config: $configPath" -ForegroundColor Gray
1449 |         Write-Host "  Restart $($Client.Name) to use the new MCP server" -ForegroundColor Gray
1450 | 
1451 |     }
1452 |     catch {
1453 |         Write-Error "Failed to update $($Client.Name) configuration: $_"
1454 |     }
1455 | }
1456 | 
1457 | # Main MCP client configuration orchestrator
1458 | function Invoke-McpClientConfiguration {
1459 |     param(
1460 |         [Parameter(Mandatory = $true)]
1461 |         [bool]$UseDocker,
1462 |         [string]$PythonPath = "",
1463 |         [string]$ServerPath = ""
1464 |     )
1465 |     
1466 |     Write-Step "Checking Client Integrations"
1467 |     
1468 |     # Configure GUI clients
1469 |     foreach ($client in $script:McpClientDefinitions) {
1470 |         Configure-McpClient -Client $client -UseDocker $UseDocker -PythonPath $PythonPath -ServerPath $ServerPath
1471 |     }
1472 |     
1473 |     # Handle CLI tools separately (they don't follow JSON config pattern)
1474 |     if (!$UseDocker) {
1475 |         Test-ClaudeCliIntegration $PythonPath $ServerPath
1476 |         Test-GeminiCliIntegration (Split-Path $ServerPath -Parent)
1477 |         Test-QwenCliIntegration $PythonPath $ServerPath
1478 |     }
1479 | }
1480 | 
1481 | # Keep existing CLI integration functions
1482 | function Test-ClaudeCliIntegration {
1483 |     param([string]$PythonPath, [string]$ServerPath)
1484 |     
1485 |     if (!(Test-Command "claude")) {
1486 |         return
1487 |     }
1488 |     
1489 |     Write-Info "Claude CLI detected - checking configuration..."
1490 |     
1491 |     try {
1492 |         $claudeConfig = claude mcp list 2>$null
1493 |         if ($claudeConfig -match "zen") {
1494 |             Write-Success "Claude CLI already configured for zen server"
1495 |         }
1496 |         else {
1497 |             Write-Info "To add zen server to Claude CLI, run:"
1498 |             Write-Host "  claude mcp add -s user zen $PythonPath $ServerPath" -ForegroundColor Cyan
1499 |         }
1500 |     }
1501 |     catch {
1502 |         Write-Info "To configure Claude CLI manually, run:"
1503 |         Write-Host "  claude mcp add -s user zen $PythonPath $ServerPath" -ForegroundColor Cyan
1504 |     }
1505 | }
1506 | 
1507 | function Test-GeminiCliIntegration {
1508 |     param([string]$ScriptDir)
1509 |     
1510 |     $zenWrapper = Join-Path $ScriptDir "zen-mcp-server.cmd"
1511 |     
1512 |     # Check if Gemini settings file exists (Windows path)
1513 |     $geminiConfig = "$env:USERPROFILE\.gemini\settings.json"
1514 |     if (!(Test-Path $geminiConfig)) {
1515 |         return
1516 |     }
1517 |     
1518 |     # Check if zen is already configured
1519 |     $configContent = Get-Content $geminiConfig -Raw -ErrorAction SilentlyContinue
1520 |     if ($configContent -and $configContent -match '"zen"') {
1521 |         return
1522 |     }
1523 |     
1524 |     # Ask user if they want to add Zen to Gemini CLI
1525 |     Write-Host ""
1526 |     $response = Read-Host "Configure Zen for Gemini CLI? (y/N)"
1527 |     if ($response -ne 'y' -and $response -ne 'Y') {
1528 |         Write-Info "Skipping Gemini CLI integration"
1529 |         return
1530 |     }
1531 |     
1532 |     # Ensure wrapper script exists
1533 |     if (!(Test-Path $zenWrapper)) {
1534 |         Write-Info "Creating wrapper script for Gemini CLI..."
1535 |         @"
1536 | @echo off
1537 | cd /d "%~dp0"
1538 | if exist ".zen_venv\Scripts\python.exe" (
1539 |     .zen_venv\Scripts\python.exe server.py %*
1540 | ) else (
1541 |     python server.py %*
1542 | )
1543 | "@ | Out-File -FilePath $zenWrapper -Encoding ASCII
1544 |         
1545 |         Write-Success "Created zen-mcp-server.cmd wrapper script"
1546 |     }
1547 |     
1548 |     # Update Gemini settings
1549 |     Write-Info "Updating Gemini CLI configuration..."
1550 |     
1551 |     try {
1552 |         # Create backup with retention management
1553 |         $backupPath = Manage-ConfigBackups $geminiConfig
1554 |         
1555 |         # Read existing config or create new one
1556 |         $config = @{}
1557 |         if (Test-Path $geminiConfig) {
1558 |             $config = Get-Content $geminiConfig -Raw | ConvertFrom-Json
1559 |         }
1560 |         
1561 |         # Ensure mcpServers exists
1562 |         if (!$config.mcpServers) {
1563 |             $config | Add-Member -MemberType NoteProperty -Name "mcpServers" -Value @{} -Force
1564 |         }
1565 |         
1566 |         # Add zen server
1567 |         $zenConfig = @{
1568 |             command = $zenWrapper
1569 |         }
1570 |         
1571 |         $config.mcpServers | Add-Member -MemberType NoteProperty -Name "zen" -Value $zenConfig -Force
1572 |         
1573 |         # Write updated config
1574 |         $config | ConvertTo-Json -Depth 10 | Out-File $geminiConfig -Encoding UTF8
1575 |         
1576 |         Write-Success "Successfully configured Gemini CLI"
1577 |         Write-Host "  Config: $geminiConfig" -ForegroundColor Gray
1578 |         Write-Host "  Restart Gemini CLI to use Zen MCP Server" -ForegroundColor Gray
1579 |         
1580 |     }
1581 |     catch {
1582 |         Write-Error "Failed to update Gemini CLI config: $_"
1583 |         Write-Host ""
1584 |         Write-Host "Manual config location: $geminiConfig"
1585 |         Write-Host "Add this configuration:"
1586 |         Write-Host @"
1587 | {
1588 |   "mcpServers": {
1589 |     "zen": {
1590 |       "command": "$zenWrapper"
1591 |     }
1592 |   }
1593 | }
1594 | "@ -ForegroundColor Yellow
1595 |     }
1596 | }   
1597 | 
1598 | function Show-QwenManualConfig {
1599 |     param(
1600 |         [string]$PythonPath,
1601 |         [string]$ServerPath,
1602 |         [string]$ScriptDir,
1603 |         [string]$ConfigPath,
1604 |         [System.Collections.IDictionary]$EnvironmentMap
1605 |     )
1606 | 
1607 |     Write-Host "Manual config location: $ConfigPath" -ForegroundColor Yellow
1608 |     Write-Host "Add or update this entry:" -ForegroundColor Yellow
1609 | 
1610 |     if ($EnvironmentMap -and $EnvironmentMap.Count -gt 0) {
1611 |         $pairs = $EnvironmentMap.GetEnumerator() | ForEach-Object {
1612 |             $escaped = ($_.Value -replace '\\', '\\\\' -replace '"', '\\"')
1613 |             '        "{0}": "{1}"' -f $_.Key, $escaped
1614 |         }
1615 | 
1616 |         Write-Host "{" -ForegroundColor Yellow
1617 |         Write-Host "  \"mcpServers\": {" -ForegroundColor Yellow
1618 |         Write-Host "    \"zen\": {" -ForegroundColor Yellow
1619 |         Write-Host "      \"command\": \"$PythonPath\"," -ForegroundColor Yellow
1620 |         Write-Host "      \"args\": [\"$ServerPath\"]," -ForegroundColor Yellow
1621 |         Write-Host "      \"cwd\": \"$ScriptDir\"," -ForegroundColor Yellow
1622 |         Write-Host "      \"env\": {" -ForegroundColor Yellow
1623 |         Write-Host ($pairs -join "`n") -ForegroundColor Yellow
1624 |         Write-Host "      }" -ForegroundColor Yellow
1625 |         Write-Host "    }" -ForegroundColor Yellow
1626 |         Write-Host "  }" -ForegroundColor Yellow
1627 |         Write-Host "}" -ForegroundColor Yellow
1628 |     }
1629 |     else {
1630 |         Write-Host "{" -ForegroundColor Yellow
1631 |         Write-Host "  \"mcpServers\": {" -ForegroundColor Yellow
1632 |         Write-Host "    \"zen\": {" -ForegroundColor Yellow
1633 |         Write-Host "      \"command\": \"$PythonPath\"," -ForegroundColor Yellow
1634 |         Write-Host "      \"args\": [\"$ServerPath\"]," -ForegroundColor Yellow
1635 |         Write-Host "      \"cwd\": \"$ScriptDir\"" -ForegroundColor Yellow
1636 |         Write-Host "    }" -ForegroundColor Yellow
1637 |         Write-Host "  }" -ForegroundColor Yellow
1638 |         Write-Host "}" -ForegroundColor Yellow
1639 |     }
1640 | }
1641 | 
1642 | function Test-QwenCliIntegration {
1643 |     param([string]$PythonPath, [string]$ServerPath)
1644 | 
1645 |     if (!(Test-Command "qwen")) {
1646 |         return
1647 |     }
1648 | 
1649 |     Write-Info "Qwen CLI detected - checking configuration..."
1650 | 
1651 |     $configPath = Join-Path $env:USERPROFILE ".qwen\settings.json"
1652 |     $configDir = Split-Path $configPath -Parent
1653 |     $scriptDir = Split-Path $ServerPath -Parent
1654 | 
1655 |     $configStatus = "missing"
1656 |     $config = @{}
1657 | 
1658 |     if (Test-Path $configPath) {
1659 |         try {
1660 |             Add-Type -AssemblyName System.Web.Extensions -ErrorAction SilentlyContinue
1661 |             $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
1662 |             $serializer.MaxJsonLength = 67108864
1663 |             $rawJson = Get-Content $configPath -Raw
1664 |             $config = $serializer.DeserializeObject($rawJson)
1665 |             if (-not ($config -is [System.Collections.IDictionary])) {
1666 |                 $config = @{}
1667 |             }
1668 | 
1669 |             if ($config.ContainsKey('mcpServers') -and $config['mcpServers'] -is [System.Collections.IDictionary]) {
1670 |                 $servers = $config['mcpServers']
1671 |                 if ($servers.Contains('zen') -and $servers['zen'] -is [System.Collections.IDictionary]) {
1672 |                     $zenConfig = $servers['zen']
1673 |                     $commandMatches = ($zenConfig['command'] -eq $PythonPath)
1674 | 
1675 |                     $argsValue = $zenConfig['args']
1676 |                     $argsList = @()
1677 |                     if ($argsValue -is [System.Collections.IEnumerable] -and $argsValue -isnot [string]) {
1678 |                         $argsList = @($argsValue)
1679 |                     }
1680 |                     elseif ($null -ne $argsValue) {
1681 |                         $argsList = @($argsValue)
1682 |                     }
1683 |                     $argsMatches = ($argsList.Count -eq 1 -and $argsList[0] -eq $ServerPath)
1684 | 
1685 |                     $cwdValue = $null
1686 |                     if ($zenConfig.Contains('cwd')) {
1687 |                         $cwdValue = $zenConfig['cwd']
1688 |                     }
1689 |                     $cwdMatches = ([string]::IsNullOrEmpty($cwdValue) -or $cwdValue -eq $scriptDir)
1690 | 
1691 |                     if ($commandMatches -and $argsMatches -and $cwdMatches) {
1692 |                         Write-Success "Qwen CLI already configured for zen server"
1693 |                         return
1694 |                     }
1695 | 
1696 |                     $configStatus = "mismatch"
1697 |                     Write-Warning "Existing Qwen CLI configuration differs from the current setup."
1698 |                 }
1699 |             }
1700 |         }
1701 |         catch {
1702 |             $configStatus = "invalid"
1703 |             Write-Warning "Unable to parse Qwen CLI settings at $configPath ($_)."
1704 |             $config = @{}
1705 |         }
1706 |     }
1707 | 
1708 |     $envMap = [ordered]@{}
1709 |     if (Test-Path ".env") {
1710 |         foreach ($line in Get-Content ".env") {
1711 |             $trimmed = $line.Trim()
1712 |             if ([string]::IsNullOrWhiteSpace($trimmed) -or $trimmed.StartsWith('#')) {
1713 |                 continue
1714 |             }
1715 | 
1716 |             if ($line -match '^\s*([^=]+)=(.*)$') {
1717 |                 $key = $matches[1].Trim()
1718 |                 $value = $matches[2]
1719 |                 $value = ($value -replace '\s+#.*$', '').Trim()
1720 |                 if ($value.StartsWith('"') -and $value.EndsWith('"')) {
1721 |                     $value = $value.Substring(1, $value.Length - 2)
1722 |                 }
1723 |                 if ([string]::IsNullOrWhiteSpace($value)) {
1724 |                     $value = [Environment]::GetEnvironmentVariable($key, "Process")
1725 |                 }
1726 |                 if (![string]::IsNullOrWhiteSpace($value) -and $value -notmatch '^your_.*_here$') {
1727 |                     $envMap[$key] = $value
1728 |                 }
1729 |             }
1730 |         }
1731 |     }
1732 | 
1733 |     $extraKeys = @(
1734 |         "GEMINI_API_KEY", "OPENAI_API_KEY", "XAI_API_KEY", "DIAL_API_KEY", "OPENROUTER_API_KEY",
1735 |         "AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_API_VERSION", "AZURE_OPENAI_ALLOWED_MODELS", "AZURE_MODELS_CONFIG_PATH",
1736 |         "CUSTOM_API_URL", "CUSTOM_API_KEY", "CUSTOM_MODEL_NAME", "DEFAULT_MODEL", "GOOGLE_ALLOWED_MODELS",
1737 |         "OPENAI_ALLOWED_MODELS", "OPENROUTER_ALLOWED_MODELS", "XAI_ALLOWED_MODELS", "DEFAULT_THINKING_MODE_THINKDEEP",
1738 |         "DISABLED_TOOLS", "CONVERSATION_TIMEOUT_HOURS", "MAX_CONVERSATION_TURNS", "LOG_LEVEL", "ZEN_MCP_FORCE_ENV_OVERRIDE"
1739 |     )
1740 | 
1741 |     foreach ($key in $extraKeys) {
1742 |         if (-not $envMap.Contains($key)) {
1743 |             $value = [Environment]::GetEnvironmentVariable($key, "Process")
1744 |             if (![string]::IsNullOrWhiteSpace($value) -and $value -notmatch '^your_.*_here$') {
1745 |                 $envMap[$key] = $value
1746 |             }
1747 |         }
1748 |     }
1749 | 
1750 |     $prompt = "Configure Zen for Qwen CLI? (y/N)"
1751 |     if ($configStatus -eq "mismatch" -or $configStatus -eq "invalid") {
1752 |         $prompt = "Update Qwen CLI zen configuration? (y/N)"
1753 |     }
1754 | 
1755 |     $response = Read-Host $prompt
1756 |     if ($response -ne 'y' -and $response -ne 'Y') {
1757 |         Write-Info "Skipping Qwen CLI integration"
1758 |         Show-QwenManualConfig $PythonPath $ServerPath $scriptDir $configPath $envMap
1759 |         return
1760 |     }
1761 | 
1762 |     if (!(Test-Path $configDir)) {
1763 |         New-Item -ItemType Directory -Path $configDir -Force | Out-Null
1764 |     }
1765 | 
1766 |     if (Test-Path $configPath -and $configStatus -ne "missing") {
1767 |         Manage-ConfigBackups $configPath | Out-Null
1768 |     }
1769 | 
1770 |     try {
1771 |         if (-not ($config -is [System.Collections.IDictionary])) {
1772 |             $config = @{}
1773 |         }
1774 | 
1775 |         if (-not $config.ContainsKey('mcpServers') -or $config['mcpServers'] -isnot [System.Collections.IDictionary]) {
1776 |             $config['mcpServers'] = @{}
1777 |         }
1778 | 
1779 |         $zenConfig = [ordered]@{
1780 |             command = $PythonPath
1781 |             args    = @($ServerPath)
1782 |             cwd     = $scriptDir
1783 |         }
1784 | 
1785 |         if ($envMap.Count -gt 0) {
1786 |             $zenConfig['env'] = $envMap
1787 |         }
1788 | 
1789 |         $config['mcpServers']['zen'] = $zenConfig
1790 | 
1791 |         $json = ($config | ConvertTo-Json -Depth 20)
1792 |         Set-Content -Path $configPath -Value $json -Encoding UTF8
1793 | 
1794 |         Write-Success "Successfully configured Qwen CLI"
1795 |         Write-Host "  Config: $configPath" -ForegroundColor Gray
1796 |         Write-Host "  Restart Qwen CLI to use Zen MCP Server" -ForegroundColor Gray
1797 |     }
1798 |     catch {
1799 |         Write-Error "Failed to update Qwen CLI configuration: $_"
1800 |         Show-QwenManualConfig $PythonPath $ServerPath $scriptDir $configPath $envMap
1801 |     }
1802 | }
1803 | 
1804 | 
1805 | # ----------------------------------------------------------------------------
1806 | # End MCP Client Configuration System
1807 | # ----------------------------------------------------------------------------
1808 | 
1809 | # ----------------------------------------------------------------------------
1810 | # User Interface Functions
1811 | # ----------------------------------------------------------------------------
1812 | 
1813 | # Show script help
1814 | function Show-Help {
1815 |     Write-Host @"
1816 | Zen MCP Server - Setup and Launch Script
1817 | 
1818 | USAGE:
1819 | .\run-server.ps1 [OPTIONS]
1820 | 
1821 | OPTIONS:
1822 | -Help                   Show this help message
1823 | -Version                Show version information
1824 | -Follow                 Follow server logs in real time
1825 | -Config                 Show configuration instructions for MCP clients
1826 | -ClearCache             Clear Python cache files and exit
1827 | -Force                  Force recreation of Python virtual environment
1828 | -Dev                    Install development dependencies from requirements-dev.txt
1829 | -Docker                 Use Docker instead of Python virtual environment
1830 | -SkipVenv              Skip Python virtual environment creation
1831 | -SkipDocker            Skip Docker checks and cleanup
1832 | 
1833 | EXAMPLES:
1834 | .\run-server.ps1                      # Normal startup
1835 | .\run-server.ps1 -Follow              # Start and follow logs
1836 | .\run-server.ps1 -Config              # Show configuration help
1837 | .\run-server.ps1 -Dev                 # Include development dependencies
1838 | .\run-server.ps1 -Docker              # Use Docker deployment
1839 | .\run-server.ps1 -Docker -Follow      # Docker with log following
1840 | 
1841 | For more information, visit: https://github.com/BeehiveInnovations/zen-mcp-server
1842 | "@ -ForegroundColor White
1843 | }
1844 | 
1845 | # Show version information
1846 | function Show-Version {
1847 |     $version = Get-Version
1848 |     Write-Host "Zen MCP Server version: $version" -ForegroundColor Green
1849 |     Write-Host "PowerShell Setup Script for Windows" -ForegroundColor Cyan
1850 |     Write-Host "Author: GiGiDKR (https://github.com/GiGiDKR)" -ForegroundColor Gray
1851 |     Write-Host "Project: BeehiveInnovations/zen-mcp-server" -ForegroundColor Gray
1852 | }
1853 | 
1854 | # Show configuration instructions
1855 | function Show-ConfigInstructions {
1856 |     param(
1857 |         [string]$PythonPath = "",
1858 |         [string]$ServerPath = "",
1859 |         [switch]$UseDocker = $false
1860 |     )
1861 |     
1862 |     Write-Step "Configuration Instructions"
1863 |     
1864 |     if ($UseDocker) {
1865 |         Write-Host "Docker Configuration:" -ForegroundColor Yellow
1866 |         Write-Host "The MCP clients have been configured to use Docker containers." -ForegroundColor White
1867 |         Write-Host "Make sure the Docker container is running with: docker-compose up -d" -ForegroundColor Cyan
1868 |         Write-Host ""
1869 |     }
1870 |     else {
1871 |         Write-Host "Python Virtual Environment Configuration:" -ForegroundColor Yellow
1872 |         Write-Host "Python Path: $PythonPath" -ForegroundColor Cyan
1873 |         Write-Host "Server Path: $ServerPath" -ForegroundColor Cyan
1874 |         Write-Host ""
1875 |     }
1876 |     
1877 |     Write-Host "Supported MCP Clients:" -ForegroundColor Green
1878 |     Write-Host "✓ Claude Desktop" -ForegroundColor White
1879 |     Write-Host "✓ Claude CLI" -ForegroundColor White  
1880 |     Write-Host "✓ VSCode (with MCP extension)" -ForegroundColor White
1881 |     Write-Host "✓ VSCode Insiders" -ForegroundColor White
1882 |     Write-Host "✓ Cursor" -ForegroundColor White
1883 |     Write-Host "✓ Windsurf" -ForegroundColor White
1884 |     Write-Host "✓ Trae" -ForegroundColor White
1885 |     Write-Host "✓ Gemini CLI" -ForegroundColor White
1886 |     Write-Host "✓ Qwen CLI" -ForegroundColor White
1887 |     Write-Host ""
1888 |     Write-Host "The script automatically detects and configures compatible clients." -ForegroundColor Gray
1889 |     Write-Host "Restart your MCP clients after configuration to use the Zen MCP Server." -ForegroundColor Yellow
1890 | }
1891 | 
1892 | # Show setup instructions
1893 | function Show-SetupInstructions {
1894 |     param(
1895 |         [string]$PythonPath = "",
1896 |         [string]$ServerPath = "",
1897 |         [switch]$UseDocker = $false
1898 |     )
1899 |     
1900 |     Write-Step "Setup Complete"
1901 |     
1902 |     if ($UseDocker) {
1903 |         Write-Success "Zen MCP Server is configured for Docker deployment"
1904 |         Write-Host "Docker command: docker exec -i zen-mcp-server python server.py" -ForegroundColor Cyan
1905 |     }
1906 |     else {
1907 |         Write-Success "Zen MCP Server is configured for Python virtual environment"
1908 |         Write-Host "Python: $PythonPath" -ForegroundColor Cyan
1909 |         Write-Host "Server: $ServerPath" -ForegroundColor Cyan
1910 |     }
1911 |     
1912 |     Write-Host ""
1913 |     Write-Host "MCP clients will automatically connect to the server." -ForegroundColor Green
1914 |     Write-Host "For manual configuration, use the paths shown above." -ForegroundColor Gray
1915 | }
1916 | 
1917 | # Start the server
1918 | function Start-Server {
1919 |     Write-Step "Starting Zen MCP Server"
1920 |     
1921 |     $pythonPath = "$VENV_PATH\Scripts\python.exe"
1922 |     if (!(Test-Path $pythonPath)) {
1923 |         Write-Error "Python virtual environment not found. Please run setup first."
1924 |         return
1925 |     }
1926 |     
1927 |     $serverPath = "server.py"
1928 |     if (!(Test-Path $serverPath)) {
1929 |         Write-Error "Server script not found: $serverPath"
1930 |         return
1931 |     }
1932 |     
1933 |     try {
1934 |         Write-Info "Launching server..."
1935 |         & $pythonPath $serverPath
1936 |     }
1937 |     catch {
1938 |         Write-Error "Failed to start server: $_"
1939 |     }
1940 | }
1941 | 
1942 | # Follow server logs
1943 | function Follow-Logs {
1944 |     Write-Step "Following Server Logs"
1945 |     
1946 |     $logPath = Join-Path $LOG_DIR $LOG_FILE
1947 |     
1948 |     if (!(Test-Path $logPath)) {
1949 |         Write-Warning "Log file not found: $logPath"
1950 |         Write-Info "Starting server to generate logs..."
1951 |         Start-Server
1952 |         return
1953 |     }
1954 |     
1955 |     try {
1956 |         Write-Info "Following logs at: $logPath"
1957 |         Write-Host "Press Ctrl+C to stop following logs"
1958 |         Write-Host ""
1959 |         Get-Content $logPath -Wait
1960 |     }
1961 |     catch {
1962 |         Write-Error "Failed to follow logs: $_"
1963 |     }
1964 | }
1965 | 
1966 | # ----------------------------------------------------------------------------
1967 | # Environment File Management
1968 | # ----------------------------------------------------------------------------
1969 | 
1970 | # Initialize .env file if it doesn't exist
1971 | function Initialize-EnvFile {
1972 |     Write-Step "Setting up Environment File"
1973 |     
1974 |     if (!(Test-Path ".env")) {
1975 |         Write-Info "Creating default .env file..."
1976 |         @"
1977 | # API Keys - Replace with your actual keys
1978 | GEMINI_API_KEY=your_gemini_api_key_here
1979 | GOOGLE_API_KEY=your_google_api_key_here
1980 | OPENAI_API_KEY=your_openai_api_key_here
1981 | ANTHROPIC_API_KEY=your_anthropic_api_key_here
1982 | XAI_API_KEY=your_xai_api_key_here
1983 | DIAL_API_KEY=your_dial_api_key_here
1984 | DIAL_API_HOST=your_dial_api_host_here
1985 | DIAL_API_VERSION=your_dial_api_version_here
1986 | OPENROUTER_API_KEY=your_openrouter_api_key_here
1987 | CUSTOM_API_URL=your_custom_api_url_here
1988 | CUSTOM_API_KEY=your_custom_api_key_here
1989 | CUSTOM_MODEL_NAME=your_custom_model_name_here
1990 | 
1991 | # Server Configuration
1992 | DEFAULT_MODEL=auto
1993 | LOG_LEVEL=INFO
1994 | LOG_MAX_SIZE=10MB
1995 | LOG_BACKUP_COUNT=5
1996 | DEFAULT_THINKING_MODE_THINKDEEP=high
1997 | 
1998 | # Optional Advanced Settings
1999 | #DISABLED_TOOLS=
2000 | #MAX_MCP_OUTPUT_TOKENS=
2001 | #TZ=UTC
2002 | "@ | Out-File -FilePath ".env" -Encoding UTF8
2003 |         
2004 |         Write-Success "Default .env file created"
2005 |         Write-Warning "Please edit .env file with your actual API keys"
2006 |     }
2007 |     else {
2008 |         Write-Success ".env file already exists"
2009 |     }
2010 | }
2011 | 
2012 | # Import environment variables from .env file
2013 | function Import-EnvFile {
2014 |     if (!(Test-Path ".env")) {
2015 |         Write-Warning "No .env file found"
2016 |         return
2017 |     }
2018 |     
2019 |     try {
2020 |         $envContent = Get-Content ".env" -ErrorAction Stop
2021 |         foreach ($line in $envContent) {
2022 |             if ($line -match '^([^#][^=]*?)=(.*)$') {
2023 |                 $key = $matches[1].Trim()
2024 |                 $value = $matches[2].Trim() -replace '^["'']|["'']$', ''
2025 |                 
2026 |                 # Set environment variable for the current session
2027 |                 [Environment]::SetEnvironmentVariable($key, $value, "Process")
2028 |             }
2029 |         }
2030 |         Write-Success "Environment variables loaded from .env file"
2031 |     }
2032 |     catch {
2033 |         Write-Warning "Could not load .env file: $_"
2034 |     }
2035 | }
2036 | 
2037 | # ----------------------------------------------------------------------------
2038 | # Workflow Functions
2039 | # ----------------------------------------------------------------------------
2040 | 
2041 | # Docker deployment workflow
2042 | function Invoke-DockerWorkflow {
2043 |     Write-Step "Starting Docker Workflow"
2044 |     Write-Host "Zen MCP Server" -ForegroundColor Green
2045 |     Write-Host "=================" -ForegroundColor Cyan
2046 |     
2047 |     $version = Get-Version
2048 |     Write-Host "Version: $version"
2049 |     Write-Host "Mode: Docker Container" -ForegroundColor Yellow
2050 |     Write-Host ""
2051 |     
2052 |     # Docker setup and validation
2053 |     if (!(Test-DockerRequirements)) { exit 1 }
2054 |     if (!(Initialize-DockerEnvironment)) { exit 1 }
2055 |     
2056 |     Import-EnvFile
2057 |     Test-ApiKeys
2058 |     
2059 |     if (!(Build-DockerImage -Force:$Force)) { exit 1 }
2060 |     
2061 |     # Configure MCP clients for Docker
2062 |     Invoke-McpClientConfiguration -UseDocker $true
2063 |     
2064 |     Show-SetupInstructions -UseDocker
2065 |     
2066 |     # Start Docker services
2067 |     Write-Step "Starting Zen MCP Server"
2068 |     if ($Follow) {
2069 |         Write-Info "Starting server and following logs..."
2070 |         Start-DockerServices -Follow
2071 |         exit 0
2072 |     }
2073 |     
2074 |     if (!(Start-DockerServices)) { exit 1 }
2075 |     
2076 |     Write-Host ""
2077 |     Write-Success "Zen MCP Server is running in Docker!"
2078 |     Write-Host ""
2079 |     
2080 |     Write-Host "Next steps:" -ForegroundColor Cyan
2081 |     Write-Host "1. Restart your MCP clients (Claude Desktop, etc.)" -ForegroundColor White
2082 |     Write-Host "2. The server is now ready to use" -ForegroundColor White
2083 |     Write-Host ""
2084 |     Write-Host "Useful commands:" -ForegroundColor Cyan
2085 |     Write-Host "  View logs: " -NoNewline -ForegroundColor White
2086 |     Write-Host "docker logs -f zen-mcp-server" -ForegroundColor Yellow
2087 |     Write-Host "  Stop server: " -NoNewline -ForegroundColor White
2088 |     Write-Host "docker-compose down" -ForegroundColor Yellow
2089 |     Write-Host "  Restart server: " -NoNewline -ForegroundColor White
2090 |     Write-Host "docker-compose restart" -ForegroundColor Yellow
2091 | }
2092 | 
2093 | # Python virtual environment deployment workflow
2094 | function Invoke-PythonWorkflow {
2095 |     Write-Step "Starting Python Virtual Environment Workflow"
2096 |     Write-Host "Zen MCP Server" -ForegroundColor Green
2097 |     Write-Host "=================" -ForegroundColor Cyan
2098 |     
2099 |     $version = Get-Version
2100 |     Write-Host "Version: $version"
2101 |     Write-Host ""
2102 |     
2103 |     if (!(Test-Path $VENV_PATH)) {
2104 |         Write-Info "Setting up Python environment for first time..."
2105 |     }
2106 |     
2107 |     # Python environment setup
2108 |     Cleanup-Docker
2109 |     Clear-PythonCache
2110 |     Initialize-EnvFile
2111 |     Import-EnvFile
2112 |     Test-ApiKeys
2113 |     
2114 |     try {
2115 |         $pythonPath = Initialize-Environment
2116 |     }
2117 |     catch {
2118 |         Write-Error "Failed to setup Python environment: $_"
2119 |         exit 1
2120 |     }
2121 |     
2122 |     try {
2123 |         Install-Dependencies $pythonPath -InstallDevDependencies:$Dev
2124 |     }
2125 |     catch {
2126 |         Write-Error "Failed to install dependencies: $_"
2127 |         exit 1
2128 |     }
2129 |     
2130 |     $serverPath = Get-AbsolutePath "server.py"
2131 |     
2132 |     # Configure MCP clients for Python
2133 |     Invoke-McpClientConfiguration -UseDocker $false -PythonPath $pythonPath -ServerPath $serverPath
2134 |     
2135 |     Show-SetupInstructions $pythonPath $serverPath
2136 |     Initialize-Logging
2137 |     
2138 |     Write-Host ""
2139 |     Write-Host "Logs will be written to: $(Get-AbsolutePath $LOG_DIR)\$LOG_FILE"
2140 |     Write-Host ""
2141 |     
2142 |     if ($Follow) {
2143 |         Follow-Logs
2144 |     }
2145 |     else {
2146 |         Write-Host "To follow logs: .\run-server.ps1 -Follow" -ForegroundColor Yellow
2147 |         Write-Host "To show config: .\run-server.ps1 -Config" -ForegroundColor Yellow
2148 |         Write-Host "To update: git pull, then run .\run-server.ps1 again" -ForegroundColor Yellow
2149 |         Write-Host ""
2150 |         Write-Host "Happy coding! 🎉" -ForegroundColor Green
2151 |         
2152 |         $response = Read-Host "`nStart the server now? (y/N)"
2153 |         if ($response -eq 'y' -or $response -eq 'Y') {
2154 |             Start-Server
2155 |         }
2156 |     }
2157 | }
2158 | 
2159 | # ----------------------------------------------------------------------------
2160 | # End Workflow Functions
2161 | # ----------------------------------------------------------------------------
2162 | 
2163 | # ----------------------------------------------------------------------------
2164 | # Main Execution
2165 | # ----------------------------------------------------------------------------
2166 | 
2167 | # Main execution function
2168 | function Start-MainProcess {
2169 |     # Parse command line arguments
2170 |     if ($Help) {
2171 |         Show-Help
2172 |         exit 0
2173 |     }
2174 |     
2175 |     if ($Version) {
2176 |         Show-Version  
2177 |         exit 0
2178 |     }
2179 |     
2180 |     if ($ClearCache) {
2181 |         Clear-PythonCache
2182 |         Write-Success "Cache cleared successfully"
2183 |         Write-Host ""
2184 |         Write-Host "You can now run '.\run-server.ps1' normally"
2185 |         exit 0
2186 |     }
2187 |     
2188 |     if ($Config) {
2189 |         # Setup minimal environment to get paths for config display
2190 |         Write-Info "Setting up environment for configuration display..."
2191 |         Write-Host ""
2192 |         try {
2193 |             if ($Docker) {
2194 |                 # Docker configuration mode
2195 |                 if (!(Test-DockerRequirements)) {
2196 |                     exit 1
2197 |                 }
2198 |                 Initialize-DockerEnvironment
2199 |                 Show-ConfigInstructions "" "" -UseDocker
2200 |             }
2201 |             else {
2202 |                 # Python virtual environment configuration mode
2203 |                 $pythonPath = Initialize-Environment
2204 |                 $serverPath = Get-AbsolutePath "server.py"
2205 |                 Show-ConfigInstructions $pythonPath $serverPath
2206 |             }
2207 |         }
2208 |         catch {
2209 |             Write-Error "Failed to setup environment for configuration: $_"
2210 |             exit 1
2211 |         }
2212 |         exit 0
2213 |     }
2214 | 
2215 |     # ============================================================================
2216 |     # Docker Workflow
2217 |     # ============================================================================
2218 |     if ($Docker) {
2219 |         Invoke-DockerWorkflow
2220 |         exit 0
2221 |     }
2222 | 
2223 |     # ============================================================================
2224 |     # Python Virtual Environment Workflow (Default)
2225 |     # ============================================================================
2226 |     Invoke-PythonWorkflow
2227 |     exit 0
2228 | }
2229 | 
2230 | # ============================================================================
2231 | # Main Script Execution
2232 | # ============================================================================
2233 | 
2234 | # Execute main process
2235 | Start-MainProcess
2236 | 
```