Skip to content

Conversation

@hvesuk
Copy link

@hvesuk hvesuk commented Jan 19, 2026

Description

This PR introduces automatic installation for the uv package manager and enhances the overall dependency management experience. Previously, users had to manually install uv via external terminals, which often led to "command not found" errors during the first run.

Key Changes

🛠 Automatic Installation

Added InstallUv() support across all platforms:

  • Windows: Automated installation via PowerShell
  • macOS/Linux: Automated installation via curl

🖥 Setup Window Improvements

  • Added an "Install UV Automatically" button to the MCP Setup window
  • Updated MCPSetupWindow.uss to ensure consistent button sizing and better layout alignment (vertical column layout for install links)
  • Buttons remain visible during the setup phase to provide users with all necessary installation options

🚀 Smart Startup Flow

  • ServerManagementService now proactively checks for uv before launching the local server
  • If missing, it prompts the user with a dialog to install it automatically without leaving Unity

🔍 Path Resolution Fix

  • Fixed a bug in PathResolverService where it would return "uvx" instead of null when the binary was not found
  • This change ensures the system correctly detects the "Not Found" state, triggering the automated installation flow instead of failing with a shell error

Benefits

  • Zero-Config Experience: Reduces friction for new users by handling dependencies internally
  • Error Prevention: Fixes the common 'uvx' is not recognized shell error
  • Unified UI: Provides a more polished and professional setup experience within the Unity Editor

Summary by Sourcery

Add automatic uv installation support and integrate dependency checks into the local server startup and setup UI to streamline MCP configuration.

New Features:

  • Add automatic uv installation on Windows via PowerShell and on macOS/Linux via curl through platform-specific detectors.
  • Add an "Install uv automatically" action in the MCP Setup window to trigger the installer from within Unity.
  • Prompt users to install uv automatically when starting the local HTTP server if uv is not detected.

Bug Fixes:

  • Fix PathResolverService to return null instead of a fallback command when the uvx binary is not found, allowing proper handling of missing uv.

Enhancements:

  • Extend the platform detector abstraction to support programmatic uv installation and expose an installation URL for error messaging.
  • Improve MCP setup UI layout and button set to keep installation options visible and provide clearer feedback on uv installation success or incomplete detection.

Summary by CodeRabbit

  • New Features

    • Automatic UV package manager installation now supported on Linux, macOS, and Windows platforms.
    • Added one-click auto-install button in setup window for UV dependency.
    • Automatic installation check and attempt when starting local HTTP server if UV is unavailable.
  • Improvements

    • Redesigned setup window layout for better readability of installation links.

✏️ Tip: You can customize this high-level summary in your review settings.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 19, 2026

Reviewer's Guide

Implements cross-platform automatic installation of the uv package manager, wires it into both the MCP setup window and local server startup flow, and fixes uv path resolution so missing binaries are correctly detected and can trigger the new install path.

Sequence diagram for automatic uv installation during local server startup

sequenceDiagram
    actor User
    participant UnityEditor
    participant ServerManagementService
    participant DependencyManager
    participant IPlatformDetector as PlatformDetector

    User->>UnityEditor: Click Start Local HTTP Server
    UnityEditor->>ServerManagementService: StartLocalHttpServer()

    ServerManagementService->>DependencyManager: GetCurrentPlatformDetector()
    DependencyManager-->>ServerManagementService: PlatformDetector

    ServerManagementService->>PlatformDetector: DetectUv()
    PlatformDetector-->>ServerManagementService: uvStatus(IsAvailable = false)

    ServerManagementService->>UnityEditor: EditorUtility.DisplayDialog("uv Package Manager Missing")
    alt User selects Install uv
        ServerManagementService->>PlatformDetector: InstallUv()
        PlatformDetector-->>ServerManagementService: success or failure
        alt InstallUv success
            ServerManagementService->>PlatformDetector: DetectUv()
            PlatformDetector-->>ServerManagementService: uvStatus(IsAvailable)
            alt uv still not detected
                ServerManagementService->>UnityEditor: EditorUtility.DisplayDialog("Installation Incomplete")
                ServerManagementService-->>UnityEditor: return false
            else uv detected
                ServerManagementService->>ServerManagementService: Continue startup flow
                ServerManagementService-->>UnityEditor: Start server (success path)
            end
        else InstallUv failed
            ServerManagementService->>PlatformDetector: GetUvInstallUrl()
            PlatformDetector-->>ServerManagementService: uvInstallUrl
            ServerManagementService->>UnityEditor: EditorUtility.DisplayDialog("Installation Failed", uvInstallUrl)
            ServerManagementService-->>UnityEditor: return false
        end
    else User cancels
        ServerManagementService-->>UnityEditor: return false
    end
Loading

Updated class diagram for platform detectors and dependency setup flow

classDiagram
    class IPlatformDetector {
        <<interface>>
        +string GetPythonInstallUrl()
        +string GetUvInstallUrl()
        +string GetInstallationRecommendations()
        +bool InstallUv()
        +DependencyStatus DetectPython()
        +DependencyStatus DetectUv()
    }

    class PlatformDetectorBase {
        <<abstract>>
        +string GetPythonInstallUrl()
        +string GetUvInstallUrl()
        +string GetInstallationRecommendations()
        +bool InstallUv()
        +DependencyStatus DetectPython()
        +DependencyStatus DetectUv()
    }

    class WindowsPlatformDetector {
        +string GetPythonInstallUrl()
        +string GetUvInstallUrl()
        +string GetInstallationRecommendations()
        +bool InstallUv()
        +DependencyStatus DetectPython()
        +DependencyStatus DetectUv()
    }

    class MacOSPlatformDetector {
        +string GetPythonInstallUrl()
        +string GetUvInstallUrl()
        +string GetInstallationRecommendations()
        +bool InstallUv()
        +DependencyStatus DetectPython()
        +DependencyStatus DetectUv()
    }

    class LinuxPlatformDetector {
        +string GetPythonInstallUrl()
        +string GetUvInstallUrl()
        +string GetInstallationRecommendations()
        +bool InstallUv()
        +DependencyStatus DetectPython()
        +DependencyStatus DetectUv()
    }

    class DependencyManager {
        +IPlatformDetector GetCurrentPlatformDetector()
        +DependencyCheckResult CheckAllDependencies()
    }

    class DependencyStatus {
        +string Name
        +bool IsRequired
        +bool IsAvailable
    }

    class DependencyCheckResult {
        +List~DependencyStatus~ Dependencies
    }

    class MCPSetupWindow {
        -Label installationInstructions
        -Button openPythonLinkButton
        -Button openUvLinkButton
        -Button autoInstallUvButton
        -Button refreshButton
        -Button doneButton
        -DependencyCheckResult _dependencyResult
        +void CreateGUI()
        +void OnOpenPythonInstallClicked()
        +void OnOpenUvInstallClicked()
        +void OnAutoInstallUvClicked()
        +void UpdateUI()
    }

    class ServerManagementService {
        +bool StartLocalHttpServer()
        +bool TryGetLocalHttpServerCommandParts()
    }

    class PathResolverService {
        +string GetUvxPath()
    }

    IPlatformDetector <|-- PlatformDetectorBase
    PlatformDetectorBase <|-- WindowsPlatformDetector
    PlatformDetectorBase <|-- MacOSPlatformDetector
    PlatformDetectorBase <|-- LinuxPlatformDetector

    DependencyManager --> IPlatformDetector : uses
    DependencyManager --> DependencyCheckResult : returns
    DependencyCheckResult --> DependencyStatus : contains

    MCPSetupWindow --> DependencyManager : uses
    MCPSetupWindow --> DependencyCheckResult : holds

    ServerManagementService --> DependencyManager : uses
    ServerManagementService --> PathResolverService : uses
