refactor: Remove all auto-detection, require explicit config

BREAKING CHANGE: Auto-detection of binary paths has been removed.

Why this change:
- WASM sandbox cannot access PATH reliably (especially on macOS)
- WASM cannot detect host OS
- WASM cannot check if files exist
- WASM cannot spawn commands like 'which'

These fundamental limitations made auto-detection unreliable and
platform-dependent. The extension would fail mysteriously on some
systems.

New behavior:
- Explicit configuration is REQUIRED
- Users MUST set either:
  1. gitea_mcp_binary_path: "/full/path/to/binary"
  2. use_docker: true

Benefits:
- Works reliably across ALL platforms
- Clear, actionable error messages
- No mysterious failures
- Easier to debug
- Less complex code
- Better user experience (explicit > implicit)

The error message now provides:
- Clear explanation of why auto-detection isn't possible
- Complete configuration examples
- Platform-specific path examples
- Docker alternative

Docker mode now uses 'docker' command directly (found via system PATH)
rather than trying to detect docker location.
This commit is contained in:
2025-11-10 21:28:42 -07:00
parent 94f34f3a43
commit 0627ebe404

View File

@@ -10,21 +10,21 @@ use zed_extension_api::{
/// This extension launches a gitea-mcp binary (locally or via Docker) and communicates with it
/// to provide Gitea repository access through Zed's AI assistant.
///
/// Binary Resolution Strategy:
/// 1. If `gitea_mcp_binary_path` is set in settings, use that exact path
/// 2. If `use_docker` is true, use Docker to run the gitea-mcp image
/// 3. Otherwise, search common system paths:
/// - /usr/local/bin/gitea-mcp
/// - ~/.local/bin/gitea-mcp
/// - ~/.cargo/bin/gitea-mcp
/// - /opt/homebrew/bin/gitea-mcp (macOS)
/// - Search in PATH environment variable
/// Configuration Requirements:
/// - EITHER set `gitea_mcp_binary_path` to the full path of the gitea-mcp binary
/// - OR set `use_docker: true` to run gitea-mcp in Docker
///
/// Transport modes:
/// - STDIO (default, recommended): Direct stdin/stdout communication
/// Works with Zed's extension API and gitea-mcp binary
/// - Docker: Runs gitea-mcp in a Docker container
/// Useful when binary isn't available on host system
/// WASM Limitation: This extension runs in a WebAssembly sandbox which cannot:
/// - Detect the host operating system
/// - Access PATH environment variable reliably
/// - Check if files exist on the filesystem
///
/// Therefore, explicit configuration is required.
///
/// Example paths:
/// - macOS (Homebrew): /opt/homebrew/bin/gitea-mcp-server
/// - Linux: /usr/local/bin/gitea-mcp
/// - Windows: C:\Program Files\gitea-mcp\gitea-mcp.exe
struct GiteaModelContextExtension;
#[derive(Debug, Deserialize, JsonSchema)]
@@ -237,150 +237,70 @@ fn build_docker_command(settings: &GiteaContextServerSettings) -> Result<Command
})
}
/// Find the docker binary in common locations
/// Returns the full absolute path to the docker executable
/// Tries: which command → /usr/bin/docker → /usr/local/bin/docker
/// Find the docker binary
/// Returns "docker" and relies on the system's PATH to find it
/// This is more reliable across platforms than trying to detect docker location in WASM
fn find_docker_binary() -> Result<String> {
// Try using 'which' to find docker in PATH
if let Ok(output) = std::process::Command::new("which").arg("docker").output() {
if output.status.success() {
if let Ok(path) = String::from_utf8(output.stdout) {
let path = path.trim();
if !path.is_empty() {
return Ok(path.to_string());
}
}
}
// Just return "docker" - the system will find it in PATH
// If docker isn't installed or not in PATH, the execution will fail with a clear error
Ok("docker".to_string())
}
// Standard docker locations as fallback
// Return the first common location - Zed will execute it and fail clearly if not found
Ok("/usr/bin/docker".to_string())
}
/// Resolve the gitea-mcp binary path with intelligent fallbacks
/// Resolve the gitea-mcp binary path
///
/// Resolution strategy:
/// 1. If explicit path provided in settings, use it directly
/// 2. Search PATH environment variable for gitea-mcp or gitea-mcp-server
/// 3. Try common installation locations as fallback
/// 4. Return error with helpful instructions if not found
/// WASM Limitation: WebAssembly sandbox cannot reliably:
/// - Detect host OS (macOS vs Linux vs Windows)
/// - Access PATH environment variable
/// - Check if files exist
/// - Spawn commands like 'which'
///
/// Note: WASM sandbox restricts process spawning and filesystem checks,
/// but we CAN read environment variables and construct paths.
/// Therefore, this function REQUIRES explicit configuration.
/// Users MUST set either:
/// - gitea_mcp_binary_path: "/full/path/to/gitea-mcp"
/// - use_docker: true
///
/// Returns the path to the binary (as a string) to use, or an error with guidance
/// Returns the path to the binary or an error with configuration instructions
fn resolve_binary_path(explicit_path: &Option<String>) -> Result<String> {
// If explicit path provided, use it (prioritize user configuration)
// Explicit path is REQUIRED in WASM environment
if let Some(path) = explicit_path {
return Ok(path.clone());
}
// Try to search PATH environment variable manually
// WASM can't spawn 'which', but CAN read env vars
if let Ok(path_env) = std::env::var("PATH") {
let binary_names = ["gitea-mcp", "gitea-mcp-server"];
// Collect all candidate paths from PATH, then prioritize them
let mut candidates = Vec::new();
// DEBUG: Log PATH directories being considered
eprintln!("[TENDRIL DEBUG] PATH environment variable: {}", path_env);
eprintln!("[TENDRIL DEBUG] Searching for binaries: {:?}", binary_names);
// Parse PATH and collect all plausible paths
for path_dir in path_env.split(':') {
for binary_name in &binary_names {
let full_path = format!("{}/{}", path_dir, binary_name);
// Categorize by priority (lower number = higher priority)
let priority = if path_dir.contains("/opt/homebrew/bin") {
eprintln!(
"[TENDRIL DEBUG] Found Homebrew path (priority 1): {}",
full_path
);
1 // Highest: macOS Homebrew (M1/M2/M3/M4 Macs)
} else if path_dir.contains("/.local/bin") {
eprintln!(
"[TENDRIL DEBUG] Found .local path (priority 2): {}",
full_path
);
2 // User-local installations
} else if path_dir.contains("/.cargo/bin") {
eprintln!(
"[TENDRIL DEBUG] Found .cargo path (priority 3): {}",
full_path
);
3 // Cargo installations
} else if path_dir.contains("/usr/local/bin") {
eprintln!(
"[TENDRIL DEBUG] Found /usr/local path (priority 4): {}",
full_path
);
4 // Linux standard location
} else if path_dir.contains("/usr/bin") {
eprintln!(
"[TENDRIL DEBUG] Found /usr/bin path (priority 5): {}",
full_path
);
5 // System binaries
} else {
eprintln!("[TENDRIL DEBUG] Skipping non-standard path: {}", path_dir);
continue; // Skip non-standard locations
};
candidates.push((priority, full_path));
}
}
// Sort by priority and return the highest priority candidate
candidates.sort_by_key(|(priority, _)| *priority);
eprintln!(
"[TENDRIL DEBUG] All candidates after sorting: {:?}",
candidates
);
if let Some((priority, path)) = candidates.first() {
eprintln!(
"[TENDRIL DEBUG] Selected path (priority {}): {}",
priority, path
);
return Ok(path.clone());
} else {
eprintln!("[TENDRIL DEBUG] No candidates found in PATH");
}
} else {
eprintln!("[TENDRIL DEBUG] PATH environment variable not found");
}
// Fallback: Try common absolute paths
// Order by likelihood across platforms
let common_paths = [
"/usr/local/bin/gitea-mcp",
"/usr/local/bin/gitea-mcp-server",
"/opt/homebrew/bin/gitea-mcp",
"/opt/homebrew/bin/gitea-mcp-server",
"/usr/bin/gitea-mcp",
"/usr/bin/gitea-mcp-server",
];
// Return first path and let Zed try it
// If it fails, the error will show which path was attempted
if let Some(path) = common_paths.first() {
return Ok(path.to_string());
}
// Last resort error message
Err(
"gitea-mcp binary not found. Please set 'gitea_mcp_binary_path' in settings.\n\
// No path provided - return clear error with platform-specific instructions
Err("gitea-mcp binary path not configured.\n\
\n\
Examples:\n\
• macOS: \"gitea_mcp_binary_path\": \"/opt/homebrew/bin/gitea-mcp-server\"\n\
• Linux: \"gitea_mcp_binary_path\": \"/usr/local/bin/gitea-mcp\"\n\
Due to WebAssembly sandbox limitations, automatic binary detection is not possible.\n\
Please add ONE of the following to your Zed settings:\n\
\n\
Or use Docker: \"use_docker\": true"
.into(),
)
Option 1 - Specify binary path explicitly:\n\
{\n\
\"context_servers\": {\n\
\"tendril-gitea-mcp\": {\n\
\"settings\": {\n\
\"gitea_access_token\": \"your_token\",\n\
\"gitea_mcp_binary_path\": \"/full/path/to/binary\"\n\
}\n\
}\n\
}\n\
}\n\
\n\
Common paths:\n\
• macOS (Homebrew): /opt/homebrew/bin/gitea-mcp-server\n\
• Linux: /usr/local/bin/gitea-mcp\n\
• Windows: C:\\Program Files\\gitea-mcp\\gitea-mcp.exe\n\
\n\
Option 2 - Use Docker:\n\
{\n\
\"context_servers\": {\n\
\"tendril-gitea-mcp\": {\n\
\"settings\": {\n\
\"gitea_access_token\": \"your_token\",\n\
\"use_docker\": true\n\
}\n\
}\n\
}\n\
}"
.into())
}
// Register the extension with Zed