Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
204e95a
fix: resolve UV path override not being detected in System Requirements
whatevertogo Jan 11, 2026
25e5d05
fix: improve uv/uvx detection robustness on macOS and Linux
whatevertogo Jan 11, 2026
cc22320
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 11, 2026
8d5fa2f
refactor: unify process execution with ExecPath.TryRun and add Window…
whatevertogo Jan 11, 2026
84cee8c
fix: improve version parsing to handle both spaces and parentheses
whatevertogo Jan 11, 2026
510e631
refactor: improve platform detectors with absolute path resolution
whatevertogo Jan 11, 2026
bf41479
fix: improve error handling in PathResolverService by logging exceptions
whatevertogo Jan 11, 2026
f39857c
Remove .meta files added after fork and update .gitignore
whatevertogo Jan 11, 2026
c4be11c
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 12, 2026
1832715
Update .gitignore
whatevertogo Jan 12, 2026
554ddd0
save .meta
whatevertogo Jan 12, 2026
fb5909d
refactor: unify uv/uvx naming and path detection across platforms
whatevertogo Jan 12, 2026
254125a
fix: improve validation light(uvxPathStatus) logic for UVX path overr…
whatevertogo Jan 12, 2026
f9ae5d5
refactor: streamline UV version validation and unify path detection m…
whatevertogo Jan 12, 2026
84cb9c6
fix: add type handling for Claude Code client in config JSON builder
whatevertogo Jan 12, 2026
ee33077
fix: correct command from 'uvx' to 'uv' for Python version listing in…
whatevertogo Jan 12, 2026
2c3ebcd
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected string GetUvxPathOrError()
string uvx = MCPServiceLocator.Paths.GetUvxPath();
if (string.IsNullOrEmpty(uvx))
{
throw new InvalidOperationException("uv not found. Install uv/uvx or set the override in Advanced Settings.");
throw new InvalidOperationException("uvx not found. Install uv/uvx or set the override in Advanced Settings.");
}
return uvx;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Dependencies.Models;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Services;

