[FEATURE] Deployment of local source code to Unity (#450)

* [FEATURE] Local MCPForUnity Deployment

Similar to deploy.bat, but sideload it to MCP For Unity for easier deployment inside Unity menu.

* Update PackageDeploymentService.cs

* Update with meta file

* Updated Readme
main
Shutong Wu 2025-12-09 20:17:18 -05:00 committed by GitHub
parent 97b85749b5
commit 79b3255d0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 544 additions and 5 deletions

View File

@ -21,6 +21,11 @@ namespace MCPForUnity.Editor.Constants
internal const string WebSocketUrlOverride = "MCPForUnity.WebSocketUrl"; internal const string WebSocketUrlOverride = "MCPForUnity.WebSocketUrl";
internal const string GitUrlOverride = "MCPForUnity.GitUrlOverride"; internal const string GitUrlOverride = "MCPForUnity.GitUrlOverride";
internal const string PackageDeploySourcePath = "MCPForUnity.PackageDeploy.SourcePath";
internal const string PackageDeployLastBackupPath = "MCPForUnity.PackageDeploy.LastBackupPath";
internal const string PackageDeployLastTargetPath = "MCPForUnity.PackageDeploy.LastTargetPath";
internal const string PackageDeployLastSourcePath = "MCPForUnity.PackageDeploy.LastSourcePath";
internal const string ServerSrc = "MCPForUnity.ServerSrc"; internal const string ServerSrc = "MCPForUnity.ServerSrc";
internal const string UseEmbeddedServer = "MCPForUnity.UseEmbeddedServer"; internal const string UseEmbeddedServer = "MCPForUnity.UseEmbeddedServer";
internal const string LockCursorConfig = "MCPForUnity.LockCursorConfig"; internal const string LockCursorConfig = "MCPForUnity.LockCursorConfig";

View File

@ -0,0 +1,29 @@
using System;
namespace MCPForUnity.Editor.Services
{
public interface IPackageDeploymentService
{
string GetStoredSourcePath();
void SetStoredSourcePath(string path);
void ClearStoredSourcePath();
string GetTargetPath();
string GetTargetDisplayPath();
string GetLastBackupPath();
bool HasBackup();
PackageDeploymentResult DeployFromStoredSource();
PackageDeploymentResult RestoreLastBackup();
}
public class PackageDeploymentResult
{
public bool Success { get; set; }
public string Message { get; set; }
public string SourcePath { get; set; }
public string TargetPath { get; set; }
public string BackupPath { get; set; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c7a6f1ce6cd4a8c8a3b5d58d4b760a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -19,6 +19,7 @@ namespace MCPForUnity.Editor.Services
private static IToolDiscoveryService _toolDiscoveryService; private static IToolDiscoveryService _toolDiscoveryService;
private static IServerManagementService _serverManagementService; private static IServerManagementService _serverManagementService;
private static TransportManager _transportManager; private static TransportManager _transportManager;
private static IPackageDeploymentService _packageDeploymentService;
public static IBridgeControlService Bridge => _bridgeService ??= new BridgeControlService(); public static IBridgeControlService Bridge => _bridgeService ??= new BridgeControlService();
public static IClientConfigurationService Client => _clientService ??= new ClientConfigurationService(); public static IClientConfigurationService Client => _clientService ??= new ClientConfigurationService();
@ -29,6 +30,7 @@ namespace MCPForUnity.Editor.Services
public static IToolDiscoveryService ToolDiscovery => _toolDiscoveryService ??= new ToolDiscoveryService(); public static IToolDiscoveryService ToolDiscovery => _toolDiscoveryService ??= new ToolDiscoveryService();
public static IServerManagementService Server => _serverManagementService ??= new ServerManagementService(); public static IServerManagementService Server => _serverManagementService ??= new ServerManagementService();
public static TransportManager TransportManager => _transportManager ??= new TransportManager(); public static TransportManager TransportManager => _transportManager ??= new TransportManager();
public static IPackageDeploymentService Deployment => _packageDeploymentService ??= new PackageDeploymentService();
/// <summary> /// <summary>
/// Registers a custom implementation for a service (useful for testing) /// Registers a custom implementation for a service (useful for testing)
@ -53,6 +55,8 @@ namespace MCPForUnity.Editor.Services
_toolDiscoveryService = td; _toolDiscoveryService = td;
else if (implementation is IServerManagementService sm) else if (implementation is IServerManagementService sm)
_serverManagementService = sm; _serverManagementService = sm;
else if (implementation is IPackageDeploymentService pd)
_packageDeploymentService = pd;
else if (implementation is TransportManager tm) else if (implementation is TransportManager tm)
_transportManager = tm; _transportManager = tm;
} }
@ -71,6 +75,7 @@ namespace MCPForUnity.Editor.Services
(_toolDiscoveryService as IDisposable)?.Dispose(); (_toolDiscoveryService as IDisposable)?.Dispose();
(_serverManagementService as IDisposable)?.Dispose(); (_serverManagementService as IDisposable)?.Dispose();
(_transportManager as IDisposable)?.Dispose(); (_transportManager as IDisposable)?.Dispose();
(_packageDeploymentService as IDisposable)?.Dispose();
_bridgeService = null; _bridgeService = null;
_clientService = null; _clientService = null;
@ -81,6 +86,7 @@ namespace MCPForUnity.Editor.Services
_toolDiscoveryService = null; _toolDiscoveryService = null;
_serverManagementService = null; _serverManagementService = null;
_transportManager = null; _transportManager = null;
_packageDeploymentService = null;
} }
} }
} }

