From 4081d8ee6d405138f4edfac6360152ab6f3db750 Mon Sep 17 00:00:00 2001 From: Ryan Parmeter Date: Mon, 10 Nov 2025 17:56:41 -0700 Subject: [PATCH] fix: Improve binary path discovery to check all paths before fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Binary discovery was returning the first absolute path without checking if it exists. On macOS with Homebrew, it would return /usr/local/bin/gitea-mcp immediately, before checking /opt/homebrew/bin/gitea-mcp-server where the binary actually is. ## Solution Restructure the binary path search logic: 1. First pass: Iterate through ALL search paths and check if they exist() - Return immediately when a path that exists is found - This ensures we find the actual binary, not just return the first path 2. Fallback: Only if NO paths exist (WASM sandbox restricts exists() checks), use the first absolute path ## Impact - Homebrew binaries on macOS are now properly discovered - Binary discovery works correctly on all platforms - WASM sandbox fallback still works for cases where exists() is restricted ## Testing - ✅ Tested on Linux with explicit path - works - ✅ Tested on Linux with auto-discovery - works - 🔄 Testing on macOS M4 with Homebrew - should now work --- README.md | 69 +++++++++++++++++++++++++++++------------ src/mcp_server_gitea.rs | 21 ++++++++----- 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index bf37935..a07b17f 100644 --- a/README.md +++ b/README.md @@ -28,15 +28,22 @@ Before you can use Tendril, you need: #### 1. Install the Gitea MCP Binary -The extension automatically searches for `gitea-mcp` in these locations: -- `/usr/local/bin/gitea-mcp` -- `~/.local/bin/gitea-mcp` -- `~/.cargo/bin/gitea-mcp` -- `/opt/homebrew/bin/gitea-mcp` (macOS M-series) +The extension automatically searches for `gitea-mcp` (and `gitea-mcp-server`) in these locations: +- `/usr/local/bin/gitea-mcp` or `gitea-mcp-server` +- `~/.local/bin/gitea-mcp` or `gitea-mcp-server` +- `~/.cargo/bin/gitea-mcp` or `gitea-mcp-server` +- `/opt/homebrew/bin/gitea-mcp` or `gitea-mcp-server` (macOS) - Anywhere in your PATH Choose an installation method: +**macOS (Recommended - Homebrew):** +```bash +# Install via Homebrew (handles binary verification and updates) +brew install gitea/tap/gitea-mcp-server +``` +This installs to `/opt/homebrew/bin/gitea-mcp-server` and is the easiest method for macOS users. + **Download Pre-built Binary:** ```bash # Download from: https://gitea.com/gitea/gitea-mcp/releases @@ -291,16 +298,25 @@ Once Tendril is published to the Zed Extension Marketplace, you'll be able to in If you get an error about gitea-mcp not being found: -1. **Using binary mode:** - - Install gitea-mcp to `/usr/local/bin/gitea-mcp` or another standard location - - Or set `gitea_mcp_binary_path` to the full path in your settings - - Verify it's executable: `chmod +x /path/to/gitea-mcp` - - Test it: `/path/to/gitea-mcp --help` +**For macOS:** +- Install via Homebrew: `brew install gitea/tap/gitea-mcp-server` +- Or set `gitea_mcp_binary_path` to the full path in your settings +- Verify it works: `/opt/homebrew/bin/gitea-mcp-server --help` -2. **Using Docker mode:** - - Ensure Docker is installed and running - - Set `use_docker: true` in settings - - Let Docker pull the image automatically +**For Linux:** +- Download from https://gitea.com/gitea/gitea-mcp/releases to `/usr/local/bin/gitea-mcp` +- Or build from source and run `make install` +- Or set `gitea_mcp_binary_path` to your custom location + +**For all platforms:** +- Verify the binary is executable: `chmod +x /path/to/gitea-mcp` +- Test it manually: `/path/to/gitea-mcp --help` +- If you've installed it in a non-standard location, use the `gitea_mcp_binary_path` setting + +**Using Docker mode:** +- Ensure Docker is installed and running +- Set `use_docker: true` in settings +- Let Docker pull the image automatically ### "Failed to Spawn Command" Error @@ -368,15 +384,28 @@ Check the Zed log for errors: When **not** using Docker, Tendril uses this search order: 1. **Explicitly configured path** (if `gitea_mcp_binary_path` is set) -2. **System standard locations**: - - `/usr/local/bin/gitea-mcp` - - `~/.local/bin/gitea-mcp` - - `~/.cargo/bin/gitea-mcp` - - `/opt/homebrew/bin/gitea-mcp` (macOS M-series) -3. **PATH environment variable** (any location in your PATH) + - User configuration takes priority + - Example: `"gitea_mcp_binary_path": "/opt/homebrew/bin/gitea-mcp-server"` + +2. **System standard locations** (searches for both `gitea-mcp` and `gitea-mcp-server`): + - `/usr/local/bin/gitea-mcp` or `gitea-mcp-server` + - `/usr/bin/gitea-mcp` or `gitea-mcp-server` + - `~/.local/bin/gitea-mcp` or `gitea-mcp-server` + - `~/bin/gitea-mcp` or `gitea-mcp-server` + - `~/.cargo/bin/gitea-mcp` or `gitea-mcp-server` + - `/opt/homebrew/bin/gitea-mcp` or `gitea-mcp-server` (macOS - installed via Homebrew) + +3. **PATH environment variable** (any location in your system PATH) If the binary isn't found, you'll get a helpful error message with troubleshooting steps. +### macOS Homebrew Note + +If you installed via Homebrew (`brew install gitea/tap/gitea-mcp-server`), the binary will be at: +- `/opt/homebrew/bin/gitea-mcp-server` (M-series/ARM64 Macs) + +This location is automatically searched, so no configuration needed! + ## Debugging ### View Gitea MCP Logs diff --git a/src/mcp_server_gitea.rs b/src/mcp_server_gitea.rs index 59337da..97eb128 100644 --- a/src/mcp_server_gitea.rs +++ b/src/mcp_server_gitea.rs @@ -307,21 +307,28 @@ fn resolve_binary_path(explicit_path: &Option) -> Result { )); } - // Check each default path - try with exists() check + // Check each default path - prioritize paths that actually exist + let mut fallback_path: Option = None; + for path in &search_paths { // Try exists() - may not work in WASM but worth trying if path.exists() { return Ok(path.display().to_string()); } - // Also try as fallback: if it can be displayed and is absolute, try it - let path_str = path.display().to_string(); - if path.is_absolute() && !path_str.is_empty() { - // Return the path even if exists() check fails - // (may be due to WASM sandbox limitations) - return Ok(path_str); + // Store first absolute path as fallback in case exists() is restricted by WASM + if fallback_path.is_none() { + let path_str = path.display().to_string(); + if path.is_absolute() && !path_str.is_empty() { + fallback_path = Some(path_str); + } } } + // Return fallback if no path existed + if let Some(path) = fallback_path { + return Ok(path); + } + // Try to find in PATH environment variable if let Ok(path_env) = std::env::var("PATH") { let separator = if cfg!(target_os = "windows") {