namespace MCPForUnity.Editor.Dependencies.PlatformDetectors
{
Expand Down Expand Up @@ -92,38 +94,35 @@ public override string GetInstallationRecommendations()

public override DependencyStatus DetectUv()
{
var status = new DependencyStatus("uv Package Manager", isRequired: true)
// First, honor overrides and cross-platform resolution via the base implementation
var status = base.DetectUv();
if (status.IsAvailable)
{
InstallationHint = GetUvInstallUrl()
};
return status;
}

// If the user configured an override path, keep the base result (failure typically means the override path is invalid)
if (MCPServiceLocator.Paths.HasUvxPathOverride)
{
return status;
}

try
{
// Try running uv/uvx directly with augmented PATH
if (TryValidateUv("uv", out string version, out string fullPath) ||
TryValidateUv("uvx", out version, out fullPath))
string augmentedPath = BuildAugmentedPath();

// Try uv first, then uvx, using ExecPath.TryRun for proper timeout handling
if (TryValidateUvWithPath("uv", augmentedPath, out string version, out string fullPath) ||
TryValidateUvWithPath("uvx", augmentedPath, out version, out fullPath))
{
status.IsAvailable = true;
status.Version = version;
status.Path = fullPath;
status.Details = $"Found uv {version} in PATH";
status.ErrorMessage = null;
return status;
}

// Fallback: use which with augmented PATH
if (TryFindInPath("uv", out string pathResult) ||
TryFindInPath("uvx", out pathResult))
{
if (TryValidateUv(pathResult, out version, out fullPath))
{
status.IsAvailable = true;
status.Version = version;
status.Path = fullPath;
status.Details = $"Found uv {version} in PATH";
return status;
}
}

status.ErrorMessage = "uv not found in PATH";
status.Details = "Install uv package manager and ensure it's added to PATH.";
}
Expand All @@ -142,45 +141,29 @@ private bool TryValidatePython(string pythonPath, out string version, out string

try
{
var psi = new ProcessStartInfo
{
FileName = pythonPath,
Arguments = "--version",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
string augmentedPath = BuildAugmentedPath();

// Set PATH to include common locations
var homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var pathAdditions = new[]
// First, try to resolve the absolute path for better UI/logging display
string commandToRun = pythonPath;
if (TryFindInPath(pythonPath, out string resolvedPath))
{
"/usr/local/bin",
"/usr/bin",
"/bin",
"/snap/bin",
Path.Combine(homeDir, ".local", "bin")
};

string currentPath = Environment.GetEnvironmentVariable("PATH") ?? "";
psi.EnvironmentVariables["PATH"] = string.Join(":", pathAdditions) + ":" + currentPath;

using var process = Process.Start(psi);
if (process == null) return false;
commandToRun = resolvedPath;
}

string output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit(5000);
if (!ExecPath.TryRun(commandToRun, "--version", null, out string stdout, out string stderr,
5000, augmentedPath))
return false;

if (process.ExitCode == 0 && output.StartsWith("Python "))
// Check stdout first, then stderr (some Python distributions output to stderr)
string output = !string.IsNullOrWhiteSpace(stdout) ? stdout.Trim() : stderr.Trim();
if (output.StartsWith("Python "))
{
version = output.Substring(7); // Remove "Python " prefix
fullPath = pythonPath;
version = output.Substring(7);
fullPath = commandToRun;

// Validate minimum version (Python 4+ or Python 3.10+)
if (TryParseVersion(version, out var major, out var minor))
{
return major > 3 || (major >= 3 && minor >= 10);
return major > 3 || (major == 3 && minor >= 10);
}
}
}
Expand All @@ -192,50 +175,13 @@ private bool TryValidatePython(string pythonPath, out string version, out string
return false;
}

private bool TryValidateUv(string uvPath, out string version, out string fullPath)
protected string BuildAugmentedPath()
{
version = null;
fullPath = null;
var additions = GetPathAdditions();
if (additions.Length == 0) return null;

try
{
var psi = new ProcessStartInfo
{
FileName = uvPath,
Arguments = "--version",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};

psi.EnvironmentVariables["PATH"] = BuildAugmentedPath();

using var process = Process.Start(psi);
if (process == null) return false;

string output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit(5000);

if (process.ExitCode == 0 && output.StartsWith("uv "))
{
version = output.Substring(3).Trim();
fullPath = uvPath;
return true;
}
}
catch
{
// Ignore validation errors
}

return false;
}

private string BuildAugmentedPath()
{
string currentPath = Environment.GetEnvironmentVariable("PATH") ?? "";
return string.Join(":", GetPathAdditions()) + ":" + currentPath;
// Only return the additions - ExecPath.TryRun will prepend to existing PATH
return string.Join(Path.PathSeparator, additions);
}

private string[] GetPathAdditions()
Expand All @@ -251,54 +197,10 @@ private string[] GetPathAdditions()
};
}

private bool TryFindInPath(string executable, out string fullPath)
protected override bool TryFindInPath(string executable, out string fullPath)
{
fullPath = null;

try
{
var psi = new ProcessStartInfo
{
FileName = "/usr/bin/which",
Arguments = executable,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};

// Enhance PATH for Unity's GUI environment
var homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var pathAdditions = new[]
{
"/usr/local/bin",
"/usr/bin",
"/bin",
"/snap/bin",
Path.Combine(homeDir, ".local", "bin")
};

string currentPath = Environment.GetEnvironmentVariable("PATH") ?? "";
psi.EnvironmentVariables["PATH"] = string.Join(":", pathAdditions) + ":" + currentPath;

using var process = Process.Start(psi);
if (process == null) return false;

string output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit(3000);

if (process.ExitCode == 0 && !string.IsNullOrEmpty(output) && File.Exists(output))
{
fullPath = output;
return true;
}
}
catch
{
// Ignore errors
}

return false;
fullPath = ExecPath.FindInPath(executable, BuildAugmentedPath());
return !string.IsNullOrEmpty(fullPath);
}
}
}
Loading