View File

@ -0,0 +1,303 @@
using System;
using System.IO;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Helpers;
using UnityEditor;
using UnityEngine;
using PackageInfo = UnityEditor.PackageManager.PackageInfo;
namespace MCPForUnity.Editor.Services
{
/// <summary>
/// Handles copying a local MCPForUnity folder into the current project's package location with backup/restore support.
/// </summary>
public class PackageDeploymentService : IPackageDeploymentService
{
private const string BackupRootFolderName = "MCPForUnityDeployBackups";
public string GetStoredSourcePath()
{
return EditorPrefs.GetString(EditorPrefKeys.PackageDeploySourcePath, string.Empty);
}
public void SetStoredSourcePath(string path)
{
ValidateSource(path);
EditorPrefs.SetString(EditorPrefKeys.PackageDeploySourcePath, Path.GetFullPath(path));
}
public void ClearStoredSourcePath()
{
EditorPrefs.DeleteKey(EditorPrefKeys.PackageDeploySourcePath);
}
public string GetTargetPath()
{
// Prefer Package Manager resolved path for the installed package
var packageInfo = PackageInfo.FindForAssembly(typeof(PackageDeploymentService).Assembly);
if (packageInfo != null)
{
if (!string.IsNullOrEmpty(packageInfo.resolvedPath) && Directory.Exists(packageInfo.resolvedPath))
{
return packageInfo.resolvedPath;
}
if (!string.IsNullOrEmpty(packageInfo.assetPath))
{
string absoluteFromAsset = MakeAbsolute(packageInfo.assetPath);
if (Directory.Exists(absoluteFromAsset))
{
return absoluteFromAsset;
}
}
}
// Fallback to computed package root
string packageRoot = AssetPathUtility.GetMcpPackageRootPath();
if (!string.IsNullOrEmpty(packageRoot))
{
string absolutePath = MakeAbsolute(packageRoot);
if (Directory.Exists(absolutePath))
{
return absolutePath;
}
}
return null;
}
public string GetTargetDisplayPath()
{
string target = GetTargetPath();
return string.IsNullOrEmpty(target)
? "Not found (check Packages/manifest.json)"
: target;
}
public string GetLastBackupPath()
{
return EditorPrefs.GetString(EditorPrefKeys.PackageDeployLastBackupPath, string.Empty);
}
public bool HasBackup()
{
string path = GetLastBackupPath();
return !string.IsNullOrEmpty(path) && Directory.Exists(path);
}
public PackageDeploymentResult DeployFromStoredSource()
{
string sourcePath = GetStoredSourcePath();
if (string.IsNullOrEmpty(sourcePath))
{
return Fail("Select a MCPForUnity folder first.");
}
string validationError = ValidateSource(sourcePath, throwOnError: false);
if (!string.IsNullOrEmpty(validationError))
{
return Fail(validationError);
}
string targetPath = GetTargetPath();
if (string.IsNullOrEmpty(targetPath))
{
return Fail("Could not locate the installed MCP package. Check Packages/manifest.json.");
}
if (PathsEqual(sourcePath, targetPath))
{
return Fail("Source and target are the same. Choose a different MCPForUnity folder.");
}
try
{
EditorUtility.DisplayProgressBar("Deploy MCP for Unity", "Creating backup...", 0.25f);
string backupPath = CreateBackup(targetPath);
EditorUtility.DisplayProgressBar("Deploy MCP for Unity", "Replacing package contents...", 0.7f);
CopyCoreFolders(sourcePath, targetPath);
EditorPrefs.SetString(EditorPrefKeys.PackageDeployLastBackupPath, backupPath);
EditorPrefs.SetString(EditorPrefKeys.PackageDeployLastTargetPath, targetPath);
EditorPrefs.SetString(EditorPrefKeys.PackageDeployLastSourcePath, sourcePath);
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
return Success("Deployment completed.", sourcePath, targetPath, backupPath);
}
catch (Exception ex)
{
McpLog.Error($"Deployment failed: {ex.Message}");
return Fail($"Deployment failed: {ex.Message}");
}
finally
{
EditorUtility.ClearProgressBar();
}
}
public PackageDeploymentResult RestoreLastBackup()
{
string backupPath = GetLastBackupPath();
string targetPath = EditorPrefs.GetString(EditorPrefKeys.PackageDeployLastTargetPath, string.Empty);
if (string.IsNullOrEmpty(backupPath) || !Directory.Exists(backupPath))
{
return Fail("No backup available to restore.");
}
if (string.IsNullOrEmpty(targetPath) || !Directory.Exists(targetPath))
{
targetPath = GetTargetPath();
}
if (string.IsNullOrEmpty(targetPath) || !Directory.Exists(targetPath))
{
return Fail("Could not locate target package path.");
}
try
{
EditorUtility.DisplayProgressBar("Restore MCP for Unity", "Restoring backup...", 0.5f);
ReplaceDirectory(backupPath, targetPath);
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
return Success("Restore completed.", null, targetPath, backupPath);
}
catch (Exception ex)
{
McpLog.Error($"Restore failed: {ex.Message}");
return Fail($"Restore failed: {ex.Message}");
}
finally
{
EditorUtility.ClearProgressBar();
}
}
private void CopyCoreFolders(string sourceRoot, string targetRoot)
{
string sourceEditor = Path.Combine(sourceRoot, "Editor");
string sourceRuntime = Path.Combine(sourceRoot, "Runtime");
ReplaceDirectory(sourceEditor, Path.Combine(targetRoot, "Editor"));
ReplaceDirectory(sourceRuntime, Path.Combine(targetRoot, "Runtime"));
}
private static void ReplaceDirectory(string source, string destination)
{
if (Directory.Exists(destination))
{
FileUtil.DeleteFileOrDirectory(destination);
}
FileUtil.CopyFileOrDirectory(source, destination);
}
private string CreateBackup(string targetPath)
{
string backupRoot = Path.Combine(GetProjectRoot(), "Library", BackupRootFolderName);
Directory.CreateDirectory(backupRoot);
string stamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string backupPath = Path.Combine(backupRoot, $"backup_{stamp}");
if (Directory.Exists(backupPath))
{
FileUtil.DeleteFileOrDirectory(backupPath);
}
FileUtil.CopyFileOrDirectory(targetPath, backupPath);
return backupPath;
}
private static string ValidateSource(string sourcePath, bool throwOnError = true)
{
if (string.IsNullOrEmpty(sourcePath))
{
if (throwOnError)
{
throw new ArgumentException("Source path cannot be empty.");
}
return "Source path is empty.";
}
if (!Directory.Exists(sourcePath))
{
if (throwOnError)
{
throw new ArgumentException("Selected folder does not exist.");
}
return "Selected folder does not exist.";
}
bool hasEditor = Directory.Exists(Path.Combine(sourcePath, "Editor"));
bool hasRuntime = Directory.Exists(Path.Combine(sourcePath, "Runtime"));
if (!hasEditor || !hasRuntime)
{
string message = "Folder must contain Editor and Runtime subfolders.";
if (throwOnError)
{
throw new ArgumentException(message);
}
return message;
}
return null;
}
private static string MakeAbsolute(string assetPath)
{
assetPath = assetPath.Replace('\\', '/');
if (assetPath.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
{
return Path.GetFullPath(Path.Combine(Application.dataPath, "..", assetPath));
}
if (assetPath.StartsWith("Packages/", StringComparison.OrdinalIgnoreCase))
{
return Path.GetFullPath(Path.Combine(Application.dataPath, "..", assetPath));
}
return Path.GetFullPath(assetPath);
}
private static string GetProjectRoot()
{
return Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
}
private static bool PathsEqual(string a, string b)
{
string normA = Path.GetFullPath(a).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
string normB = Path.GetFullPath(b).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
return string.Equals(normA, normB, StringComparison.OrdinalIgnoreCase);
}
private static PackageDeploymentResult Success(string message, string source, string target, string backup)
{
return new PackageDeploymentResult
{
Success = true,
Message = message,
SourcePath = source,
TargetPath = target,
BackupPath = backup
};
}
private static PackageDeploymentResult Fail(string message)
{
return new PackageDeploymentResult
{
Success = false,
Message = message
};
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b1f45e4e5d24413a6f1c8c0d8c5f2f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -29,6 +29,14 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
private VisualElement uvxPathStatus; private VisualElement uvxPathStatus;
private TextField gitUrlOverride; private TextField gitUrlOverride;
private Button clearGitUrlButton; private Button clearGitUrlButton;
private TextField deploySourcePath;
private Button browseDeploySourceButton;
private Button clearDeploySourceButton;
private Button deployButton;
private Button deployRestoreButton;
private Label deployTargetLabel;
private Label deployBackupLabel;
private Label deployStatusLabel;
// Data // Data
private ValidationLevel currentValidationLevel = ValidationLevel.Standard; private ValidationLevel currentValidationLevel = ValidationLevel.Standard;
@ -69,6 +77,14 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
uvxPathStatus = Root.Q<VisualElement>("uv-path-status"); uvxPathStatus = Root.Q<VisualElement>("uv-path-status");
gitUrlOverride = Root.Q<TextField>("git-url-override"); gitUrlOverride = Root.Q<TextField>("git-url-override");
clearGitUrlButton = Root.Q<Button>("clear-git-url-button"); clearGitUrlButton = Root.Q<Button>("clear-git-url-button");
deploySourcePath = Root.Q<TextField>("deploy-source-path");
browseDeploySourceButton = Root.Q<Button>("browse-deploy-source-button");
clearDeploySourceButton = Root.Q<Button>("clear-deploy-source-button");
deployButton = Root.Q<Button>("deploy-button");
deployRestoreButton = Root.Q<Button>("deploy-restore-button");
deployTargetLabel = Root.Q<Label>("deploy-target-label");
deployBackupLabel = Root.Q<Label>("deploy-backup-label");
deployStatusLabel = Root.Q<Label>("deploy-status-label");
} }
private void InitializeUI() private void InitializeUI()
@ -87,6 +103,8 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
advancedSettingsFoldout.value = false; advancedSettingsFoldout.value = false;
gitUrlOverride.value = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, ""); gitUrlOverride.value = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
UpdateDeploymentSection();
} }
private void RegisterCallbacks() private void RegisterCallbacks()
@ -128,6 +146,11 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
OnGitUrlChanged?.Invoke(); OnGitUrlChanged?.Invoke();
OnHttpServerCommandUpdateRequested?.Invoke(); OnHttpServerCommandUpdateRequested?.Invoke();
}; };
browseDeploySourceButton.clicked += OnBrowseDeploySourceClicked;
clearDeploySourceButton.clicked += OnClearDeploySourceClicked;
deployButton.clicked += OnDeployClicked;
deployRestoreButton.clicked += OnRestoreBackupClicked;
} }
public void UpdatePathOverrides() public void UpdatePathOverrides()
@ -159,6 +182,7 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
} }
gitUrlOverride.value = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, ""); gitUrlOverride.value = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
UpdateDeploymentSection();
} }
private void UpdateVersionLabel() private void UpdateVersionLabel()
@ -225,5 +249,103 @@ namespace MCPForUnity.Editor.Windows.Components.Settings
UpdatePathOverrides(); UpdatePathOverrides();
McpLog.Info("uv path override cleared"); McpLog.Info("uv path override cleared");
} }
private void UpdateDeploymentSection()
{
var deployService = MCPServiceLocator.Deployment;
string sourcePath = deployService.GetStoredSourcePath();
deploySourcePath.value = string.IsNullOrEmpty(sourcePath) ? "Not set" : sourcePath;
deployTargetLabel.text = $"Target: {deployService.GetTargetDisplayPath()}";
string backupPath = deployService.GetLastBackupPath();
if (deployService.HasBackup())
{
deployBackupLabel.text = $"Last backup: {backupPath}";
}
else
{
deployBackupLabel.text = "Last backup: none";
}
deployRestoreButton?.SetEnabled(deployService.HasBackup());
}
private void OnBrowseDeploySourceClicked()
{
string picked = EditorUtility.OpenFolderPanel("Select MCPForUnity folder", string.Empty, string.Empty);
if (string.IsNullOrEmpty(picked))
{
return;
}
try
{
MCPServiceLocator.Deployment.SetStoredSourcePath(picked);
SetDeployStatus($"Source set: {picked}");
}
catch (Exception ex)
{
EditorUtility.DisplayDialog("Invalid Source", ex.Message, "OK");
SetDeployStatus("Source selection failed");
}
UpdateDeploymentSection();
}
private void OnClearDeploySourceClicked()
{
MCPServiceLocator.Deployment.ClearStoredSourcePath();
UpdateDeploymentSection();
SetDeployStatus("Source cleared");
}
private void OnDeployClicked()
{
var result = MCPServiceLocator.Deployment.DeployFromStoredSource();
SetDeployStatus(result.Message, !result.Success);
if (!result.Success)
{
EditorUtility.DisplayDialog("Deployment Failed", result.Message, "OK");
}
else
{
EditorUtility.DisplayDialog("Deployment Complete", result.Message + (string.IsNullOrEmpty(result.BackupPath) ? string.Empty : $"\nBackup: {result.BackupPath}"), "OK");
}
UpdateDeploymentSection();
}
private void OnRestoreBackupClicked()
{
var result = MCPServiceLocator.Deployment.RestoreLastBackup();
SetDeployStatus(result.Message, !result.Success);
if (!result.Success)
{
EditorUtility.DisplayDialog("Restore Failed", result.Message, "OK");
}
else
{
EditorUtility.DisplayDialog("Restore Complete", result.Message, "OK");
}
UpdateDeploymentSection();
}
private void SetDeployStatus(string message, bool isError = false)
{
if (deployStatusLabel == null)
{
return;
}
deployStatusLabel.text = message;
deployStatusLabel.style.color = isError
? new StyleColor(new Color(0.85f, 0.2f, 0.2f))
: StyleKeyword.Null;
}
} }
} }

