feat: support for Qwen Code
This commit is contained in:
30
README.md
30
README.md
@@ -3,7 +3,7 @@
|
|||||||
[zen_web.webm](https://github.com/user-attachments/assets/851e3911-7f06-47c0-a4ab-a2601236697c)
|
[zen_web.webm](https://github.com/user-attachments/assets/851e3911-7f06-47c0-a4ab-a2601236697c)
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<b>🤖 <a href="https://www.anthropic.com/claude-code">Claude Code</a> OR <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a> OR <a href="https://github.com/openai/codex">Codex CLI</a> + [Gemini / OpenAI / Azure / Grok / OpenRouter / DIAL / Ollama / Anthropic / Any Model] = Your Ultimate AI Development Team</b>
|
<b>🤖 <a href="https://www.anthropic.com/claude-code">Claude Code</a> OR <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a> OR <a href="https://github.com/openai/codex">Codex CLI</a> OR <a href="https://qwenlm.github.io/qwen-code-docs/">Qwen Code CLI</a> + [Gemini / OpenAI / Azure / Grok / OpenRouter / DIAL / Ollama / Anthropic / Any Model] = Your Ultimate AI Development Team</b>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
@@ -119,7 +119,7 @@ git clone https://github.com/BeehiveInnovations/zen-mcp-server.git
|
|||||||
cd zen-mcp-server
|
cd zen-mcp-server
|
||||||
|
|
||||||
# Handles everything: setup, config, API keys from system environment.
|
# Handles everything: setup, config, API keys from system environment.
|
||||||
# Auto-configures Claude Desktop, Claude Code, Gemini CLI, Codex CLI
|
# Auto-configures Claude Desktop, Claude Code, Gemini CLI, Codex CLI, Qwen CLI
|
||||||
# Enable / disable additional settings in .env
|
# Enable / disable additional settings in .env
|
||||||
./run-server.sh
|
./run-server.sh
|
||||||
```
|
```
|
||||||
@@ -144,6 +144,30 @@ cd zen-mcp-server
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**For Qwen Code CLI:**
|
||||||
|
Edit `~/.qwen/settings.json` and register Zen as an MCP server:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zen": {
|
||||||
|
"command": "bash",
|
||||||
|
"args": [
|
||||||
|
"-c",
|
||||||
|
"for p in $(which uvx 2>/dev/null) $HOME/.local/bin/uvx /opt/homebrew/bin/uvx /usr/local/bin/uvx uvx; do [ -x \"$p\" ] && exec \"$p\" --from git+https://github.com/BeehiveInnovations/zen-mcp-server.git zen-mcp-server; done; echo 'uvx not found' >&2; exit 1"
|
||||||
|
],
|
||||||
|
"cwd": "/path/to/zen-mcp-server",
|
||||||
|
"env": {
|
||||||
|
"PATH": "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:~/.local/bin",
|
||||||
|
"GEMINI_API_KEY": "your_api_key_here"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Update the `env` block with the API keys you use (Gemini, OpenAI, OpenRouter, etc.).
|
||||||
|
|
||||||
**3. Start Using!**
|
**3. Start Using!**
|
||||||
```
|
```
|
||||||
"Use zen to analyze this code for security issues with gemini pro"
|
"Use zen to analyze this code for security issues with gemini pro"
|
||||||
@@ -151,7 +175,7 @@ cd zen-mcp-server
|
|||||||
"Plan the migration strategy with zen, get consensus from multiple models"
|
"Plan the migration strategy with zen, get consensus from multiple models"
|
||||||
```
|
```
|
||||||
|
|
||||||
👉 **[Complete Setup Guide](docs/getting-started.md)** with detailed installation, configuration for Gemini / Codex, and troubleshooting
|
👉 **[Complete Setup Guide](docs/getting-started.md)** with detailed installation, configuration for Gemini / Codex / Qwen, and troubleshooting
|
||||||
👉 **[Cursor & VS Code Setup](docs/getting-started.md#ide-clients)** for IDE integration instructions
|
👉 **[Cursor & VS Code Setup](docs/getting-started.md#ide-clients)** for IDE integration instructions
|
||||||
|
|
||||||
## Provider Configuration
|
## Provider Configuration
|
||||||
|
|||||||
@@ -147,6 +147,31 @@ PATH = "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:$HOME/.local/bin:$HOME/.c
|
|||||||
GEMINI_API_KEY = "your_api_key_here"
|
GEMINI_API_KEY = "your_api_key_here"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
**For Qwen Code CLI:**
|
||||||
|
Create or edit `~/.qwen/settings.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zen": {
|
||||||
|
"command": "bash",
|
||||||
|
"args": [
|
||||||
|
"-c",
|
||||||
|
"for p in $(which uvx 2>/dev/null) $HOME/.local/bin/uvx /opt/homebrew/bin/uvx /usr/local/bin/uvx uvx; do [ -x \"$p\" ] && exec \"$p\" --from git+https://github.com/BeehiveInnovations/zen-mcp-server.git zen-mcp-server; done; echo 'uvx not found' >&2; exit 1"
|
||||||
|
],
|
||||||
|
"cwd": "/path/to/zen-mcp-server",
|
||||||
|
"env": {
|
||||||
|
"PATH": "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:~/.local/bin",
|
||||||
|
"GEMINI_API_KEY": "your_api_key_here"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace the placeholder API key with the providers you use (Gemini, OpenAI, OpenRouter, etc.).
|
||||||
|
|
||||||
#### IDE Clients (Cursor & VS Code)
|
#### IDE Clients (Cursor & VS Code)
|
||||||
|
|
||||||
Zen works in GUI IDEs that speak MCP. The configuration mirrors the CLI examples above—point the client at the `uvx` launcher and set any required environment variables.
|
Zen works in GUI IDEs that speak MCP. The configuration mirrors the CLI examples above—point the client at the `uvx` launcher and set any required environment variables.
|
||||||
@@ -263,6 +288,11 @@ CUSTOM_MODEL_NAME=llama3.2 # Default model name
|
|||||||
### For Gemini CLI:
|
### For Gemini CLI:
|
||||||
**Note**: While Zen MCP connects to Gemini CLI, tool invocation isn't working correctly yet. See [Gemini CLI Setup](gemini-setup.md) for updates.
|
**Note**: While Zen MCP connects to Gemini CLI, tool invocation isn't working correctly yet. See [Gemini CLI Setup](gemini-setup.md) for updates.
|
||||||
|
|
||||||
|
### For Qwen Code CLI:
|
||||||
|
1. Restart the Qwen Code CLI if it's running (`qwen exit`).
|
||||||
|
2. Run `qwen mcp list --scope user` and confirm `zen` shows `CONNECTED`.
|
||||||
|
3. Try: `"/mcp"` to inspect available tools or `"Use zen to analyze this repo"`.
|
||||||
|
|
||||||
### For Codex CLI:
|
### For Codex CLI:
|
||||||
1. Restart Codex CLI if running
|
1. Restart Codex CLI if running
|
||||||
2. Open a new conversation
|
2. Open a new conversation
|
||||||
|
|||||||
204
run-server.ps1
204
run-server.ps1
@@ -1409,6 +1409,7 @@ function Invoke-McpClientConfiguration {
|
|||||||
if (!$UseDocker) {
|
if (!$UseDocker) {
|
||||||
Test-ClaudeCliIntegration $PythonPath $ServerPath
|
Test-ClaudeCliIntegration $PythonPath $ServerPath
|
||||||
Test-GeminiCliIntegration (Split-Path $ServerPath -Parent)
|
Test-GeminiCliIntegration (Split-Path $ServerPath -Parent)
|
||||||
|
Test-QwenCliIntegration $PythonPath $ServerPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1522,6 +1523,208 @@ if exist ".zen_venv\Scripts\python.exe" (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Show-QwenManualConfig {
|
||||||
|
param(
|
||||||
|
[string]$PythonPath,
|
||||||
|
[string]$ServerPath,
|
||||||
|
[string]$ScriptDir,
|
||||||
|
[string]$ConfigPath,
|
||||||
|
[System.Collections.IDictionary]$EnvironmentMap
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Host "Manual config location: $ConfigPath" -ForegroundColor Yellow
|
||||||
|
Write-Host "Add or update this entry:" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
if ($EnvironmentMap -and $EnvironmentMap.Count -gt 0) {
|
||||||
|
$pairs = $EnvironmentMap.GetEnumerator() | ForEach-Object {
|
||||||
|
$escaped = ($_.Value -replace '\\', '\\\\' -replace '"', '\\"')
|
||||||
|
' "{0}": "{1}"' -f $_.Key, $escaped
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "{" -ForegroundColor Yellow
|
||||||
|
Write-Host " \"mcpServers\": {" -ForegroundColor Yellow
|
||||||
|
Write-Host " \"zen\": {" -ForegroundColor Yellow
|
||||||
|
Write-Host " \"command\": \"$PythonPath\"," -ForegroundColor Yellow
|
||||||
|
Write-Host " \"args\": [\"$ServerPath\"]," -ForegroundColor Yellow
|
||||||
|
Write-Host " \"cwd\": \"$ScriptDir\"," -ForegroundColor Yellow
|
||||||
|
Write-Host " \"env\": {" -ForegroundColor Yellow
|
||||||
|
Write-Host ($pairs -join "`n") -ForegroundColor Yellow
|
||||||
|
Write-Host " }" -ForegroundColor Yellow
|
||||||
|
Write-Host " }" -ForegroundColor Yellow
|
||||||
|
Write-Host " }" -ForegroundColor Yellow
|
||||||
|
Write-Host "}" -ForegroundColor Yellow
|
||||||
|
} else {
|
||||||
|
Write-Host "{" -ForegroundColor Yellow
|
||||||
|
Write-Host " \"mcpServers\": {" -ForegroundColor Yellow
|
||||||
|
Write-Host " \"zen\": {" -ForegroundColor Yellow
|
||||||
|
Write-Host " \"command\": \"$PythonPath\"," -ForegroundColor Yellow
|
||||||
|
Write-Host " \"args\": [\"$ServerPath\"]," -ForegroundColor Yellow
|
||||||
|
Write-Host " \"cwd\": \"$ScriptDir\"" -ForegroundColor Yellow
|
||||||
|
Write-Host " }" -ForegroundColor Yellow
|
||||||
|
Write-Host " }" -ForegroundColor Yellow
|
||||||
|
Write-Host "}" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-QwenCliIntegration {
|
||||||
|
param([string]$PythonPath, [string]$ServerPath)
|
||||||
|
|
||||||
|
if (!(Test-Command "qwen")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info "Qwen CLI detected - checking configuration..."
|
||||||
|
|
||||||
|
$configPath = Join-Path $env:USERPROFILE ".qwen\settings.json"
|
||||||
|
$configDir = Split-Path $configPath -Parent
|
||||||
|
$scriptDir = Split-Path $ServerPath -Parent
|
||||||
|
|
||||||
|
$configStatus = "missing"
|
||||||
|
$config = @{}
|
||||||
|
|
||||||
|
if (Test-Path $configPath) {
|
||||||
|
try {
|
||||||
|
Add-Type -AssemblyName System.Web.Extensions -ErrorAction SilentlyContinue
|
||||||
|
$serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
|
||||||
|
$serializer.MaxJsonLength = 67108864
|
||||||
|
$rawJson = Get-Content $configPath -Raw
|
||||||
|
$config = $serializer.DeserializeObject($rawJson)
|
||||||
|
if (-not ($config -is [System.Collections.IDictionary])) {
|
||||||
|
$config = @{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($config.ContainsKey('mcpServers') -and $config['mcpServers'] -is [System.Collections.IDictionary]) {
|
||||||
|
$servers = $config['mcpServers']
|
||||||
|
if ($servers.Contains('zen') -and $servers['zen'] -is [System.Collections.IDictionary]) {
|
||||||
|
$zenConfig = $servers['zen']
|
||||||
|
$commandMatches = ($zenConfig['command'] -eq $PythonPath)
|
||||||
|
|
||||||
|
$argsValue = $zenConfig['args']
|
||||||
|
$argsList = @()
|
||||||
|
if ($argsValue -is [System.Collections.IEnumerable] -and $argsValue -isnot [string]) {
|
||||||
|
$argsList = @($argsValue)
|
||||||
|
} elseif ($null -ne $argsValue) {
|
||||||
|
$argsList = @($argsValue)
|
||||||
|
}
|
||||||
|
$argsMatches = ($argsList.Count -eq 1 -and $argsList[0] -eq $ServerPath)
|
||||||
|
|
||||||
|
$cwdValue = $null
|
||||||
|
if ($zenConfig.Contains('cwd')) {
|
||||||
|
$cwdValue = $zenConfig['cwd']
|
||||||
|
}
|
||||||
|
$cwdMatches = ([string]::IsNullOrEmpty($cwdValue) -or $cwdValue -eq $scriptDir)
|
||||||
|
|
||||||
|
if ($commandMatches -and $argsMatches -and $cwdMatches) {
|
||||||
|
Write-Success "Qwen CLI already configured for zen server"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$configStatus = "mismatch"
|
||||||
|
Write-Warning "Existing Qwen CLI configuration differs from the current setup."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
$configStatus = "invalid"
|
||||||
|
Write-Warning "Unable to parse Qwen CLI settings at $configPath ($_)."
|
||||||
|
$config = @{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$envMap = [ordered]@{}
|
||||||
|
if (Test-Path ".env") {
|
||||||
|
foreach ($line in Get-Content ".env") {
|
||||||
|
$trimmed = $line.Trim()
|
||||||
|
if ([string]::IsNullOrWhiteSpace($trimmed) -or $trimmed.StartsWith('#')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($line -match '^\s*([^=]+)=(.*)$') {
|
||||||
|
$key = $matches[1].Trim()
|
||||||
|
$value = $matches[2]
|
||||||
|
$value = ($value -replace '\s+#.*$', '').Trim()
|
||||||
|
if ($value.StartsWith('"') -and $value.EndsWith('"')) {
|
||||||
|
$value = $value.Substring(1, $value.Length - 2)
|
||||||
|
}
|
||||||
|
if ([string]::IsNullOrWhiteSpace($value)) {
|
||||||
|
$value = [Environment]::GetEnvironmentVariable($key, "Process")
|
||||||
|
}
|
||||||
|
if (![string]::IsNullOrWhiteSpace($value) -and $value -notmatch '^your_.*_here$') {
|
||||||
|
$envMap[$key] = $value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$extraKeys = @(
|
||||||
|
"GEMINI_API_KEY","OPENAI_API_KEY","XAI_API_KEY","DIAL_API_KEY","OPENROUTER_API_KEY",
|
||||||
|
"AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT","AZURE_OPENAI_API_VERSION","AZURE_OPENAI_ALLOWED_MODELS","AZURE_MODELS_CONFIG_PATH",
|
||||||
|
"CUSTOM_API_URL","CUSTOM_API_KEY","CUSTOM_MODEL_NAME","DEFAULT_MODEL","GOOGLE_ALLOWED_MODELS",
|
||||||
|
"OPENAI_ALLOWED_MODELS","OPENROUTER_ALLOWED_MODELS","XAI_ALLOWED_MODELS","DEFAULT_THINKING_MODE_THINKDEEP",
|
||||||
|
"DISABLED_TOOLS","CONVERSATION_TIMEOUT_HOURS","MAX_CONVERSATION_TURNS","LOG_LEVEL","ZEN_MCP_FORCE_ENV_OVERRIDE"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($key in $extraKeys) {
|
||||||
|
if (-not $envMap.Contains($key)) {
|
||||||
|
$value = [Environment]::GetEnvironmentVariable($key, "Process")
|
||||||
|
if (![string]::IsNullOrWhiteSpace($value) -and $value -notmatch '^your_.*_here$') {
|
||||||
|
$envMap[$key] = $value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$prompt = "Configure Zen for Qwen CLI? (y/N)"
|
||||||
|
if ($configStatus -eq "mismatch" -or $configStatus -eq "invalid") {
|
||||||
|
$prompt = "Update Qwen CLI zen configuration? (y/N)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = Read-Host $prompt
|
||||||
|
if ($response -ne 'y' -and $response -ne 'Y') {
|
||||||
|
Write-Info "Skipping Qwen CLI integration"
|
||||||
|
Show-QwenManualConfig $PythonPath $ServerPath $scriptDir $configPath $envMap
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $configDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $configDir -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path $configPath -and $configStatus -ne "missing") {
|
||||||
|
Manage-ConfigBackups $configPath | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (-not ($config -is [System.Collections.IDictionary])) {
|
||||||
|
$config = @{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $config.ContainsKey('mcpServers') -or $config['mcpServers'] -isnot [System.Collections.IDictionary]) {
|
||||||
|
$config['mcpServers'] = @{}
|
||||||
|
}
|
||||||
|
|
||||||
|
$zenConfig = [ordered]@{
|
||||||
|
command = $PythonPath
|
||||||
|
args = @($ServerPath)
|
||||||
|
cwd = $scriptDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($envMap.Count -gt 0) {
|
||||||
|
$zenConfig['env'] = $envMap
|
||||||
|
}
|
||||||
|
|
||||||
|
$config['mcpServers']['zen'] = $zenConfig
|
||||||
|
|
||||||
|
$json = ($config | ConvertTo-Json -Depth 20)
|
||||||
|
Set-Content -Path $configPath -Value $json -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Success "Successfully configured Qwen CLI"
|
||||||
|
Write-Host " Config: $configPath" -ForegroundColor Gray
|
||||||
|
Write-Host " Restart Qwen CLI to use Zen MCP Server" -ForegroundColor Gray
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to update Qwen CLI configuration: $_"
|
||||||
|
Show-QwenManualConfig $PythonPath $ServerPath $scriptDir $configPath $envMap
|
||||||
|
}
|
||||||
|
}
|
||||||
"@ -ForegroundColor Yellow
|
"@ -ForegroundColor Yellow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1606,6 +1809,7 @@ function Show-ConfigInstructions {
|
|||||||
Write-Host "✓ Windsurf" -ForegroundColor White
|
Write-Host "✓ Windsurf" -ForegroundColor White
|
||||||
Write-Host "✓ Trae" -ForegroundColor White
|
Write-Host "✓ Trae" -ForegroundColor White
|
||||||
Write-Host "✓ Gemini CLI" -ForegroundColor White
|
Write-Host "✓ Gemini CLI" -ForegroundColor White
|
||||||
|
Write-Host "✓ Qwen CLI" -ForegroundColor White
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "The script automatically detects and configures compatible clients." -ForegroundColor Gray
|
Write-Host "The script automatically detects and configures compatible clients." -ForegroundColor Gray
|
||||||
Write-Host "Restart your MCP clients after configuration to use the Zen MCP Server." -ForegroundColor Yellow
|
Write-Host "Restart your MCP clients after configuration to use the Zen MCP Server." -ForegroundColor Yellow
|
||||||
|
|||||||
306
run-server.sh
306
run-server.sh
@@ -1779,6 +1779,247 @@ EOF
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Print manual Qwen CLI configuration guidance
|
||||||
|
print_qwen_manual_instructions() {
|
||||||
|
local python_cmd="$1"
|
||||||
|
local server_path="$2"
|
||||||
|
local script_dir="$3"
|
||||||
|
local config_path="$4"
|
||||||
|
local env_lines="$5"
|
||||||
|
|
||||||
|
local env_array=()
|
||||||
|
if [[ -n "$env_lines" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
env_array+=("$line")
|
||||||
|
done <<< "$env_lines"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Manual config location: $config_path"
|
||||||
|
echo "Add or update this entry:"
|
||||||
|
|
||||||
|
local env_block=""
|
||||||
|
if [[ ${#env_array[@]} -gt 0 ]]; then
|
||||||
|
env_block=$' "env": {\n'
|
||||||
|
local first=true
|
||||||
|
for env_entry in "${env_array[@]}"; do
|
||||||
|
local key="${env_entry%%=*}"
|
||||||
|
local value="${env_entry#*=}"
|
||||||
|
value=${value//\\/\\\\}
|
||||||
|
value=${value//"/\\"}
|
||||||
|
if [[ "$first" == true ]]; then
|
||||||
|
first=false
|
||||||
|
env_block+=" \"$key\": \"$value\""
|
||||||
|
else
|
||||||
|
env_block+=$',\n '
|
||||||
|
env_block+="\"$key\": \"$value\""
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
env_block+=$'\n }'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$env_block" ]]; then
|
||||||
|
cat << EOF
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zen": {
|
||||||
|
"command": "$python_cmd",
|
||||||
|
"args": ["$server_path"],
|
||||||
|
"cwd": "$script_dir",
|
||||||
|
$env_block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat << EOF
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zen": {
|
||||||
|
"command": "$python_cmd",
|
||||||
|
"args": ["$server_path"],
|
||||||
|
"cwd": "$script_dir"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check and update Qwen Code CLI configuration
|
||||||
|
check_qwen_cli_integration() {
|
||||||
|
local python_cmd="$1"
|
||||||
|
local server_path="$2"
|
||||||
|
|
||||||
|
if ! command -v qwen &> /dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local qwen_config="$HOME/.qwen/settings.json"
|
||||||
|
local script_dir
|
||||||
|
script_dir=$(dirname "$server_path")
|
||||||
|
|
||||||
|
local env_vars
|
||||||
|
env_vars=$(parse_env_variables)
|
||||||
|
local env_array=()
|
||||||
|
if [[ -n "$env_vars" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ -n "$line" && "$line" =~ ^([^=]+)=(.*)$ ]]; then
|
||||||
|
env_array+=("${BASH_REMATCH[1]}=${BASH_REMATCH[2]}")
|
||||||
|
fi
|
||||||
|
done <<< "$env_vars"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local env_lines=""
|
||||||
|
if [[ ${#env_array[@]} -gt 0 ]]; then
|
||||||
|
env_lines=$(printf '%s\n' "${env_array[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local config_status=3
|
||||||
|
if [[ -f "$qwen_config" ]]; then
|
||||||
|
if python3 - "$qwen_config" "$python_cmd" "$server_path" "$script_dir" <<'PYCONF'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
config_path, expected_cmd, expected_arg, expected_cwd = sys.argv[1:5]
|
||||||
|
try:
|
||||||
|
with open(config_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception:
|
||||||
|
sys.exit(5)
|
||||||
|
|
||||||
|
servers = data.get('mcpServers')
|
||||||
|
if not isinstance(servers, dict):
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
config = servers.get('zen')
|
||||||
|
if not isinstance(config, dict):
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
cmd = config.get('command')
|
||||||
|
args = config.get('args') or []
|
||||||
|
cwd = config.get('cwd')
|
||||||
|
|
||||||
|
cwd_matches = cwd in (None, "", expected_cwd)
|
||||||
|
if cmd == expected_cmd and len(args) == 1 and args[0] == expected_arg and cwd_matches:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
sys.exit(4)
|
||||||
|
PYCONF
|
||||||
|
then
|
||||||
|
config_status=0
|
||||||
|
else
|
||||||
|
config_status=$?
|
||||||
|
if [[ $config_status -eq 1 ]]; then
|
||||||
|
config_status=3
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $config_status -eq 0 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ $config_status -eq 4 ]]; then
|
||||||
|
print_warning "Found existing Qwen CLI zen configuration with different settings."
|
||||||
|
elif [[ $config_status -eq 5 ]]; then
|
||||||
|
print_warning "Unable to parse Qwen CLI settings; replacing with a fresh entry may help."
|
||||||
|
fi
|
||||||
|
|
||||||
|
local prompt="Configure Zen for Qwen CLI? (Y/n): "
|
||||||
|
if [[ $config_status -eq 4 || $config_status -eq 5 ]]; then
|
||||||
|
prompt="Update Qwen CLI zen configuration? (Y/n): "
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "$prompt" -n 1 -r
|
||||||
|
echo ""
|
||||||
|
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||||||
|
print_info "Skipping Qwen CLI integration"
|
||||||
|
print_qwen_manual_instructions "$python_cmd" "$server_path" "$script_dir" "$qwen_config" "$env_lines"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$qwen_config")" 2>/dev/null || true
|
||||||
|
if [[ -f "$qwen_config" && $config_status -ne 3 ]]; then
|
||||||
|
cp "$qwen_config" "${qwen_config}.backup_$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
local update_output
|
||||||
|
local update_status=0
|
||||||
|
update_output=$(ZEN_QWEN_ENV="$env_lines" ZEN_QWEN_CMD="$python_cmd" ZEN_QWEN_ARG="$server_path" ZEN_QWEN_CWD="$script_dir" python3 - "$qwen_config" <<'PYUPDATE'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
config_path = pathlib.Path(sys.argv[1])
|
||||||
|
cmd = os.environ['ZEN_QWEN_CMD']
|
||||||
|
arg = os.environ['ZEN_QWEN_ARG']
|
||||||
|
cwd = os.environ['ZEN_QWEN_CWD']
|
||||||
|
env_lines = os.environ.get('ZEN_QWEN_ENV', '').splitlines()
|
||||||
|
|
||||||
|
env_map = {}
|
||||||
|
for line in env_lines:
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
if '=' in line:
|
||||||
|
key, value = line.split('=', 1)
|
||||||
|
env_map[key] = value
|
||||||
|
|
||||||
|
if config_path.exists():
|
||||||
|
try:
|
||||||
|
with config_path.open('r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
except Exception:
|
||||||
|
data = {}
|
||||||
|
else:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
servers = data.get('mcpServers')
|
||||||
|
if not isinstance(servers, dict):
|
||||||
|
servers = {}
|
||||||
|
data['mcpServers'] = servers
|
||||||
|
|
||||||
|
zen_config = {
|
||||||
|
'command': cmd,
|
||||||
|
'args': [arg],
|
||||||
|
'cwd': cwd,
|
||||||
|
}
|
||||||
|
|
||||||
|
if env_map:
|
||||||
|
zen_config['env'] = env_map
|
||||||
|
|
||||||
|
servers['zen'] = zen_config
|
||||||
|
|
||||||
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
tmp_path = config_path.with_suffix(config_path.suffix + '.tmp')
|
||||||
|
with tmp_path.open('w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
f.write('\n')
|
||||||
|
tmp_path.replace(config_path)
|
||||||
|
PYUPDATE
|
||||||
|
) || update_status=$?
|
||||||
|
|
||||||
|
if [[ $update_status -eq 0 ]]; then
|
||||||
|
print_success "Successfully configured Qwen CLI"
|
||||||
|
echo " Config: $qwen_config"
|
||||||
|
echo " Restart Qwen CLI to use Zen MCP Server"
|
||||||
|
else
|
||||||
|
print_error "Failed to update Qwen CLI config"
|
||||||
|
if [[ -n "$update_output" ]]; then
|
||||||
|
echo "$update_output"
|
||||||
|
fi
|
||||||
|
print_qwen_manual_instructions "$python_cmd" "$server_path" "$script_dir" "$qwen_config" "$env_lines"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Display configuration instructions
|
# Display configuration instructions
|
||||||
display_config_instructions() {
|
display_config_instructions() {
|
||||||
local python_cmd="$1"
|
local python_cmd="$1"
|
||||||
@@ -1792,7 +2033,7 @@ display_config_instructions() {
|
|||||||
echo "===== $config_header ====="
|
echo "===== $config_header ====="
|
||||||
printf '%*s\n' "$((${#config_header} + 12))" | tr ' ' '='
|
printf '%*s\n' "$((${#config_header} + 12))" | tr ' ' '='
|
||||||
echo ""
|
echo ""
|
||||||
echo "To use Zen MCP Server with your Claude clients:"
|
echo "To use Zen MCP Server with your CLI clients:"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
print_info "1. For Claude Code (CLI):"
|
print_info "1. For Claude Code (CLI):"
|
||||||
@@ -1833,19 +2074,34 @@ display_config_instructions() {
|
|||||||
done <<< "$env_vars"
|
done <<< "$env_vars"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat << EOF
|
if [[ -n "$example_env" ]]; then
|
||||||
|
cat << EOF
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"zen": {
|
"zen": {
|
||||||
"command": "$python_cmd",
|
"command": "$python_cmd",
|
||||||
"args": ["$server_path"]$(if [[ -n "$example_env" ]]; then echo ","; fi)$(if [[ -n "$example_env" ]]; then echo "
|
"args": ["$server_path"],
|
||||||
\"env\": {
|
"cwd": "$script_dir",
|
||||||
|
"env": {
|
||||||
$(echo -e "$example_env")
|
$(echo -e "$example_env")
|
||||||
}"; fi)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
else
|
||||||
|
cat << EOF
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zen": {
|
||||||
|
"command": "$python_cmd",
|
||||||
|
"args": ["$server_path"],
|
||||||
|
"cwd": "$script_dir"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
# Show platform-specific config location
|
# Show platform-specific config location
|
||||||
local config_path=$(get_claude_config_path)
|
local config_path=$(get_claude_config_path)
|
||||||
@@ -1873,6 +2129,39 @@ EOF
|
|||||||
EOF
|
EOF
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
print_info "For Qwen Code CLI:"
|
||||||
|
echo " Add this configuration to ~/.qwen/settings.json:"
|
||||||
|
echo ""
|
||||||
|
if [[ -n "$example_env" ]]; then
|
||||||
|
cat << EOF
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zen": {
|
||||||
|
"command": "$python_cmd",
|
||||||
|
"args": ["$server_path"],
|
||||||
|
"cwd": "$script_dir",
|
||||||
|
"env": {
|
||||||
|
$(echo -e "$example_env")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat << EOF
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"zen": {
|
||||||
|
"command": "$python_cmd",
|
||||||
|
"args": ["$server_path"],
|
||||||
|
"cwd": "$script_dir"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
print_info "For Codex CLI:"
|
print_info "For Codex CLI:"
|
||||||
echo " Add this configuration to ~/.codex/config.toml:"
|
echo " Add this configuration to ~/.codex/config.toml:"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -2131,12 +2420,15 @@ main() {
|
|||||||
# Step 11: Check Codex CLI integration
|
# Step 11: Check Codex CLI integration
|
||||||
check_codex_cli_integration
|
check_codex_cli_integration
|
||||||
|
|
||||||
# Step 12: Display log information
|
# Step 12: Check Qwen CLI integration
|
||||||
|
check_qwen_cli_integration "$python_cmd" "$server_path"
|
||||||
|
|
||||||
|
# Step 13: Display log information
|
||||||
echo ""
|
echo ""
|
||||||
echo "Logs will be written to: $script_dir/$LOG_DIR/$LOG_FILE"
|
echo "Logs will be written to: $script_dir/$LOG_DIR/$LOG_FILE"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Step 13: Handle command line arguments
|
# Step 14: Handle command line arguments
|
||||||
if [[ "$arg" == "-f" ]] || [[ "$arg" == "--follow" ]]; then
|
if [[ "$arg" == "-f" ]] || [[ "$arg" == "--follow" ]]; then
|
||||||
follow_logs
|
follow_logs
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user