diff --git a/README.md b/README.md index 4f284fc..cd7b3b6 100644 --- a/README.md +++ b/README.md @@ -505,7 +505,9 @@ See [Windows Installation Guide](docs/WINDOWS_SETUP.md) for detailed instruction ### macOS -**Important:** On macOS, use KiCAD's bundled Python to ensure proper access to pcbnew module. +**Important:** On macOS, use KiCAD's bundled Python to ensure proper access to the `pcbnew` module. + +#### Manual Setup ```bash # Install KiCAD 9.0 from kicad.org/download/macos @@ -529,7 +531,135 @@ pip install -r requirements.txt npm run build ``` -**Note:** The `--system-site-packages` flag is required to access KiCAD's pcbnew module from the virtual environment. +**Note:** The `--system-site-packages` flag is required to access KiCAD's `pcbnew` module from the virtual environment. + +#### Automated Setup + +To simplify configuration with Claude Desktop, this repository provides a macOS setup script: + +```bash +./setup-macos.sh +``` + +In case of error `zsh: permission denied: ./setup-macos.sh` you can either: + +- always allow the script to be executed by running: `chmod +x setup-macos.sh`. +- alternatively explicitly run it with bash: `bash setup-macos.sh` so no chmod change needed. + +This script does **not replace the manual setup above** — it assumes dependencies are already installed and the project is built. Instead, it automates: + +- detection of your environment (Node.js, KiCad Python, `pcbnew`) +- resolving the correct macOS `PYTHONPATH` +- generating the correct Claude Desktop MCP configuration +- safely merging the configuration into your existing Claude config +- optionally writing the configuration with backup support + +##### Basic Usage + +###### Verify setup (no changes) + +```bash +./setup-macos.sh --verify +``` + +###### Preview configuration (dry run) + +```bash +./setup-macos.sh --dry-run +``` + +###### Apply configuration + +```bash +./setup-macos.sh --apply +``` + +After applying, restart Claude Desktop. + +##### Parameters + +###### Required parameters + +None. The script works out-of-the-box using sensible defaults. + +###### Optional parameters + +##### `--name NAME` + +Specify the MCP server name in Claude Desktop. + +Default: + +```text +kicad +``` + +Example: + +```bash +./setup-macos.sh --apply --name kicad-dev +``` + +Use this when: + +- running multiple MCP configurations +- testing forks or development versions +- avoiding overwriting an existing setup + +##### `--claude-config PATH` + +Specify a custom Claude Desktop configuration file. + +Default: + +```text +~/Library/Application Support/Claude/claude_desktop_config.json +``` + +Example: + +```bash +./setup-macos.sh --dry-run --claude-config ~/tmp/claude_config.json +``` + +Use this when: + +- testing configurations safely +- using non-standard config locations +- debugging without modifying your main setup + +##### `--yes` + +Skip confirmation prompt when applying changes. + +Example: + +```bash +./setup-macos.sh --apply --yes +``` + +##### After Setup + +1. Fully quit Claude Desktop +2. Reopen Claude Desktop +3. Open a new chat +4. Click **+ → Connectors** +5. Verify the server appears (e.g. `kicad` or your custom name) + +Test with prompt in Claude Desktop: + +```text +Use the kicad MCP server to run check_kicad_ui. +``` + +##### Notes + +- The script only modifies the `mcpServers` section and leaves all other configuration untouched +- Existing configurations are automatically backed up before changes +- macOS support relies on KiCad’s bundled Python; system Python will not work correctly +- If KiCad is updated or moved, re-run the script to refresh paths + +--- ## Configuration @@ -537,7 +667,8 @@ npm run build Edit configuration file: -- **Linux/macOS:** `~/.config/Claude/claude_desktop_config.json` +- **Linux:** `~/.config/Claude/claude_desktop_config.json` +- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` **Configuration:** diff --git a/docs/PLATFORM_GUIDE.md b/docs/PLATFORM_GUIDE.md index 11e8dd3..999bde0 100644 --- a/docs/PLATFORM_GUIDE.md +++ b/docs/PLATFORM_GUIDE.md @@ -1,22 +1,22 @@ -# Platform Guide: Linux vs Windows +# Platform Guide: Linux, macOS & Windows -This guide explains the differences between using KiCAD MCP Server on Linux and Windows platforms. +This guide explains the differences between using KiCAD MCP Server on Linux, macOS, and Windows platforms. -**Last Updated:** 2025-11-05 +**Last Updated:** 2026-04-11 --- ## Quick Comparison -| Feature | Linux | Windows | -| ------------------------ | ------------------------- | ------------------------------- | -| **Primary Support** | Full (tested extensively) | Community tested | -| **Setup Complexity** | Moderate | Easy (automated script) | -| **Prerequisites** | Manual package management | Automated detection | -| **KiCAD Python Access** | System paths | Bundled with KiCAD | -| **Path Separators** | Forward slash (/) | Backslash (\\) or forward slash | -| **Virtual Environments** | Recommended | Optional | -| **Troubleshooting** | Standard Linux tools | PowerShell diagnostics | +| Feature | Linux | Windows | macOS | +| ------------------------ | ------------------------- | ------------------------------- | ----------------------- | +| **Primary Support** | Full (tested extensively) | Community tested | Community tested | +| **Setup Complexity** | Moderate | Easy (automated script) | Easy (automated script) | +| **Prerequisites** | Manual package management | Automated detection | Automated detection | +| **KiCAD Python Access** | System paths | Bundled with KiCAD | Bundled with KiCAD | +| **Path Separators** | Forward slash (/) | Backslash (\\) or forward slash | Forward slash (/) | +| **Virtual Environments** | Recommended | Optional | Optional | +| **Troubleshooting** | Standard Linux tools | PowerShell diagnostics | Bash diagnostics | --- @@ -65,6 +65,51 @@ Python: /usr/bin/python3 } ``` +### macOS Installation + +**Advantages:** + +- Automated setup script (`setup-macos.sh`) handles detection and configuration +- KiCAD includes bundled Python (no system Python needed for pcbnew) +- Prerequisite checks with clear pass/fail output +- Generates and merges Claude Desktop configuration automatically + +**Process:** + +1. Install KiCAD 9.0 from the official `.dmg` installer +2. Install Node.js (e.g. via Homebrew or nvm) +3. Clone repository +4. Run `npm install && npm run build` +5. Run `setup-macos.sh`: + - `bash setup-macos.sh --verify` — check prerequisites and detected paths + - `bash setup-macos.sh --dry-run` — preview the merged Claude Desktop config + - `bash setup-macos.sh --apply` — write the configuration + +**Typical paths:** + +```bash +KiCAD Python: /Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 +KiCAD Libraries: /Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/3.x/lib/python3.x/site-packages +Node.js: /usr/local/bin/node # or via nvm +``` + +**Configuration example:** + +```json +{ + "mcpServers": { + "kicad": { + "command": "node", + "args": ["/Users/username/KiCAD-MCP-Server/dist/index.js"], + "env": { + "KICAD_PYTHON": "/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3", + "PYTHONPATH": "/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages" + } + } + } +} +``` + ### Windows Installation **Advantages:** @@ -130,6 +175,21 @@ export PYTHONPATH=/usr/lib/kicad/lib/python3/dist-packages python3 -c "import pcbnew" ``` +### macOS Paths + +- Use forward slashes: `/Users/username/project` +- Case-insensitive but case-preserving filesystem (APFS default) +- No drive letters +- KiCAD paths are inside the `.app` bundle + +**Example commands:** + +```bash +cd ~/KiCAD-MCP-Server +export KICAD_PYTHON=/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 +"$KICAD_PYTHON" -c "import pcbnew" +``` + ### Windows Paths - Use backslashes in native commands: `C:\Users\username` @@ -199,6 +259,27 @@ export PYTHONPATH=/usr/lib/kicad/lib/python3/dist-packages echo 'export PYTHONPATH=/usr/lib/kicad/lib/python3/dist-packages' >> ~/.bashrc ``` +### macOS + +**KiCAD Bundled Python:** + +- KiCAD bundles Python inside the `.app` framework (versions 3.9–3.12) +- No system Python installation needed for pcbnew +- `setup-macos.sh` detects the correct path automatically + +**Setup:** + +```bash +# Check KiCAD Python +/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 --version + +# Verify pcbnew module +/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 -c "import pcbnew; print(pcbnew.GetBuildVersion())" + +# Or use the setup script to verify everything at once +bash setup-macos.sh --verify +``` + ### Windows **KiCAD Bundled Python:** @@ -275,6 +356,32 @@ export PYTHONPATH=/usr/lib/kicad/lib/python3/dist-packages node dist/index.js ``` +### macOS + +**Check KiCAD installation:** + +```bash +ls /Applications/KiCad/KiCad.app +``` + +**Run automated diagnostics:** + +```bash +bash setup-macos.sh --verify +``` + +**View logs:** + +```bash +tail -f ~/.kicad-mcp/logs/kicad_interface.log +``` + +**Start server manually:** + +```bash +node dist/index.js +``` + ### Windows **Check KiCAD installation:** @@ -356,6 +463,30 @@ sudo apt-get install python3-wxgtk4.0 python3-cairo ldd /usr/lib/kicad/lib/python3/dist-packages/pcbnew.so ``` +### macOS-Specific Issues + +**1. KiCad Python Not Found** + +```bash +# Verify the expected path exists +ls /Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3 + +# If installed elsewhere, set the override +export KICAD_PYTHON=/path/to/your/kicad/python3 +bash setup-macos.sh --verify +``` + +**2. pcbnew Import Fails** + +- Run `bash setup-macos.sh --verify` — the Prerequisites section will show a ✗ if pcbnew can't be imported +- Reinstall KiCAD if the bundled Python is corrupted + +**3. Claude Config Not Picked Up** + +- Default path is `~/Library/Application Support/Claude/claude_desktop_config.json` +- Use `--claude-config` flag to point to a different location +- Fully quit and reopen Claude Desktop after changes + ### Windows-Specific Issues **1. Server Exits Immediately** @@ -479,6 +610,14 @@ npm test 4. **Check file permissions** if encountering access errors 5. **Monitor system logs** with `journalctl` if needed +### macOS + +1. **Run `setup-macos.sh --verify` first** — confirms all prerequisites +2. **Use `--dry-run` before `--apply`** — review the merged config before writing +3. **Use KiCAD's bundled Python** — don't rely on system or Homebrew Python for pcbnew +4. **Override with `KICAD_PYTHON` env var** if KiCAD is in a non-standard location +5. **Check logs** in `~/.kicad-mcp/logs/` when debugging + ### Windows 1. **Run setup-windows.ps1 first** - saves time troubleshooting @@ -507,6 +646,13 @@ npm test 4. Update PYTHONPATH to Linux format 5. Set file permissions: `chmod +x python/kicad_interface.py` +### Moving to/from macOS + +1. Clone repository on the target machine +2. Run `npm install && npm run build` +3. Run `bash setup-macos.sh --apply` (to macOS) or follow the target platform's setup +4. No project file changes needed + **KiCAD project files (.kicad_pro, .kicad_pcb) are identical across platforms.** --- @@ -520,6 +666,13 @@ npm test - Search: GitHub Issues filtered by `linux` label - Community: Linux users in Discussions +### macOS Support + +- Check: [README.md](../README.md) macOS installation section +- Run: `bash setup-macos.sh --verify` for automated diagnostics +- Search: GitHub Issues filtered by `macos` label +- Community: macOS users in Discussions + ### Windows Support - Check: [README.md](../README.md) Windows installation section @@ -539,6 +692,12 @@ npm test - You're developing or contributing to the project - You need maximum performance +**Choose macOS if:** + +- You're already using KiCAD on macOS +- You want automated setup with `setup-macos.sh` +- You prefer a Unix-based development environment + **Choose Windows if:** - You want automated setup and diagnostics @@ -546,16 +705,18 @@ npm test - You need detailed troubleshooting guidance - You're a KiCAD user new to development tools -**Both platforms work well for PCB design with KiCAD MCP. Choose based on your comfort level and existing development environment.** +**All platforms work well for PCB design with KiCAD MCP. Choose based on your comfort level and existing development environment.** --- **For platform-specific installation instructions, see:** - Linux: [README.md - Linux Installation](../README.md#linux-ubuntudebian) +- macOS: [README.md - macOS Installation](../README.md#macos) - Windows: [README.md - Windows Installation](../README.md#windows-1011) **For troubleshooting:** - Linux: [KNOWN_ISSUES.md](./KNOWN_ISSUES.md) +- macOS: Run `bash setup-macos.sh --verify` - Windows: [WINDOWS_TROUBLESHOOTING.md](./WINDOWS_TROUBLESHOOTING.md) diff --git a/docs/STATUS_SUMMARY.md b/docs/STATUS_SUMMARY.md index df216c8..136b080 100644 --- a/docs/STATUS_SUMMARY.md +++ b/docs/STATUS_SUMMARY.md @@ -90,9 +90,11 @@ The server automatically selects the best backend: ### macOS -- Community Supported -- Configuration provided +- Automated setup script (setup-macos.sh) +- Auto-detects KiCad Python and pcbnew +- Generates Claude Desktop configuration - Process detection implemented -- Library paths configured +- Library paths auto-configured - Needs community testing --- @@ -170,4 +172,4 @@ The server automatically selects the best backend: --- -_Last Updated: 2026-03-21_ +_Last Updated: 2026-04-11_ diff --git a/setup-macos.sh b/setup-macos.sh new file mode 100644 index 0000000..2d207df --- /dev/null +++ b/setup-macos.sh @@ -0,0 +1,317 @@ +#!/usr/bin/env bash +set -euo pipefail + +MODE="" +ASSUME_YES=0 +SERVER_NAME="kicad" +CLAUDE_CONFIG_PATH="" + +SCRIPT_NAME="$(basename "$0")" + +usage() { + cat <&2; exit 1; } +info() { echo "${SYM_OK} $1"; } +warn() { echo "${SYM_WARN} ${YELLOW}$1${RESET}"; } + +section() { + echo + echo "${DIM}────────────────────────────────────────────────────${RESET}" + echo "${BOLD}${CYAN}$1${RESET}" + echo "${DIM}────────────────────────────────────────────────────${RESET}" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --verify) + MODE="verify" + shift + ;; + --dry-run) + MODE="dry-run" + shift + ;; + --apply) + MODE="apply" + shift + ;; + --yes) + ASSUME_YES=1 + shift + ;; + --name) + [[ $# -ge 2 ]] || fail "--name requires a value" + SERVER_NAME="$2" + shift 2 + ;; + --claude-config) + [[ $# -ge 2 ]] || fail "--claude-config requires a value" + CLAUDE_CONFIG_PATH="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + fail "Unknown argument: $1" + ;; + esac +done + +[[ -n "$MODE" ]] || { usage; exit 1; } +[[ -n "$SERVER_NAME" ]] || fail "Server name must not be empty" + +if [[ -z "$CLAUDE_CONFIG_PATH" ]]; then + CLAUDE_CONFIG_PATH="$HOME/Library/Application Support/Claude/claude_desktop_config.json" +fi + +case "$CLAUDE_CONFIG_PATH" in + "~/"*) + CLAUDE_CONFIG_PATH="$HOME/${CLAUDE_CONFIG_PATH#~/}" + ;; +esac + +CLAUDE_CONFIG_DIR="$(dirname "$CLAUDE_CONFIG_PATH")" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [[ -f "$SCRIPT_DIR/package.json" ]]; then + REPO_ROOT="$SCRIPT_DIR" +else + REPO_ROOT="$(pwd)" +fi + +DIST_JS="$REPO_ROOT/dist/index.js" + +DEFAULT_KICAD_PYTHON="/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3" +KICAD_PYTHON="${KICAD_PYTHON:-$DEFAULT_KICAD_PYTHON}" + +command -v python3 >/dev/null 2>&1 || fail "python3 not found" +NODE_PATH="$(command -v node || true)" +[[ -n "$NODE_PATH" ]] || fail "node not found in PATH" + +[[ -f "$DIST_JS" ]] || fail "Missing build artifact: $DIST_JS. Run 'npm install && npm run build' first." +[[ -x "$KICAD_PYTHON" ]] || fail "KiCad Python not found or not executable: $KICAD_PYTHON" + +DETECT_JSON="$("$KICAD_PYTHON" - <<'PY' +import json, sys, sysconfig +result = { + "python_executable": sys.executable, + "python_version": sys.version.split()[0], + "purelib": sysconfig.get_paths().get("purelib"), + "pcbnew_ok": False, + "pcbnew_version": None, + "pcbnew_error": None, +} +try: + import pcbnew + result["pcbnew_ok"] = True + if hasattr(pcbnew, "GetBuildVersion"): + result["pcbnew_version"] = pcbnew.GetBuildVersion() +except Exception as e: + result["pcbnew_error"] = repr(e) +print(json.dumps(result)) +PY +)" + +PYTHON_EXE="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["python_executable"])' "$DETECT_JSON")" +PYTHON_VERSION="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["python_version"])' "$DETECT_JSON")" +PYTHONPATH_VALUE="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["purelib"])' "$DETECT_JSON")" +PCBNEW_OK="$(python3 -c 'import json,sys; print("true" if json.loads(sys.argv[1])["pcbnew_ok"] else "false")' "$DETECT_JSON")" +PCBNEW_VERSION="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["pcbnew_version"] or "")' "$DETECT_JSON")" +PCBNEW_ERROR="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["pcbnew_error"] or "")' "$DETECT_JSON")" + +if [[ "$PCBNEW_OK" != "true" ]]; then + fail "KiCad Python could not import pcbnew. Details: $PCBNEW_ERROR" +fi + +CONFIG_FRAGMENT_JSON="$(python3 - "$NODE_PATH" "$DIST_JS" "$KICAD_PYTHON" "$PYTHONPATH_VALUE" <<'PY' +import json, sys +fragment = { + "command": sys.argv[1], + "args": [sys.argv[2]], + "env": { + "KICAD_PYTHON": sys.argv[3], + "PYTHONPATH": sys.argv[4], + "LOG_LEVEL": "info" + } +} +print(json.dumps(fragment, indent=2)) +PY +)" + +show_detected() { + section "Prerequisites" + echo " ${SYM_OK} python3 $(command -v python3)" + echo " ${SYM_OK} node $NODE_PATH" + echo " ${SYM_OK} build artifact $DIST_JS" + echo " ${SYM_OK} KiCad Python $KICAD_PYTHON" + echo " ${SYM_OK} pcbnew import $PCBNEW_VERSION" + + section "Configuration" + echo " Server name: ${BOLD}$SERVER_NAME${RESET}" + echo " Repo root: $REPO_ROOT" + echo " Python executable: $PYTHON_EXE" + echo " Python version: $PYTHON_VERSION" + echo " PYTHONPATH: $PYTHONPATH_VALUE" + echo " Claude config: $CLAUDE_CONFIG_PATH" +} + +merge_config() { + python3 - "$CLAUDE_CONFIG_PATH" "$CONFIG_FRAGMENT_JSON" "$SERVER_NAME" <<'PY' +import json, os, sys + +config_path = sys.argv[1] +fragment = json.loads(sys.argv[2]) +server_name = sys.argv[3] + +existing = {} +status = { + "config_exists": False, + "config_valid": True, + "had_mcpServers": False, + "had_entry": False, +} + +if os.path.exists(config_path): + status["config_exists"] = True + try: + with open(config_path, "r", encoding="utf-8") as f: + text = f.read().strip() + existing = json.loads(text) if text else {} + except Exception: + print(json.dumps({"error": "Existing Claude config is not valid JSON", "status": status})) + sys.exit(2) + +if not isinstance(existing, dict): + print(json.dumps({"error": "Existing Claude config root is not a JSON object", "status": status})) + sys.exit(2) + +if "mcpServers" in existing: + status["had_mcpServers"] = True + if not isinstance(existing["mcpServers"], dict): + print(json.dumps({"error": "'mcpServers' exists but is not an object", "status": status})) + sys.exit(2) +else: + existing["mcpServers"] = {} + +if server_name in existing["mcpServers"]: + status["had_entry"] = True + +existing["mcpServers"][server_name] = fragment +print(json.dumps({"status": status, "merged": existing}, indent=2)) +PY +} + +if [[ "$MODE" == "verify" ]]; then + show_detected + section "Proposed Claude Desktop entry ('$SERVER_NAME')" + echo "$CONFIG_FRAGMENT_JSON" + exit 0 +fi + +MERGE_RESULT="$(merge_config 2>&1)" || { + echo "$MERGE_RESULT" + exit 1 +} + +MERGED_JSON="$(python3 -c 'import json,sys; print(json.dumps(json.loads(sys.stdin.read())["merged"], indent=2))' <<<"$MERGE_RESULT")" +CONFIG_EXISTS="$(python3 -c 'import json,sys; print("true" if json.loads(sys.stdin.read())["status"]["config_exists"] else "false")' <<<"$MERGE_RESULT")" +HAD_ENTRY="$(python3 -c 'import json,sys; print("true" if json.loads(sys.stdin.read())["status"]["had_entry"] else "false")' <<<"$MERGE_RESULT")" + +show_detected + +section "Proposed MCP entry ('$SERVER_NAME')" +echo "$CONFIG_FRAGMENT_JSON" + +section "Claude Desktop config" +if [[ "$CONFIG_EXISTS" == "true" ]]; then + if [[ "$HAD_ENTRY" == "true" ]]; then + warn "Existing config already has mcpServers.$SERVER_NAME — it will be replaced." + else + info "Existing config will be preserved; mcpServers.$SERVER_NAME will be added." + fi +else + info "Config does not exist yet. A new file will be created." +fi +echo +echo "${DIM}Merged config preview:${RESET}" +echo "$MERGED_JSON" + +if [[ "$MODE" == "dry-run" ]]; then + exit 0 +fi + +mkdir -p "$CLAUDE_CONFIG_DIR" + +if [[ $ASSUME_YES -ne 1 ]]; then + echo + read -r -p "Write this configuration to $CLAUDE_CONFIG_PATH ? [y/N] " REPLY + case "$REPLY" in + y|Y|yes|YES) ;; + *) + echo "Aborted." + exit 0 + ;; + esac +fi + +ORIG_MODE="" +if [[ -f "$CLAUDE_CONFIG_PATH" ]]; then + ORIG_MODE="$(stat -f '%Lp' "$CLAUDE_CONFIG_PATH")" + BACKUP_PATH="${CLAUDE_CONFIG_PATH}.bak.$(date +%Y%m%d-%H%M%S)" + cp "$CLAUDE_CONFIG_PATH" "$BACKUP_PATH" + info "Backup written to ${DIM}$BACKUP_PATH${RESET}" +fi + +TMP_PATH="${CLAUDE_CONFIG_PATH}.tmp.$$" +(umask 077 && printf '%s\n' "$MERGED_JSON" > "$TMP_PATH") +if [[ -n "$ORIG_MODE" ]]; then + chmod "$ORIG_MODE" "$TMP_PATH" +fi +mv "$TMP_PATH" "$CLAUDE_CONFIG_PATH" + +section "Done" +info "Claude Desktop configuration updated successfully." + +echo +echo "${BOLD}Next steps:${RESET}" +echo " 1. Fully quit Claude Desktop" +echo " 2. Reopen Claude Desktop" +echo " 3. In a new chat, check: + → Connectors" +echo " 4. Verify with:" +echo " Use the ${BOLD}$SERVER_NAME${RESET} MCP server to run ${BOLD}check_kicad_ui${RESET}." +echo