-
Notifications
You must be signed in to change notification settings - Fork 744
Bug fix and batch customization #727
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -158,6 +158,12 @@ private void RegisterCallbacks() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url = ResolveServerPath(url); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Update the text field if the path was auto-corrected, without re-triggering the callback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (url != evt.newValue?.Trim()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gitUrlOverride.SetValueWithoutNotify(url); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EditorPrefs.SetString(EditorPrefKeys.GitUrlOverride, url); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OnGitUrlChanged?.Invoke(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -326,9 +332,10 @@ private void OnClearUvxClicked() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void OnBrowseGitUrlClicked() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string picked = EditorUtility.OpenFolderPanel("Select Server folder", string.Empty, string.Empty); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string picked = EditorUtility.OpenFolderPanel("Select Server folder (containing pyproject.toml)", string.Empty, string.Empty); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!string.IsNullOrEmpty(picked)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| picked = ResolveServerPath(picked); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gitUrlOverride.value = picked; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EditorPrefs.SetString(EditorPrefKeys.GitUrlOverride, picked); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OnGitUrlChanged?.Invoke(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -337,6 +344,54 @@ private void OnBrowseGitUrlClicked() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Validates and auto-corrects a local server path to ensure it points to the directory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// containing pyproject.toml (the Python package root). If the user selects a parent | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// directory (e.g. the repo root), this checks for a "Server" subdirectory with | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// pyproject.toml and returns that instead. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static string ResolveServerPath(string path) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (string.IsNullOrEmpty(path)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return path; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If path is not a local filesystem path, return as-is (git URLs, PyPI refs, etc.) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+353
to
+362
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Path resolution logic is duplicated between ResolveServerPath and ResolveLocalServerPath and may drift over time. These two methods share most of the same concerns (local vs URL detection, Suggested implementation: /// <summary>
/// Shared normalization logic for resolving server paths (both local and remote).
/// Handles scheme detection, `file://` URIs, and normalization of local filesystem
/// paths before any call-site specific heuristics are applied.
/// </summary>
/// <param name="path">Raw user-provided path or URL.</param>
/// <param name="normalizedPath">
/// Normalized local filesystem path (if applicable). When this returns false,
/// this will be null and the original <paramref name="path" /> should be used.
/// </param>
/// <returns>
/// True when <paramref name="path" /> represents a local filesystem location that
/// should be further resolved (e.g. probing for a "Server" subdirectory); false
/// when the caller should treat the original string as-is (URLs, PyPI-style refs, etc.).
/// </returns>
private static bool TryNormalizeLocalServerPath(string path, out string normalizedPath)
{
normalizedPath = null;
if (string.IsNullOrEmpty(path))
return false;
// Non-local sources (git URLs, PyPI refs, etc.) are returned as-is at call sites.
if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) ||
path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Support `file://` style URIs, which can come from Unity file pickers on some platforms.
if (path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
if (Uri.TryCreate(path, UriKind.Absolute, out var uri) && uri.IsFile)
normalizedPath = uri.LocalPath;
else
normalizedPath = path;
return true;
}
// For plain paths, just normalize separators; call sites can perform additional
// probing (e.g. for "Server" subdirectories or pyproject.toml detection).
normalizedPath = path.Replace('\\', Path.DirectorySeparatorChar)
.Replace('/', Path.DirectorySeparatorChar);
return true;
}
/// <summary>
/// Validates and auto-corrects a local server path to ensure it points to the directory
/// containing pyproject.toml (the Python package root). If the user selects a parent
/// directory (e.g. the repo root), this checks for a "Server" subdirectory with
/// pyproject.toml and returns that instead.
/// </summary>
private static string ResolveServerPath(string path)To complete the refactor and fully address the duplication / drift risk, you should:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return path; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Strip file:// prefix for filesystem checks, but preserve it for the return value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string checkPath = path; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string prefix = string.Empty; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (checkPath.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prefix = "file://"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| checkPath = checkPath.Substring(7); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Already points to a directory with pyproject.toml — correct path | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (File.Exists(Path.Combine(checkPath, "pyproject.toml"))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return path; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check if "Server" subdirectory contains pyproject.toml (common repo structure) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string serverSubDir = Path.Combine(checkPath, "Server"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (File.Exists(Path.Combine(serverSubDir, "pyproject.toml"))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string corrected = prefix + serverSubDir; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| McpLog.Info($"Auto-corrected server path to 'Server' subdirectory: {corrected}"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return corrected; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Return as-is; uvx will report the error if the path is invalid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return path; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+355
to
+392
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (string.IsNullOrEmpty(path)) | |
| return path; | |
| // If path is not a local filesystem path, return as-is (git URLs, PyPI refs, etc.) | |
| if (path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || | |
| path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) || | |
| path.StartsWith("git+", StringComparison.OrdinalIgnoreCase) || | |
| path.StartsWith("ssh://", StringComparison.OrdinalIgnoreCase)) | |
| { | |
| return path; | |
| } | |
| // Strip file:// prefix for filesystem checks, but preserve it for the return value | |
| string checkPath = path; | |
| string prefix = string.Empty; | |
| if (checkPath.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) | |
| { | |
| prefix = "file://"; | |
| checkPath = checkPath.Substring(7); | |
| } | |
| // Already points to a directory with pyproject.toml — correct path | |
| if (File.Exists(Path.Combine(checkPath, "pyproject.toml"))) | |
| { | |
| return path; | |
| } | |
| // Check if "Server" subdirectory contains pyproject.toml (common repo structure) | |
| string serverSubDir = Path.Combine(checkPath, "Server"); | |
| if (File.Exists(Path.Combine(serverSubDir, "pyproject.toml"))) | |
| { | |
| string corrected = prefix + serverSubDir; | |
| McpLog.Info($"Auto-corrected server path to 'Server' subdirectory: {corrected}"); | |
| return corrected; | |
| } | |
| // Return as-is; uvx will report the error if the path is invalid | |
| return path; | |
| // Delegate to shared helper to keep path resolution logic centralized | |
| return AssetPathUtility.ResolveLocalServerPath(path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Relative local paths without separators are treated as PyPI references and skipped.
This logic treats any string without
/,\, orfile:as a PyPI reference, so relative directories likeServerormy_packagewon’t be auto-corrected even if they contain a validpyproject.toml. If users are likely to enter such relative paths, consider tightening the heuristic (e.g. require a more PyPI-specific pattern or explicitly distinguish absolute vs relative paths) so these local directories can still be handled.