[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 Readmemain
parent
97b85749b5
commit
79b3255d0a
|
|
@ -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";
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c7a6f1ce6cd4a8c8a3b5d58d4b760a2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0b1f45e4e5d24413a6f1c8c0d8c5f2f1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5c8b3a2f4b0e42dba7f6d7c6e8a4c1f2
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b33f3e9c912b4f17a5a4374e4f6c2a91
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
- **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 (don’t 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 |
Loading…
Reference in New Issue