Loading

Flow diagram for uv detection and installation logic

flowchart TD
    A[Start local server or click Install UV Automatically] --> B[GetCurrentPlatformDetector]
    B --> C[DetectUv]
    C --> D{uv available?}

    D -->|Yes| E[Proceed with normal flow]

    D -->|No| F{User chooses automatic install?}
    F -->|No| G[Abort operation]

    F -->|Yes| H[PlatformDetector.InstallUv]
    H --> I{Install succeeded?}

    I -->|No| J[Show Installation Failed dialog]
    J --> G

    I -->|Yes| K[Re run DetectUv]
    K --> L{uv now detected?}

    L -->|Yes| E
    L -->|No| M[Show Installation Incomplete dialog]
    M --> G
Loading

File-Level Changes

Change Details Files
Add automatic uv installation capability to platform detectors and expose it via the dependency abstraction.
  • Extend IPlatformDetector with an InstallUv() method and provide a default no-op implementation in PlatformDetectorBase.
  • Implement InstallUv() on Windows to run the official uv PowerShell installer with -NoProfile and ByPass execution policy, waiting for completion and logging failures.
  • Implement InstallUv() on macOS and Linux to execute the official uv shell install script via /bin/sh with curl piping, waiting for completion and logging failures.
MCPForUnity/Editor/Dependencies/PlatformDetectors/IPlatformDetector.cs
MCPForUnity/Editor/Dependencies/PlatformDetectors/PlatformDetectorBase.cs
MCPForUnity/Editor/Dependencies/PlatformDetectors/WindowsPlatformDetector.cs
MCPForUnity/Editor/Dependencies/PlatformDetectors/MacOSPlatformDetector.cs
MCPForUnity/Editor/Dependencies/PlatformDetectors/LinuxPlatformDetector.cs
Integrate uv presence checking and auto-install prompts into local server startup and setup UI.
  • Inject dependency detection into ServerManagementService.StartLocalHttpServer() to verify uv availability before starting, prompting the user to auto-install or cancel when missing, and handling post-install re-detection and error dialogs.
  • Add an "Install UV Automatically" button to MCPSetupWindow, wiring it to call the current platform detector’s InstallUv(), refresh dependency state, and show success/incomplete/failure dialogs based on detection results.
  • Ensure setup window keeps buttons visible during the setup phase so manual and automatic uv installation options are available without closing the window.
MCPForUnity/Editor/Services/ServerManagementService.cs
MCPForUnity/Editor/Windows/MCPSetupWindow.cs
MCPForUnity/Editor/Windows/MCPSetupWindow.uxml
MCPForUnity/Editor/Windows/MCPSetupWindow.uss
Fix uv path resolution to correctly signal missing binaries and avoid misleading shell errors.
  • Change PathResolverService.GetUvxPath() to return null instead of a bare "uvx" command when the executable cannot be found in standard locations, allowing dependency checks to treat uv as unavailable rather than assuming a valid command.
MCPForUnity/Editor/Services/PathResolverService.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 19, 2026

📝 Walkthrough

Walkthrough

This PR adds automatic uv package manager installation capability across all platforms by introducing an InstallUv() method to the platform detector interface, implementing it for Linux, macOS, and Windows, and integrating installation flows into server management and setup UI. It also adjusts the PathResolverService fallback behavior to return null instead of a bare command string.

Changes

Cohort / File(s) Summary
Platform Detector Interface
MCPForUnity/Editor/Dependencies/PlatformDetectors/IPlatformDetector.cs
Adds bool InstallUv() method definition to the interface contract.
Platform Detector Base & Implementations
MCPForUnity/Editor/Dependencies/PlatformDetectors/PlatformDetectorBase.cs, LinuxPlatformDetector.cs, MacOSPlatformDetector.cs, WindowsPlatformDetector.cs
Implements InstallUv() across all platform detectors: base provides default false return, Linux/macOS use curl-piped shell execution, Windows uses PowerShell with -NoProfile flag. All wait up to 60 seconds and return success/failure based on exit code.
Service Layer
MCPForUnity/Editor/Services/PathResolverService.cs
Changes GetUvxPath final fallback from returning literal "uvx" command to returning null.
Server Management
MCPForUnity/Editor/Services/ServerManagementService.cs
Adds pre-flight check in StartLocalHttpServer: verifies uv availability, prompts user if missing, attempts platform-specific installation, rechecks, and shows appropriate success/warning/failure dialogs.
Setup Window UI
MCPForUnity/Editor/Windows/MCPSetupWindow.cs, MCPSetupWindow.uxml, MCPSetupWindow.uss
Adds "Install UV Automatically" button with handler that invokes InstallUv(), rechecks dependencies, and updates UI. Styling changes convert horizontal install-link layout to vertical stack with full-width buttons.

Sequence Diagram

sequenceDiagram
    participant User
    participant MCPSetupWindow
    participant DependencyManager
    participant PlatformDetector
    participant Shell

    User->>MCPSetupWindow: Click "Install UV Automatically"
    MCPSetupWindow->>DependencyManager: GetCurrentPlatformDetector()
    DependencyManager-->>MCPSetupWindow: PlatformDetector instance
    MCPSetupWindow->>PlatformDetector: InstallUv()
    PlatformDetector->>Shell: Execute platform-specific install command
    Shell-->>PlatformDetector: Exit code (0=success)
    PlatformDetector-->>MCPSetupWindow: bool (true/false)
    MCPSetupWindow->>MCPSetupWindow: CheckAllDependencies()
    alt Installation Success
        MCPSetupWindow->>User: Show success dialog
    else Incomplete Installation
        MCPSetupWindow->>User: Warn restart Unity needed
    else Installation Failed
        MCPSetupWindow->>User: Show failure dialog with install URL
    end
    MCPSetupWindow->>MCPSetupWindow: UpdateUI()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • dsarno

Poem