View File

@ -31,7 +31,7 @@
<ui:Label text="Server Source Override:" class="advanced-label" style="margin-top: 10px;" /> <ui:Label text="Server Source Override:" class="advanced-label" style="margin-top: 10px;" />
<ui:Label text="Override the source used for uvx --from. Leave empty to use default package." class="help-text" /> <ui:Label text="Override the source used for uvx --from. Leave empty to use default package." class="help-text" />
<ui:VisualElement class="override-row"> <ui:VisualElement class="override-row">
<ui:Label text="Source Path/URL:" class="override-label" /> <ui:Label text="Server Path/URL:" class="override-label" />
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement class="path-override-controls"> <ui:VisualElement class="path-override-controls">
<ui:TextField name="git-url-override" placeholder-text="/path/to/Server or git+https://..." class="override-field" /> <ui:TextField name="git-url-override" placeholder-text="/path/to/Server or git+https://..." class="override-field" />
@ -40,6 +40,24 @@
<ui:Label text="Examples:" class="help-text" style="margin-top: 5px;" /> <ui:Label text="Examples:" class="help-text" style="margin-top: 5px;" />
<ui:Label text="• Local: /Users/you/Projects/unity-mcp/Server" class="help-text" /> <ui:Label text="• Local: /Users/you/Projects/unity-mcp/Server" class="help-text" />
<ui:Label text="• Remote: git+https://github.com/CoplayDev/unity-mcp@v6.3.0#subdirectory=Server" class="help-text" /> <ui:Label text="• Remote: git+https://github.com/CoplayDev/unity-mcp@v6.3.0#subdirectory=Server" class="help-text" />
<ui:Label text="Local Package Deployment:" class="advanced-label" style="margin-top: 12px;" />
<ui:Label text="Copy a MCPForUnity folder into this project's package location." class="help-text" />
<ui:VisualElement class="override-row">
<ui:Label text="MCP For Unity Source Folder:" class="override-label" />
</ui:VisualElement>
<ui:VisualElement class="path-override-controls">
<ui:TextField name="deploy-source-path" readonly="true" class="override-field" />
<ui:Button name="browse-deploy-source-button" text="Select" class="icon-button" />
<ui:Button name="clear-deploy-source-button" text="Clear" class="icon-button" />
</ui:VisualElement>
<ui:Label name="deploy-target-label" class="help-text" />
<ui:Label name="deploy-backup-label" class="help-text" />
<ui:VisualElement class="path-override-controls" style="margin-top: 4px;">
<ui:Button name="deploy-button" text="Deploy to Project" class="icon-button" />
<ui:Button name="deploy-restore-button" text="Restore Last Backup" class="icon-button" />
</ui:VisualElement>
<ui:Label name="deploy-status-label" class="help-text" />
</ui:VisualElement> </ui:VisualElement>
</ui:Foldout> </ui:Foldout>
</ui:VisualElement> </ui:VisualElement>

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c8b3a2f4b0e42dba7f6d7c6e8a4c1f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b33f3e9c912b4f17a5a4374e4f6c2a91
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -54,17 +54,32 @@ uv run pytest tests/ -v -m unit
### ✅ Development Deployment Scripts ### ✅ Development Deployment Scripts
Quick deployment and testing tools for MCP for Unity core changes. Quick deployment and testing tools for MCP for Unity core changes.
**Development Mode Toggle**: Built-in Unity editor development features -> Now operating as Advanced Setting
**Hot Reload System**: Real-time code updates without Unity restarts -> Roslyn Runtime_Compilation Custom Tools
**Plugin Development Kit**: Tools for creating custom MCP for Unity extensions -> Custom Tools
### 🔄 Coming Soon ### 🔄 Coming Soon
- **Development Mode Toggle**: Built-in Unity editor development features
- **Hot Reload System**: Real-time code updates without Unity restarts
- **Plugin Development Kit**: Tools for creating custom MCP for Unity extensions
- **Automated Testing Suite**: Comprehensive testing framework for contributions - **Automated Testing Suite**: Comprehensive testing framework for contributions
- **Debug Dashboard**: Advanced debugging and monitoring tools - **Debug Dashboard**: Advanced debugging and monitoring tools
--- ---
## Advanced Settings (Editor Window)
Use the MCP for Unity Editor window (Window > MCP for Unity) and open **Advanced Settings** inside the Settings tab to override tooling and deploy local code during development.
![Advanced Settings](./images/advanced-setting.png)
- **UV/UVX Path Override**: Point the UI to a specific `uv`/`uvx` executable (e.g., from a custom install) when PATH resolution is wrong. Clear to fall back to auto-discovery.
- **Server Source Override**: Set a local folder or git URL for the Python server (`uvx --from <url> mcp-for-unity`). Clear to use the packaged default.
- **Local Package Deployment**: Pick a local `MCPForUnity` folder (must contain `Editor/` and `Runtime/`) and click **Deploy to Project** to copy it over the currently installed package path (from `Packages/manifest.json` / Package Manager). A timestamped backup is stored under `Library/MCPForUnityDeployBackups`, and **Restore Last Backup** reverts the last deploy.
Tips:
- After deploy/restore, Unity will refresh scripts automatically; if in doubt, re-open the MCP window and confirm the target path label in Advanced Settings.
- Keep the source and target distinct (dont point the source at the already-installed `PackageCache` folder).
- Use git ignored working folders for rapid iteration; the deploy flow copies only `Editor` and `Runtime`.
## Switching MCP package sources quickly ## Switching MCP package sources quickly
Run this from the unity-mcp repo, not your game's root directory. Use `mcp_source.py` to quickly switch between different MCP for Unity package sources: Run this from the unity-mcp repo, not your game's root directory. Use `mcp_source.py` to quickly switch between different MCP for Unity package sources:

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB