From d2723bc29207c942bc93083131e2ac2a8d1a82d5 Mon Sep 17 00:00:00 2001 From: KiCAD MCP Bot Date: Sat, 10 Jan 2026 22:23:54 -0500 Subject: [PATCH] fix: Resolve Python executable validation failure on Linux (Issue #29) This commit fixes the critical bug where the MCP server fails to start on Linux with "Python executable not found: python3" even when python3 is correctly installed and available in PATH. Root Cause: - findPythonExecutable() returned 'python3' (command name) on Linux - validatePrerequisites() used existsSync('python3') which checks current directory, not PATH - Validation failed even though spawn() would resolve 'python3' via PATH Changes Made: 1. Enhanced findPythonExecutable() for Linux (src/server.ts:42-126): - Added Linux platform detection - Check KiCad bundled Python paths first (/usr/lib/kicad/bin/python3, etc.) - Use 'which python3' to resolve system python3 to absolute path - Fallback to common system paths (/usr/bin/python3, /bin/python3) - Import execSync for 'which' command execution 2. Improved validatePrerequisites() (src/server.ts:214-266): - Distinguish between absolute paths and command names - Use existsSync for absolute paths - Use --version execution test for command names - Added Linux-specific error messages and troubleshooting 3. Documentation Updates (README.md:409-444): - Added "Linux Python Detection" section - Documented detection priority order - Added troubleshooting steps for KICAD_PYTHON - Clarified that no manual config needed for standard installations Testing: - Build completed successfully (npm run build) - Python detection now resolves /usr/bin/python3 on Ubuntu/Debian - Maintains backward compatibility with Windows/macOS - KICAD_PYTHON override still works Fixes #29 Co-Authored-By: Claude Sonnet 4.5 --- README.md | 37 ++++++++++++++++++++ src/server.ts | 94 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 121 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f3512ed..538a6d7 100644 --- a/README.md +++ b/README.md @@ -406,6 +406,43 @@ Edit configuration file: - **Windows:** `C:\Program Files\KiCad\9.0\lib\python3\dist-packages` - **macOS:** `/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages` +#### Linux Python Detection + +The server automatically detects Python on Linux in this priority order: + +1. **Virtual environment** - `venv/bin/python` or `.venv/bin/python` (highest priority) +2. **KICAD_PYTHON env var** - User override for non-standard installations +3. **KiCad bundled Python** - `/usr/lib/kicad/bin/python3`, `/usr/local/lib/kicad/bin/python3`, `/opt/kicad/bin/python3` +4. **System Python via which** - Resolves `which python3` to absolute path (e.g., `/usr/bin/python3`) +5. **Common system paths** - `/usr/bin/python3`, `/bin/python3` + +**For most standard Linux installations (Ubuntu, Debian, Fedora, Arch), no KICAD_PYTHON configuration is needed** - the server will automatically find your Python installation. + +**Troubleshooting:** + +If you see "Python executable not found: python3", you can manually specify the Python path: + +```json +{ + "mcpServers": { + "kicad": { + "command": "node", + "args": ["/path/to/KiCAD-MCP-Server/dist/index.js"], + "env": { + "KICAD_PYTHON": "/usr/bin/python3", + "PYTHONPATH": "/usr/lib/kicad/lib/python3/dist-packages" + } + } + } +} +``` + +To find your Python path: +```bash +which python3 # Example output: /usr/bin/python3 +python3 -c "import pcbnew; print(pcbnew.GetBuildVersion())" # Verify pcbnew access +``` + ### Cline (VSCode) Edit: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` diff --git a/src/server.ts b/src/server.ts index 5e8f9d9..6fe9904 100644 --- a/src/server.ts +++ b/src/server.ts @@ -5,7 +5,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import express from 'express'; -import { spawn, exec, ChildProcess } from 'child_process'; +import { spawn, exec, execSync, ChildProcess } from 'child_process'; import { existsSync } from 'fs'; import { join, dirname } from 'path'; import { logger } from './logger.js'; @@ -41,6 +41,8 @@ import { registerDesignPrompts } from './prompts/design.js'; */ function findPythonExecutable(scriptPath: string): string { const isWindows = process.platform === 'win32'; + const isMac = process.platform === 'darwin'; + const isLinux = !isWindows && !isMac; // Get the project root (parent of the python/ directory) const projectRoot = dirname(dirname(scriptPath)); @@ -65,8 +67,6 @@ function findPythonExecutable(scriptPath: string): string { } // Platform-specific KiCAD bundled Python detection - const isMac = process.platform === 'darwin'; - if (isWindows && process.env.PYTHONPATH?.includes('KiCad')) { // Windows: Try KiCAD's bundled Python const kicadPython = 'C:\\Program Files\\KiCad\\9.0\\bin\\python.exe'; @@ -84,9 +84,43 @@ function findPythonExecutable(scriptPath: string): string { return kicadPython; } } + } else if (isLinux) { + // Linux: Try KiCAD bundled Python locations first + const linuxKicadPaths = [ + '/usr/lib/kicad/bin/python3', + '/usr/local/lib/kicad/bin/python3', + '/opt/kicad/bin/python3', + ]; + + for (const path of linuxKicadPaths) { + if (existsSync(path)) { + logger.info(`Found KiCAD bundled Python at: ${path}`); + return path; + } + } + + // Resolve system python3 to full path using 'which' + try { + const result = execSync('which python3', { encoding: 'utf-8' }).trim(); + if (result && existsSync(result)) { + logger.info(`Resolved system Python via which: ${result}`); + return result; + } + } catch (e) { + logger.warn('Failed to resolve python3 via which command'); + } + + // Fallback to common system paths + const systemPaths = ['/usr/bin/python3', '/bin/python3']; + for (const path of systemPaths) { + if (existsSync(path)) { + logger.info(`Found system Python at: ${path}`); + return path; + } + } } - // Default to system Python + // Default to system Python (last resort) logger.info('Using system Python (no venv found)'); return isWindows ? 'python.exe' : 'python3'; } @@ -179,15 +213,55 @@ export class KiCADMcpServer { */ private async validatePrerequisites(pythonExe: string): Promise { const isWindows = process.platform === 'win32'; + const isLinux = process.platform !== 'win32' && process.platform !== 'darwin'; const errors: string[] = []; - // Check if Python executable exists - if (!existsSync(pythonExe)) { - errors.push(`Python executable not found: ${pythonExe}`); + // Check if Python executable exists (for absolute paths) or is executable (for commands) + const isAbsolutePath = pythonExe.startsWith('/') || pythonExe.startsWith('C:') || pythonExe.startsWith('\\'); - if (isWindows) { - errors.push('Windows: Install KiCAD 9.0+ from https://www.kicad.org/download/windows/'); - errors.push('Or run: .\\setup-windows.ps1 for automatic configuration'); + if (isAbsolutePath) { + // Absolute path: use existsSync + if (!existsSync(pythonExe)) { + errors.push(`Python executable not found: ${pythonExe}`); + + if (isWindows) { + errors.push('Windows: Install KiCAD 9.0+ from https://www.kicad.org/download/windows/'); + errors.push('Or run: .\\setup-windows.ps1 for automatic configuration'); + } else if (isLinux) { + errors.push('Linux: Install KiCAD 9.0+ or set KICAD_PYTHON environment variable'); + errors.push('Set KICAD_PYTHON to specify a custom Python path'); + } + } + } else { + // Command name: verify it's executable via --version test + logger.info(`Validating command-based Python executable: ${pythonExe}`); + try { + const { stdout } = await new Promise<{stdout: string, stderr: string}>((resolve, reject) => { + exec(`"${pythonExe}" --version`, { + timeout: 3000, + env: { ...process.env } + }, (error: any, stdout: string, stderr: string) => { + if (error) { + reject(error); + } else { + resolve({ stdout, stderr }); + } + }); + }); + + logger.info(`Python version check passed: ${stdout.trim()}`); + } catch (error: any) { + errors.push(`Python executable not found in PATH: ${pythonExe}`); + errors.push(`Error: ${error.message}`); + errors.push('Set KICAD_PYTHON environment variable to specify full path'); + + if (isLinux) { + errors.push(''); + errors.push('Linux troubleshooting:'); + errors.push('1. Check if python3 is installed: which python3'); + errors.push('2. Install KiCAD: sudo apt install kicad (Ubuntu/Debian)'); + errors.push('3. Set KICAD_PYTHON=/usr/bin/python3 in your MCP config'); + } } }