🐰 Hopping through platforms with vim and with cheer,
A button to install, making uv appear!
Linux and Mac and Windows embrace,
Auto-install flows put smiles on your face!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately describes the main objective of the PR: adding automatic uv installation support and improving the dependency setup flow in the Unity Editor.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 5 issues, and left some high level feedback:

  • The InstallUv implementations on Windows/macOS/Linux synchronously wait up to 60 seconds on an external process, which will block the Unity editor UI; consider running these installs asynchronously (e.g., with a progress dialog) or at least making the timeout configurable and surfacing progress to the user.
  • The curl-based InstallUv logic for macOS and Linux is duplicated; you could extract a shared helper in PlatformDetectorBase (or a small utility) to avoid repeating the /bin/sh + curl command and error handling.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The InstallUv implementations on Windows/macOS/Linux synchronously wait up to 60 seconds on an external process, which will block the Unity editor UI; consider running these installs asynchronously (e.g., with a progress dialog) or at least making the timeout configurable and surfacing progress to the user.
- The curl-based InstallUv logic for macOS and Linux is duplicated; you could extract a shared helper in PlatformDetectorBase (or a small utility) to avoid repeating the /bin/sh + curl command and error handling.

## Individual Comments

### Comment 1
<location> `MCPForUnity/Editor/Dependencies/PlatformDetectors/WindowsPlatformDetector.cs:103-112` </location>
<code_context>
 Note: Make sure ~/.local/bin is in your PATH for user-local installations.";
         }

+        public override bool InstallUv()
+        {
+            try
+            {
+                McpLog.Info("Attempting to install uv package manager via curl...");
+
+                var psi = new System.Diagnostics.ProcessStartInfo
+                {
+                    FileName = "/bin/sh",
+                    Arguments = "-c \"curl -LsSf https://astral.sh/uv/install.sh | sh\"",
+                    UseShellExecute = false,
+                    CreateNoWindow = false
+                };
+
+                using var process = System.Diagnostics.Process.Start(psi);
+                if (process == null) return false;
+
+                process.WaitForExit(60000);
+
+                return process.ExitCode == 0;
+            }
+            catch (Exception ex)
</code_context>

<issue_to_address>
**issue (bug_risk):** Handle long-running installs and potential timeout behavior more robustly.

`process.WaitForExit(60000)` can leave the process running and cause `process.ExitCode` to throw if the install takes longer than 60s. It also risks flaky failures on slower machines.

Please check the return value of `WaitForExit` and handle timeouts explicitly (e.g., log and return failure), and consider either a longer or configurable timeout (or no timeout with appropriate UI feedback) to avoid premature failures.
</issue_to_address>

### Comment 2
<location> `MCPForUnity/Editor/Dependencies/PlatformDetectors/LinuxPlatformDetector.cs:95-104` </location>
<code_context>
 Note: Make sure ~/.local/bin is in your PATH for user-local installations.";
         }

+        public override bool InstallUv()
+        {
+            try
+            {
+                McpLog.Info("Attempting to install uv package manager via curl...");
+
+                var psi = new System.Diagnostics.ProcessStartInfo
+                {
+                    FileName = "/bin/sh",
+                    Arguments = "-c \"curl -LsSf https://astral.sh/uv/install.sh | sh\"",
+                    UseShellExecute = false,
+                    CreateNoWindow = false
+                };
+
+                using var process = System.Diagnostics.Process.Start(psi);
+                if (process == null) return false;
+
+                process.WaitForExit(60000);
+
+                return process.ExitCode == 0;
+            }
+            catch (Exception ex)
</code_context>

<issue_to_address>
**issue (bug_risk):** Avoid blocking indefinitely on the installer and hard-coding a short timeout.

`WaitForExit(60000)` has the same issues as on Windows:
- A slow download/install can exceed 60s, causing a timeout and possible `InvalidOperationException` when reading `ExitCode`.
- 60s may be too short for users on slow networks.

Please check the `WaitForExit` return value and handle timeouts explicitly (e.g., log and return `false`), and consider a longer/parameterized timeout or waiting without a timeout while showing progress. The same logic applies to macOS, so a shared helper would help keep behavior consistent across platforms.
</issue_to_address>

### Comment 3
<location> `MCPForUnity/Editor/Windows/MCPSetupWindow.cs:119-117` </location>
<code_context>
             Application.OpenURL(uvUrl);
         }

+        private void OnAutoInstallUvClicked()
+        {
+            var detector = DependencyManager.GetCurrentPlatformDetector();
+            if (detector.InstallUv())
+            {
+                _dependencyResult = DependencyManager.CheckAllDependencies();
+                UpdateUI();
+
+                var uvDep = _dependencyResult.Dependencies.Find(d => d.Name == "uv Package Manager");
+                if (uvDep != null && !uvDep.IsAvailable)
+                {
+                    EditorUtility.DisplayDialog(
+                        "Installation Incomplete",
+                        "uv was installed, but it is not yet detected in the current process PATH. " +
+                        "You may need to restart Unity for the changes to take effect.",
+                        "OK");
+                }
+                else
+                {
</code_context>

<issue_to_address>
**suggestion (performance):** Running the installer synchronously on the UI thread may freeze the Unity editor for up to a minute.

`OnAutoInstallUvClicked` calls `detector.InstallUv()` synchronously on the main editor thread, and platform implementations can block for up to 60 seconds, making the editor appear hung.

Please move the installation to a background task and surface progress via `EditorUtility.DisplayProgressBar` or a similar mechanism so the UI stays responsive. If full async handling isn’t feasible, at least show a clear, modal progress dialog indicating that a long-running operation is in progress to discourage users from force-quitting the editor.

Suggested implementation:

```csharp
        // Run uv installation off the main thread and surface progress so the editor stays responsive.
        private async void OnAutoInstallUvClicked()
        {
            var detector = DependencyManager.GetCurrentPlatformDetector();

            try
            {
                // Show a modal progress bar so it's clear a long-running operation is in progress.
                EditorUtility.DisplayProgressBar(
                    "Installing uv",
                    "Installing uv Package Manager. This may take up to a minute...",
                    0.5f);

                // Run the installer on a background thread to avoid freezing the editor UI.
                var installSucceeded = await System.Threading.Tasks.Task.Run(() => detector.InstallUv());

                // Clear the progress bar as soon as the background work completes.
                EditorUtility.ClearProgressBar();

                if (installSucceeded)
                {
                    _dependencyResult = DependencyManager.CheckAllDependencies();
                    UpdateUI();

                    var uvDep = _dependencyResult.Dependencies.Find(d => d.Name == "uv Package Manager");
                    if (uvDep != null && !uvDep.IsAvailable)
                    {
                        EditorUtility.DisplayDialog(

```

1. The SEARCH block above only includes the beginning of the original `OnAutoInstallUvClicked` method because the full method body was not visible in the snippet you provided. You should expand the SEARCH range to cover the *entire* original `OnAutoInstallUvClicked` method body (from the `private void OnAutoInstallUvClicked()` line down to its closing `}`) so it is completely replaced by the new async implementation.
2. The new method assumes the rest of the original logic after `EditorUtility.DisplayDialog(` remains the same (e.g., the "Installation Incomplete" dialog text and any `else` branch for the successful case). Ensure that the closing `)` / `;`, the `else` block (if any), and the methods closing brace `}` are all present after the inserted `EditorUtility.DisplayDialog(` line.
3. If `System.Threading.Tasks` is not already imported at the top of `MCPForUnity/Editor/Windows/MCPSetupWindow.cs`, add:
   ```csharp
   using System.Threading.Tasks;
   ```
   This allows you to simplify the call to `Task.Run` (you can then replace `System.Threading.Tasks.Task.Run` with `Task.Run` if you prefer).
4. Make sure your project is using the .NET 4.x scripting runtime and API compatibility level so that `async`/`await` in editor code works correctly. If your codebase prefers not to use `async void`, you can instead enqueue completion logic back to the main thread with `EditorApplication.delayCall`, but that will require adjusting the method signature and event hookup accordingly.
</issue_to_address>

### Comment 4
<location> `MCPForUnity/Editor/Services/ServerManagementService.cs:415-424` </location>
<code_context>

+        private void OnAutoInstallUvClicked()
+        {
+            var detector = DependencyManager.GetCurrentPlatformDetector();
+            if (detector.InstallUv())
+            {
+                _dependencyResult = DependencyManager.CheckAllDependencies();
+                UpdateUI();
+
+                var uvDep = _dependencyResult.Dependencies.Find(d => d.Name == "uv Package Manager");
+                if (uvDep != null && !uvDep.IsAvailable)
+                {
+                    EditorUtility.DisplayDialog(
+                        "Installation Incomplete",
+                        "uv was installed, but it is not yet detected in the current process PATH. " +
+                        "You may need to restart Unity for the changes to take effect.",
+                        "OK");
+                }
+                else
+                {
</code_context>

<issue_to_address>
**suggestion (performance):** Avoid blocking server startup on a potentially long-running interactive installer without clear feedback.

`detector.InstallUv()` can block for up to 60 seconds as part of starting the local HTTP server, which will freeze the editor with no in-editor indication of progress.

Consider:
- Showing an editor progress dialog during installation so users understand the delay.
- Separating the automatic install path from ajust start serverpath, so users who know uv is missing arent forced into a long blocking operation when starting the server.

Suggested implementation:

```csharp
            // Check if uv is installed, and offer to install if missing.
            var detector = DependencyManager.GetCurrentPlatformDetector();
            var uvStatus = detector.DetectUv();
            if (!uvStatus.IsAvailable)
            {
                if (EditorUtility.DisplayDialog(
                    "uv Package Manager Missing",
                    "The 'uv' package manager is required to run the local MCP server, but it was not found in your PATH.\n\n" +
                    "An automatic installation can be attempted now. This may take up to 60 seconds while a local HTTP server is started.\n\n" +
                    "Alternatively, you can start the server without uv (if you plan to install it manually or are just inspecting the project).",
                    "Install uv (may take up to 60 seconds)",
                    "Start without uv"))
                {
                    EditorUtility.DisplayProgressBar(
                        "Installing uv",
                        "Installing the 'uv' package manager. This may take up to 60 seconds...",
                        0.5f);

                    try
                    {
                        if (detector.InstallUv())
                        {
                            // Re-check after installation
                        }
                    }
                    finally
                    {
                        EditorUtility.ClearProgressBar();
                    }

```

The current control flow already effectively separates theinstall uvandstart without uvpaths: declining the dialog skips `InstallUv()` and continues with the rest of the method. If later code assumes uv is present, you may want to:
1. Explicitly handle thestart without uvcase (e.g., by skipping server startup or showing a more targeted warning when uv is still missing).
2. Optionally log or surface a non-blocking notification if the user chooses to start without uv, so they understand any subsequent failures are due to uv being absent.
</issue_to_address>

### Comment 5
<location> `MCPForUnity/Editor/Dependencies/PlatformDetectors/MacOSPlatformDetector.cs:93-102` </location>
<code_context>
 Note: Make sure ~/.local/bin is in your PATH for user-local installations.";
         }

+        public override bool InstallUv()
+        {
+            try
+            {
+                McpLog.Info("Attempting to install uv package manager via curl...");
+
+                var psi = new System.Diagnostics.ProcessStartInfo
+                {
+                    FileName = "/bin/sh",
+                    Arguments = "-c \"curl -LsSf https://astral.sh/uv/install.sh | sh\"",
+                    UseShellExecute = false,
+                    CreateNoWindow = false
+                };
+
+                using var process = System.Diagnostics.Process.Start(psi);
+                if (process == null) return false;
+
+                process.WaitForExit(60000);
+
+                return process.ExitCode == 0;
+            }
+            catch (Exception ex)
</code_context>

<issue_to_address>
**🚨 suggestion (security):** Consider the security implications of piping remote installer scripts directly into a shell.

Both Linux and macOS `InstallUv` use `curl -LsSf https://astral.sh/uv/install.sh | sh`, which executes remote content without independent verification or an explicit confirmation step in the editor.

Given this runs inside the editor, consider:
- Logging the exact command and URL before execution.
- Downloading to a temporary file and optionally prompting the user to inspect/confirm before running it.
- Providing a setting to disable this automatic installer for securitysensitive environments.

Suggested implementation:

```csharp
        public override bool InstallUv()
        {
            try
            {
                const string installScriptUrl = "https://astral.sh/uv/install.sh";

                // NOTE: This method intentionally logs the exact command and URL used so that
                // users and security‑sensitive environments can audit what is being executed.
                McpLog.Info($"Attempting to install uv package manager from '{installScriptUrl}'...");

                // In security‑sensitive environments this automatic installer should be
                // disabled via settings; see <additional_changes> for details.
                // if (McpSettings.Current.DisableAutomaticInstallers)
                // {
                //     McpLog.Info("Automatic uv installation is disabled by settings.");
                //     return false;
                // }

                // Optional confirmation prompt (requires UnityEditor):
                // if (!UnityEditor.EditorUtility.DisplayDialog(
                //         "Install uv",
                //         $"The editor will download and execute the installer script from:\n\n{installScriptUrl}\n\nDo you want to continue?",
                //         "Install",
                //         "Cancel"))
                // {
                //     McpLog.Info("User cancelled uv installation.");
                //     return false;
                // }

                // Download the installer script to a temporary file instead of piping directly to sh.
                var tempScriptPath = System.IO.Path.Combine(
                    System.IO.Path.GetTempPath(),
                    $"uv-install-{Guid.NewGuid():N}.sh");

                var downloadArguments =
                    $"-c \"curl -LsSf {installScriptUrl} -o '{tempScriptPath}'\"";

                McpLog.Info($"Downloading uv installer script with: /bin/sh {downloadArguments}");

                var downloadPsi = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = "/bin/sh",
                    Arguments = downloadArguments,
                    UseShellExecute = false,
                    CreateNoWindow = false
                };

                using (var downloadProcess = System.Diagnostics.Process.Start(downloadPsi))
                {
                    if (downloadProcess == null)
                    {
                        McpLog.Error("Failed to start curl process to download uv installer.");
                        return false;
                    }

                    downloadProcess.WaitForExit(60000);
                    if (downloadProcess.ExitCode != 0 || !System.IO.File.Exists(tempScriptPath))
                    {
                        McpLog.Error($"curl failed to download uv installer (exit code {downloadProcess.ExitCode}).");
                        return false;
                    }
                }

                // Now execute the downloaded script.
                var executeArguments = $"-c \"sh '{tempScriptPath}'\"";
                McpLog.Info($"Executing uv installer script with: /bin/sh {executeArguments}");

                var executePsi = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = "/bin/sh",
                    Arguments = executeArguments,
                    UseShellExecute = false,
                    CreateNoWindow = false
                };

                using (var executeProcess = System.Diagnostics.Process.Start(executePsi))
                {
                    if (executeProcess == null)
                    {
                        McpLog.Error("Failed to start process to execute uv installer script.");
                        return false;
                    }

                    executeProcess.WaitForExit(600000); // allow more time for installation
                    if (executeProcess.ExitCode != 0)
                    {
                        McpLog.Error($"uv installer script exited with code {executeProcess.ExitCode}.");
                        return false;
                    }
                }

                // Best effort clean‑up of the temporary script
                try
                {
                    if (System.IO.File.Exists(tempScriptPath))
                    {
                        System.IO.File.Delete(tempScriptPath);
                    }
                }
                catch (Exception cleanupEx)
                {
                    McpLog.Warn($"Failed to delete temporary uv installer script '{tempScriptPath}': {cleanupEx.Message}");
                }

                McpLog.Info("uv package manager installed successfully.");
                return true;
            }
            catch (Exception ex)
            {
                McpLog.Error($"Failed to install uv: {ex.Message}");
                return false;
            }
        }

```

` note since the settings system isnt visible here).

Here are the concrete edits:

<file_operations>
<file_operation operation="edit" file_path="MCPForUnity/Editor/Dependencies/PlatformDetectors/MacOSPlatformDetector.cs">
<<<<<<< SEARCH
        public override bool InstallUv()
        {
            try
            {
                McpLog.Info("Attempting to install uv package manager via curl...");

                var psi = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = "/bin/sh",
                    Arguments = "-c \"curl -LsSf https://astral.sh/uv/install.sh | sh\"",
                    UseShellExecute = false,
                    CreateNoWindow = false
                };

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

                process.WaitForExit(60000);

                return process.ExitCode == 0;
            }
            catch (Exception ex)
            {
                McpLog.Error($"Failed to install uv: {ex.Message}");
                return false;
            }
        }
=======
        public override bool InstallUv()
        {
            try
            {
                const string installScriptUrl = "https://astral.sh/uv/install.sh";

                // NOTE: This method intentionally logs the exact command and URL used so that
                // users and security‑sensitive environments can audit what is being executed.
                McpLog.Info($"Attempting to install uv package manager from '{installScriptUrl}'...");

                // In security‑sensitive environments this automatic installer should be
                // disabled via settings; see <additional_changes> for details.
                // if (McpSettings.Current.DisableAutomaticInstallers)
                // {
                //     McpLog.Info("Automatic uv installation is disabled by settings.");
                //     return false;
                // }

                // Optional confirmation prompt (requires UnityEditor):
                // if (!UnityEditor.EditorUtility.DisplayDialog(
                //         "Install uv",
                //         $"The editor will download and execute the installer script from:\n\n{installScriptUrl}\n\nDo you want to continue?",
                //         "Install",
                //         "Cancel"))
                // {
                //     McpLog.Info("User cancelled uv installation.");
                //     return false;
                // }

                // Download the installer script to a temporary file instead of piping directly to sh.
                var tempScriptPath = System.IO.Path.Combine(
                    System.IO.Path.GetTempPath(),
                    $"uv-install-{Guid.NewGuid():N}.sh");

                var downloadArguments =
                    $"-c \"curl -LsSf {installScriptUrl} -o '{tempScriptPath}'\"";

                McpLog.Info($"Downloading uv installer script with: /bin/sh {downloadArguments}");

                var downloadPsi = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = "/bin/sh",
                    Arguments = downloadArguments,
                    UseShellExecute = false,
                    CreateNoWindow = false
                };

                using (var downloadProcess = System.Diagnostics.Process.Start(downloadPsi))
                {
                    if (downloadProcess == null)
                    {
                        McpLog.Error("Failed to start curl process to download uv installer.");
                        return false;
                    }

                    downloadProcess.WaitForExit(60000);
                    if (downloadProcess.ExitCode != 0 || !System.IO.File.Exists(tempScriptPath))
                    {
                        McpLog.Error($"curl failed to download uv installer (exit code {downloadProcess.ExitCode}).");
                        return false;
                    }
                }

                // Now execute the downloaded script.
                var executeArguments = $"-c \"sh '{tempScriptPath}'\"";
                McpLog.Info($"Executing uv installer script with: /bin/sh {executeArguments}");

                var executePsi = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = "/bin/sh",
                    Arguments = executeArguments,
                    UseShellExecute = false,
                    CreateNoWindow = false
                };

                using (var executeProcess = System.Diagnostics.Process.Start(executePsi))
                {
                    if (executeProcess == null)
                    {
                        McpLog.Error("Failed to start process to execute uv installer script.");
                        return false;
                    }

                    executeProcess.WaitForExit(600000); // allow more time for installation
                    if (executeProcess.ExitCode != 0)
                    {
                        McpLog.Error($"uv installer script exited with code {executeProcess.ExitCode}.");
                        return false;
                    }
                }

                // Best effort clean‑up of the temporary script
                try
                {
                    if (System.IO.File.Exists(tempScriptPath))
                    {
                        System.IO.File.Delete(tempScriptPath);
                    }
                }
                catch (Exception cleanupEx)
                {
                    McpLog.Warn($"Failed to delete temporary uv installer script '{tempScriptPath}': {cleanupEx.Message}");
                }

                McpLog.Info("uv package manager installed successfully.");
                return true;
            }
            catch (Exception ex)
            {
                McpLog.Error($"Failed to install uv: {ex.Message}");
                return false;
            }
        }
>>>>>>> REPLACE
</file_operation>
</file_operations>

<additional_changes>
To fully implement the review suggestions, you may also want to:

1. **Add a setting to disable automatic installers** (e.g. in your existing settings/config system):
   - Introduce a flag such as `bool DisableAutomaticInstallers` (or a more specific one like `DisableUvAutoInstall`) in your MCP/Editor settings.
   - Respect that flag in `InstallUv()` where the commented `McpSettings.Current.DisableAutomaticInstallers` check is, and wire it to your UI/preferences.

2. **Optional confirmation prompt**:
   - If you want the confirmation dialog enabled by default, uncomment the `UnityEditor.EditorUtility.DisplayDialog` block and ensure `using UnityEditor;` is present at the top of the file (if not already).
   - If you prefer no GUI prompt by default, consider adding a setting `RequireConfirmationForInstallers` and use that to guard the dialog.

3. **Crossplatform consistency**:
   - Apply the same pattern (download to temp file, log command/URL, optional confirmation and settings check) to the Linux and any other platform `InstallUv` implementations so behavior is consistent across platforms.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +103 to +112
public override bool InstallUv()
{
try
{
McpLog.Info("Attempting to install uv package manager via PowerShell...");

var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "powershell.exe",
Arguments = "-NoProfile -ExecutionPolicy ByPass -Command \"irm https://astral.sh/uv/install.ps1 | iex\"",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Handle long-running installs and potential timeout behavior more robustly.

process.WaitForExit(60000) can leave the process running and cause process.ExitCode to throw if the install takes longer than 60s. It also risks flaky failures on slower machines.

Please check the return value of WaitForExit and handle timeouts explicitly (e.g., log and return failure), and consider either a longer or configurable timeout (or no timeout with appropriate UI feedback) to avoid premature failures.

Comment on lines +415 to +424
var detector = DependencyManager.GetCurrentPlatformDetector();
var uvStatus = detector.DetectUv();
if (!uvStatus.IsAvailable)
{
if (EditorUtility.DisplayDialog(
"uv Package Manager Missing",
"The 'uv' package manager is required to run the local MCP server, but it was not found in your PATH.\n\n" +
"Would you like to attempt an automatic installation now?",
"Install uv",
"Cancel"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Avoid blocking server startup on a potentially long-running interactive installer without clear feedback.

detector.InstallUv() can block for up to 60 seconds as part of starting the local HTTP server, which will freeze the editor with no in-editor indication of progress.

Consider:

  • Showing an editor progress dialog during installation so users understand the delay.
  • Separating the automatic install path from a “just start server” path, so users who know uv is missing aren’t forced into a long blocking operation when starting the server.

Suggested implementation:

            // Check if uv is installed, and offer to install if missing.
            var detector = DependencyManager.GetCurrentPlatformDetector();
            var uvStatus = detector.DetectUv();
            if (!uvStatus.IsAvailable)
            {
                if (EditorUtility.DisplayDialog(
                    "uv Package Manager Missing",
                    "The 'uv' package manager is required to run the local MCP server, but it was not found in your PATH.\n\n" +
                    "An automatic installation can be attempted now. This may take up to 60 seconds while a local HTTP server is started.\n\n" +
                    "Alternatively, you can start the server without uv (if you plan to install it manually or are just inspecting the project).",
                    "Install uv (may take up to 60 seconds)",
                    "Start without uv"))
                {
                    EditorUtility.DisplayProgressBar(
                        "Installing uv",
                        "Installing the 'uv' package manager. This may take up to 60 seconds...",
                        0.5f);

                    try
                    {
                        if (detector.InstallUv())
                        {
                            // Re-check after installation
                        }
                    }
                    finally
                    {
                        EditorUtility.ClearProgressBar();
                    }

The current control flow already effectively separates the “install uv” and “start without uv” paths: declining the dialog skips InstallUv() and continues with the rest of the method. If later code assumes uv is present, you may want to:

  1. Explicitly handle the “start without uv” case (e.g., by skipping server startup or showing a more targeted warning when uv is still missing).
  2. Optionally log or surface a non-blocking notification if the user chooses to start without uv, so they understand any subsequent failures are due to uv being absent.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@MCPForUnity/Editor/Dependencies/PlatformDetectors/LinuxPlatformDetector.cs`:
- Around line 95-121: The InstallUv method blocks the main thread and doesn't
handle WaitForExit timeouts safely; update InstallUv to run asynchronously or be
invoked from a background thread (e.g., Task.Run) so editor UI isn't blocked,
and change its process-wait logic to check the boolean result of
process.WaitForExit(timeout) before reading process.ExitCode—if WaitForExit
returns false, kill/Dispose the child process (Process.Kill/Close) and return
false, and ensure exceptions are logged with full details in the catch; update
callers (MCPSetupWindow and ServerManagementService) to await or otherwise
offload the async/background InstallUv invocation.

In `@MCPForUnity/Editor/Services/ServerManagementService.cs`:
- Around line 414-453: The current call to detector.InstallUv() in the
ServerManagementService flow blocks the Unity main thread (via
process.WaitForExit), so wrap the installation with a user-visible progress
indicator and ensure the progress bar is cleared regardless of outcome: before
calling InstallUv() show EditorUtility.DisplayProgressBar("Installing uv",
"Please wait while uv is being installed...", 0.5f), call detector.InstallUv(),
then always call EditorUtility.ClearProgressBar() in a finally block, and only
after successful return call DetectUv() again; alternatively refactor
InstallUv() on the platform detector to an async/polled API and drive it via
EditorApplication.update instead of calling InstallUv() synchronously so the UI
remains responsive.

In `@MCPForUnity/Editor/Windows/MCPSetupWindow.cs`:
- Around line 119-151: OnAutoInstallUvClicked currently calls
DependencyManager.GetCurrentPlatformDetector().InstallUv() on the main thread
which can block the Unity Editor; change this to run the long-running
InstallUv() off the main thread (e.g. Task.Run or a background thread) while
showing EditorUtility.DisplayProgressBar and allowing cancelation, then marshal
back to the main thread to set _dependencyResult =
DependencyManager.CheckAllDependencies(), call UpdateUI(), clear the progress
bar, and show the appropriate EditorUtility.DisplayDialog results; ensure you
reference the existing symbols OnAutoInstallUvClicked,
DependencyManager.GetCurrentPlatformDetector(), InstallUv(), _dependencyResult,
DependencyManager.CheckAllDependencies(), UpdateUI(), and
EditorUtility.DisplayProgressBar/DisplayDialog when implementing the
async/background flow and main-thread UI updates.
♻️ Duplicate comments (2)
MCPForUnity/Editor/Dependencies/PlatformDetectors/MacOSPlatformDetector.cs (1)

93-119: Same timeout + UI-thread blocking risk as Linux InstallUv().

MCPForUnity/Editor/Dependencies/PlatformDetectors/WindowsPlatformDetector.cs (1)

103-129: Same timeout + UI-thread blocking risk as Linux/macOS InstallUv().

Comment on lines +95 to +121
public override bool InstallUv()
{
try
{
McpLog.Info("Attempting to install uv package manager via curl...");

var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "/bin/sh",
Arguments = "-c \"curl -LsSf https://astral.sh/uv/install.sh | sh\"",
UseShellExecute = false,
CreateNoWindow = false
};

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

process.WaitForExit(60000);

return process.ExitCode == 0;
}
catch (Exception ex)
{
McpLog.Error($"Failed to install uv: {ex.Message}");
return false;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all call sites of InstallUv
rg -n --type=cs 'InstallUv\s*\(' MCPForUnity/

Repository: CoplayDev/unity-mcp

Length of output: 831


🏁 Script executed:

#!/bin/bash
# Get more context around the file to understand the class and its usage
cat -n MCPForUnity/Editor/Dependencies/PlatformDetectors/LinuxPlatformDetector.cs | head -120 | tail -40

Repository: CoplayDev/unity-mcp

Length of output: 1748


🏁 Script executed:

#!/bin/bash
# Search for the class definition and any threading-related patterns
rg -n --type=cs 'class LinuxPlatformDetector' MCPForUnity/ -A 5

Repository: CoplayDev/unity-mcp

Length of output: 749


🏁 Script executed:

#!/bin/bash
# Examine MCPSetupWindow.cs around line 122
cat -n MCPForUnity/Editor/Windows/MCPSetupWindow.cs | sed -n '110,135p'

Repository: CoplayDev/unity-mcp

Length of output: 1284


🏁 Script executed:

#!/bin/bash
# Examine ServerManagementService.cs around line 426
cat -n MCPForUnity/Editor/Services/ServerManagementService.cs | sed -n '410,440p'

Repository: CoplayDev/unity-mcp

Length of output: 1814


🏁 Script executed:

#!/bin/bash
# Check if there's any async/threading context in MCPSetupWindow
rg -n --type=cs 'async|Task|Thread|Dispatch|Main|UI' MCPForUnity/Editor/Windows/MCPSetupWindow.cs | head -20

Repository: CoplayDev/unity-mcp

Length of output: 313


🏁 Script executed:

#!/bin/bash
# Check the method containing the call at ServerManagementService.cs:426
cat -n MCPForUnity/Editor/Services/ServerManagementService.cs | sed -n '390,410p'

Repository: CoplayDev/unity-mcp

Length of output: 1181


🏁 Script executed:

#!/bin/bash
# Search for the method signature to understand threading
rg -n --type=cs 'public|private|async' MCPForUnity/Editor/Services/ServerManagementService.cs | grep -A 2 -B 2 '426'

Repository: CoplayDev/unity-mcp

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Search for method definition containing line 426
cat -n MCPForUnity/Editor/Services/ServerManagementService.cs | sed -n '380,395p'

Repository: CoplayDev/unity-mcp

Length of output: 855


🏁 Script executed:

#!/bin/bash
# Check if there's any async/await pattern in ServerManagementService
rg -n --type=cs 'async|Task|Coroutine' MCPForUnity/Editor/Services/ServerManagementService.cs | head -10

Repository: CoplayDev/unity-mcp

Length of output: 232


Avoid blocking the editor and handle installer timeouts.

Both call sites (MCPSetupWindow.cs:122 and ServerManagementService.cs:426) execute InstallUv() on the main editor thread. The current implementation blocks for up to 60 seconds with WaitForExit(60000), freezing the UI.

Additionally, the code ignores the timeout result; if WaitForExit times out and returns false, accessing process.ExitCode throws InvalidOperationException, leaving the installer process running in the background. While caught by the outer try-catch, this silently returns false without cleaning up the orphaned process.

🔧 Safer timeout handling (still synchronous)
-                process.WaitForExit(60000);
-
-                return process.ExitCode == 0;
+                if (!process.WaitForExit(60000))
+                {
+                    try { process.Kill(); } catch { /* best-effort */ }
+                    return false;
+                }
+
+                return process.ExitCode == 0;

Consider moving this operation to a background thread or async pattern to prevent UI hangs during installation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public override bool InstallUv()
{
try
{
McpLog.Info("Attempting to install uv package manager via curl...");
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "/bin/sh",
Arguments = "-c \"curl -LsSf https://astral.sh/uv/install.sh | sh\"",
UseShellExecute = false,
CreateNoWindow = false
};
using var process = System.Diagnostics.Process.Start(psi);
if (process == null) return false;
process.WaitForExit(60000);
return process.ExitCode == 0;
}
catch (Exception ex)
{
McpLog.Error($"Failed to install uv: {ex.Message}");
return false;
}
}
public override bool InstallUv()
{
try
{
McpLog.Info("Attempting to install uv package manager via curl...");
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "/bin/sh",
Arguments = "-c \"curl -LsSf https://astral.sh/uv/install.sh | sh\"",
UseShellExecute = false,
CreateNoWindow = false
};
using var process = System.Diagnostics.Process.Start(psi);
if (process == null) return false;
if (!process.WaitForExit(60000))
{
try { process.Kill(); } catch { /* best-effort */ }
return false;
}
return process.ExitCode == 0;
}
catch (Exception ex)
{
McpLog.Error($"Failed to install uv: {ex.Message}");
return false;
}
}
🤖 Prompt for AI Agents
In `@MCPForUnity/Editor/Dependencies/PlatformDetectors/LinuxPlatformDetector.cs`
around lines 95 - 121, The InstallUv method blocks the main thread and doesn't
handle WaitForExit timeouts safely; update InstallUv to run asynchronously or be
invoked from a background thread (e.g., Task.Run) so editor UI isn't blocked,
and change its process-wait logic to check the boolean result of
process.WaitForExit(timeout) before reading process.ExitCode—if WaitForExit
returns false, kill/Dispose the child process (Process.Kill/Close) and return
false, and ensure exceptions are logged with full details in the catch; update
callers (MCPSetupWindow and ServerManagementService) to await or otherwise
offload the async/background InstallUv invocation.

Comment on lines +414 to +453
// Check if uv is installed, and offer to install if missing.
var detector = DependencyManager.GetCurrentPlatformDetector();
var uvStatus = detector.DetectUv();
if (!uvStatus.IsAvailable)
{
if (EditorUtility.DisplayDialog(
"uv Package Manager Missing",
"The 'uv' package manager is required to run the local MCP server, but it was not found in your PATH.\n\n" +
"Would you like to attempt an automatic installation now?",
"Install uv",
"Cancel"))
{
if (detector.InstallUv())
{
// Re-check after installation
uvStatus = detector.DetectUv();
if (!uvStatus.IsAvailable)
{
EditorUtility.DisplayDialog(
"Installation Incomplete",
"uv was installed, but it is not yet detected in the current process PATH. " +
"You may need to restart Unity for the changes to take effect.",
"OK");
return false;
}
}
else
{
EditorUtility.DisplayDialog(
"Installation Failed",
"Failed to automatically install uv. Please install it manually:\n\n" + detector.GetUvInstallUrl(),
"OK");
return false;
}
}
else
{
return false;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Blocking main thread during uv installation may freeze Unity Editor.

The InstallUv() method (in platform detectors) calls process.WaitForExit(60000), which blocks for up to 60 seconds. This runs on the Unity main thread and will freeze the Editor UI during installation.

Consider running the installation asynchronously or showing a progress dialog to inform the user that the operation is in progress.

💡 Suggested approach

Option 1: Add a simple progress indicator before blocking:

EditorUtility.DisplayProgressBar("Installing uv", "Please wait while uv is being installed...", 0.5f);
try
{
    if (detector.InstallUv())
    {
        // ...
    }
}
finally
{
    EditorUtility.ClearProgressBar();
}

Option 2 (better UX): Refactor InstallUv() to be async and use EditorApplication.update callbacks to poll completion.

🤖 Prompt for AI Agents
In `@MCPForUnity/Editor/Services/ServerManagementService.cs` around lines 414 -
453, The current call to detector.InstallUv() in the ServerManagementService
flow blocks the Unity main thread (via process.WaitForExit), so wrap the
installation with a user-visible progress indicator and ensure the progress bar
is cleared regardless of outcome: before calling InstallUv() show
EditorUtility.DisplayProgressBar("Installing uv", "Please wait while uv is being
installed...", 0.5f), call detector.InstallUv(), then always call
EditorUtility.ClearProgressBar() in a finally block, and only after successful
return call DetectUv() again; alternatively refactor InstallUv() on the platform
detector to an async/polled API and drive it via EditorApplication.update
instead of calling InstallUv() synchronously so the UI remains responsive.

Comment on lines +119 to +151
private void OnAutoInstallUvClicked()
{
var detector = DependencyManager.GetCurrentPlatformDetector();
if (detector.InstallUv())
{
_dependencyResult = DependencyManager.CheckAllDependencies();
UpdateUI();

var uvDep = _dependencyResult.Dependencies.Find(d => d.Name == "uv Package Manager");
if (uvDep != null && !uvDep.IsAvailable)
{
EditorUtility.DisplayDialog(
"Installation Incomplete",
"uv was installed, but it is not yet detected in the current process PATH. " +
"You may need to restart Unity for the changes to take effect.",
"OK");
}
else
{
EditorUtility.DisplayDialog(
"Installation Successful",
"uv package manager has been installed successfully.",
"OK");
}
}
else
{
EditorUtility.DisplayDialog(
"Installation Failed",
"Failed to automatically install uv. Please install it manually.",
"OK");
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same blocking concern applies here.

This handler has the same main-thread blocking issue mentioned in ServerManagementService.cs. The InstallUv() call can block for up to 60 seconds, freezing the Editor UI.

Consider adding a progress bar here as well to provide feedback during the installation:

💡 Suggested fix
 private void OnAutoInstallUvClicked()
 {
     var detector = DependencyManager.GetCurrentPlatformDetector();
+    EditorUtility.DisplayProgressBar("Installing uv", "Please wait while uv is being installed...", 0.5f);
+    bool installResult;
+    try
+    {
+        installResult = detector.InstallUv();
+    }
+    finally
+    {
+        EditorUtility.ClearProgressBar();
+    }
+
-    if (detector.InstallUv())
+    if (installResult)
     {
         _dependencyResult = DependencyManager.CheckAllDependencies();
         UpdateUI();
         // ... rest of the logic
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private void OnAutoInstallUvClicked()
{
var detector = DependencyManager.GetCurrentPlatformDetector();
if (detector.InstallUv())
{
_dependencyResult = DependencyManager.CheckAllDependencies();
UpdateUI();
var uvDep = _dependencyResult.Dependencies.Find(d => d.Name == "uv Package Manager");
if (uvDep != null && !uvDep.IsAvailable)
{
EditorUtility.DisplayDialog(
"Installation Incomplete",
"uv was installed, but it is not yet detected in the current process PATH. " +
"You may need to restart Unity for the changes to take effect.",
"OK");
}
else
{
EditorUtility.DisplayDialog(
"Installation Successful",
"uv package manager has been installed successfully.",
"OK");
}
}
else
{
EditorUtility.DisplayDialog(
"Installation Failed",
"Failed to automatically install uv. Please install it manually.",
"OK");
}
}
private void OnAutoInstallUvClicked()
{
var detector = DependencyManager.GetCurrentPlatformDetector();
EditorUtility.DisplayProgressBar("Installing uv", "Please wait while uv is being installed...", 0.5f);
bool installResult;
try
{
installResult = detector.InstallUv();
}
finally
{
EditorUtility.ClearProgressBar();
}
if (installResult)
{
_dependencyResult = DependencyManager.CheckAllDependencies();
UpdateUI();
var uvDep = _dependencyResult.Dependencies.Find(d => d.Name == "uv Package Manager");
if (uvDep != null && !uvDep.IsAvailable)
{
EditorUtility.DisplayDialog(
"Installation Incomplete",
"uv was installed, but it is not yet detected in the current process PATH. " +
"You may need to restart Unity for the changes to take effect.",
"OK");
}
else
{
EditorUtility.DisplayDialog(
"Installation Successful",
"uv package manager has been installed successfully.",
"OK");
}
}
else
{
EditorUtility.DisplayDialog(
"Installation Failed",
"Failed to automatically install uv. Please install it manually.",
"OK");
}
}
🤖 Prompt for AI Agents
In `@MCPForUnity/Editor/Windows/MCPSetupWindow.cs` around lines 119 - 151,
OnAutoInstallUvClicked currently calls
DependencyManager.GetCurrentPlatformDetector().InstallUv() on the main thread
which can block the Unity Editor; change this to run the long-running
InstallUv() off the main thread (e.g. Task.Run or a background thread) while
showing EditorUtility.DisplayProgressBar and allowing cancelation, then marshal
back to the main thread to set _dependencyResult =
DependencyManager.CheckAllDependencies(), call UpdateUI(), clear the progress
bar, and show the appropriate EditorUtility.DisplayDialog results; ensure you
reference the existing symbols OnAutoInstallUvClicked,
DependencyManager.GetCurrentPlatformDetector(), InstallUv(), _dependencyResult,
DependencyManager.CheckAllDependencies(), UpdateUI(), and
EditorUtility.DisplayProgressBar/DisplayDialog when implementing the
async/background flow and main-thread UI updates.

@hvesuk
Copy link
Author

hvesuk commented Jan 19, 2026

Hi @dsarno! I’ve opened this PR — let me know if anything needs adjustment. Thanks!

@dsarno
Copy link
Collaborator

dsarno commented Jan 19, 2026

Hi @dsarno! I’ve opened this PR — let me know if anything needs adjustment. Thanks!

Awesome, thanks for these PRs @hvesuk. I definitely like user friendliness of this flow. Let me play with it when I'm back tomorrow. Also @msanatan might want to weigh in because he did all the setup window flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants